ATLAS Offline Software
Loading...
Searching...
No Matches
MuonML::SegmentEdgeInferenceAlg Class Referencefinal

#include <SegmentEdgeInferenceAlg.h>

Inheritance diagram for MuonML::SegmentEdgeInferenceAlg:
Collaboration diagram for MuonML::SegmentEdgeInferenceAlg:

Classes

struct  TruthLossCounters
 Job-summed classification of lost truth segments (classifyLostTruthSegments()). More...

Public Member Functions

StatusCode initialize () override
StatusCode execute (const EventContext &ctx) const override
StatusCode finalize () override
 Log of the post-ONNX rank/mutuality and min-component-size selection.
virtual StatusCode sysInitialize () override
 Override sysInitialize.
virtual bool isClonable () const override
 Specify if the algorithm is clonable.
virtual StatusCode sysExecute (const EventContext &ctx) override
 Execute an algorithm.
virtual const DataObjIDColl & extraOutputDeps () const override
 Return the list of extra output dependencies.
virtual bool filterPassed (const EventContext &ctx) const
 Get filter decision:
virtual void setFilterPassed (bool state, const EventContext &ctx) const
 Set filter decision:
ServiceHandle< StoreGateSvc > & evtStore ()
 The standard StoreGateSvc (event store) Returns (kind of) a pointer to the StoreGateSvc.
const ServiceHandle< StoreGateSvc > & detStore () const
 The standard StoreGateSvc/DetectorStore Returns (kind of) a pointer to the StoreGateSvc.
virtual StatusCode sysStart () override
 Handle START transition.
virtual std::vector< Gaudi::DataHandle * > inputHandles () const override
 Return this algorithm's input handles.
virtual std::vector< Gaudi::DataHandle * > outputHandles () const override
 Return this algorithm's output handles.
Gaudi::Details::PropertyBase & declareProperty (Gaudi::Property< T, V, H > &t)
void updateVHKA (Gaudi::Details::PropertyBase &)
MsgStream & msg () const
bool msgLvl (const MSG::Level lvl) const

Protected Member Functions

void renounceArray (SG::VarHandleKeyArray &handlesArray)
 remove all handles from I/O resolution
std::enable_if_t< std::is_void_v< std::result_of_t< decltype(&T::renounce)(T)> > &&!std::is_base_of_v< SG::VarHandleKeyArray, T > &&std::is_base_of_v< Gaudi::DataHandle, T >, void > renounce (T &h)
void extraDeps_update_handler (Gaudi::Details::PropertyBase &ExtraDeps)
 Add StoreName to extra input/output deps as needed.

Private Types

typedef ServiceHandle< StoreGateSvc > StoreGateSvc_t

Private Member Functions

void classifyLostTruthSegments (const xAOD::MuonSegmentContainer &segments, const SegmentEdgeGraph &graph, const std::unordered_map< std::uint64_t, float > &pairProbability, const std::vector< unsigned char > &thresholded, const std::vector< std::uint8_t > &keptNode, const std::vector< unsigned char > &nodeDropReason) const
 MC-only: classify every truth-labeled input segment that did not make it into the filtered container by the stage that removed it.
Gaudi::Details::PropertyBase & declareGaudiProperty (Gaudi::Property< T, V, H > &hndl, const SG::VarHandleKeyType &)
 specialization for handling Gaudi::Property<SG::VarHandleKey>

Private Attributes

SG::ReadHandleKey< xAOD::MuonSegmentContainer > m_segmentKey {this, "SegmentKey", "MuonSegmentsFromR4"}
SG::WriteDecorHandleKey< xAOD::MuonSegmentContainer > m_pairGateDecorKey
 Per-segment payload consumed by MlMsTrackSeeder: [componentId, isSeedAnchor] Empty means that the segment was rejected by the ML preselection.
SG::WriteHandleKey< ConstDataVector< xAOD::MuonSegmentContainer > > m_filteredSegmentKey
Gaudi::Property< float > m_pairGateThreshold
Gaudi::Property< unsigned int > m_maxEdgesPerNode
Gaudi::Property< bool > m_useDegreeCappedComponents
Gaudi::Property< bool > m_requireMutualTopKEdges
Gaudi::Property< bool > m_recoverOrphanNodes
Gaudi::Property< unsigned int > m_seedAnchorsPerComponent
Gaudi::Property< bool > m_anchorInnermostLayer
Gaudi::Property< bool > m_keepBestSegmentPerChamber
Gaudi::Property< unsigned int > m_minSegmentsPerComponent
ToolHandle< ISegmentEdgeClassifierTool > m_edgeClassifier {this, "EdgeClassifierTool", "MuonML::SegmentEdgeClassifierTool/SegmentEdgeClassifierTool"}
bool m_truthDiagEnabled {false}
 Whether truth-particle matching is available/enabled this job.
std::atomic< std::size_t > m_sumGraphNodes {0}
 Job-summed post-ONNX selection counters (see execute()).
std::atomic< std::size_t > m_sumThresholdedNodes {0}
std::atomic< std::size_t > m_sumMutualTopKPairs {0}
std::atomic< std::size_t > m_sumOneSidedTopKPairs {0}
std::atomic< std::size_t > m_sumOrphanRecoveryPairs {0}
std::atomic< std::size_t > m_sumTopologyNodes {0}
std::atomic< std::size_t > m_sumRetainedNodes {0}
std::atomic< std::size_t > m_sumChamberSuppressedNodes {0}
std::atomic< std::size_t > m_sumNodesRejectedByMinComponent {0}
std::atomic< std::size_t > m_sumRejectedComponents {0}
std::atomic< std::size_t > m_sumComponentsKept {0}
std::atomic< std::size_t > m_sumInputTruthSegments {0}
std::atomic< std::size_t > m_sumInputBkgSegments {0}
std::atomic< std::size_t > m_sumOnnxTruthSegments {0}
std::atomic< std::size_t > m_sumOnnxBkgSegments {0}
std::atomic< std::size_t > m_sumRetainedTruthSegments {0}
std::atomic< std::size_t > m_sumRetainedBkgSegments {0}
std::atomic< std::size_t > m_sumTrueEdgesTotal {0}
std::atomic< std::size_t > m_sumTrueEdgesPassed {0}
std::atomic< std::size_t > m_sumBkgEdgesTotal {0}
std::atomic< std::size_t > m_sumBkgEdgesPassed {0}
TruthLossCounters m_truthLoss ATLAS_THREAD_SAFE
 Every element is a std::atomic, updated only with atomic additions.
std::atomic< bool > m_warnedMissingToolDiagnostics {false}
DataObjIDColl m_extendedExtraObjects
 Extra output dependency collection, extended by AthAlgorithmDHUpdate to add symlinks.
StoreGateSvc_t m_evtStore
 Pointer to StoreGate (event store by default).
StoreGateSvc_t m_detStore
 Pointer to StoreGate (detector store by default).
std::vector< SG::VarHandleKeyArray * > m_vhka
bool m_varHandleArraysDeclared

Detailed Description

Definition at line 24 of file SegmentEdgeInferenceAlg.h.

Member Typedef Documentation

◆ StoreGateSvc_t

typedef ServiceHandle<StoreGateSvc> AthCommonDataStore< AthCommonMsg< Gaudi::Algorithm > >::StoreGateSvc_t
privateinherited

Definition at line 376 of file AthCommonDataStore.h.

Member Function Documentation

◆ classifyLostTruthSegments()

void MuonML::SegmentEdgeInferenceAlg::classifyLostTruthSegments ( const xAOD::MuonSegmentContainer & segments,
const SegmentEdgeGraph & graph,
const std::unordered_map< std::uint64_t, float > & pairProbability,
const std::vector< unsigned char > & thresholded,
const std::vector< std::uint8_t > & keptNode,
const std::vector< unsigned char > & nodeDropReason ) const
private

MC-only: classify every truth-labeled input segment that did not make it into the filtered container by the stage that removed it.

Called only when truth diagnostics are on.

Definition at line 549 of file SegmentEdgeInferenceAlg.cxx.

554 {
555 static_assert(kNumLossCategories == TruthLossCounters::kCategories);
556 constexpr std::size_t kColumns = TruthLossCounters::kColumns;
557 const std::size_t nInput = segments.size();
558
559 if (graph.inputNodeIndex.size() != nInput) {
560 if (!m_warnedMissingToolDiagnostics.exchange(true)) {
561 ATH_MSG_WARNING("Truth diagnostics requested, but the edge classifier tool "
562 "did not provide its pre-ONNX fates; set "
563 "EnableTruthDiagnostics on the tool as well. Skipping the "
564 "lost-segment classification.");
565 }
566 return;
567 }
568
569 // Truth particle of every input segment and the segments grouped per particle.
570 std::vector<const xAOD::TruthParticle*> truthPart(nInput, nullptr);
571 std::vector<std::int32_t> truthId(nInput, -1);
572 std::unordered_map<std::int32_t, std::vector<std::uint32_t>> byTruth;
573 {
574 std::uint32_t inputIndex = 0;
575 for (const xAOD::MuonSegment* segment : segments) {
576 if (const xAOD::TruthParticle* part = MuonR4::getTruthMatchedParticle(*segment)) {
577 truthPart[inputIndex] = part;
578 truthId[inputIndex] = static_cast<std::int32_t>(part->index());
579 byTruth[truthId[inputIndex]].push_back(inputIndex);
580 }
581 ++inputIndex;
582 }
583 }
584 const auto isRetained = [&](std::size_t input) {
585 const std::int32_t node = graph.inputNodeIndex[input];
586 return node >= 0 && keptNode[static_cast<std::size_t>(node)];
587 };
588
589 // Per segment: how far its best cross-chamber true pair got (1 = bucket cap,
590 // 2 = sector window, 3 = angle window, 4 = edge caps, 5 = scored, 0 = only
591 // same-chamber partners) and the score / index of its best scored pair.
592 const auto rankOf = [](PairGate gate) -> unsigned char {
593 switch (gate) {
594 case PairGate::BucketCap: return 1;
595 case PairGate::SectorWindow: return 2;
596 case PairGate::AngleWindow: return 3;
597 case PairGate::EdgeCaps: return 4;
598 case PairGate::Scored: return 5;
599 case PairGate::SameChamber: break;
600 }
601 return 0;
602 };
603 std::vector<unsigned char> bestRank(nInput, 0);
604 std::vector<float> bestScore(nInput, -1.f);
605 std::vector<std::int32_t> bestScoredPair(nInput, -1);
606 for (std::size_t k = 0; k < graph.truthPairFates.size(); ++k) {
607 const PairFate& fate = graph.truthPairFates[k];
608 const unsigned char rank = rankOf(fate.gate);
609 if (rank == 0) continue;
610 const std::array<std::uint32_t, 2> ends{fate.first, fate.second};
611 for (const std::uint32_t end : ends) bestRank[end] = std::max(bestRank[end], rank);
612 if (fate.gate != PairGate::Scored) continue;
613 const std::int32_t firstNode = graph.inputNodeIndex[fate.first];
614 const std::int32_t secondNode = graph.inputNodeIndex[fate.second];
615 if (firstNode < 0 || secondNode < 0) continue;
616 const auto found = pairProbability.find(undirectedPairKey(
617 static_cast<std::size_t>(firstNode), static_cast<std::size_t>(secondNode)));
618 const float score = found != pairProbability.end() ? found->second : 0.f;
619 for (const std::uint32_t end : ends) {
620 if (score > bestScore[end]) {
621 bestScore[end] = score;
622 bestScoredPair[end] = static_cast<std::int32_t>(k);
623 }
624 }
625 }
626
627 // Seedability per truth muon: at least 2 distinct chambers in the input,
628 // fewer than 2 among the retained segments.
629 std::unordered_map<std::int32_t, bool> seedLostByTruth;
630 std::array<std::size_t, 4> muonsSeedable{};
631 std::array<std::size_t, 4> muonsSeedLost{};
632 for (const auto& entry : byTruth) {
633 std::unordered_set<int> inputChambers;
634 std::unordered_set<int> retainedChambers;
635 for (const std::uint32_t input : entry.second) {
636 const int chamber = static_cast<int>(segments[input]->chamberIndex());
637 inputChambers.insert(chamber);
638 if (isRetained(input)) retainedChambers.insert(chamber);
639 }
640 const bool seedable = inputChambers.size() >= 2;
641 const bool lost = seedable && retainedChambers.size() < 2;
642 seedLostByTruth[entry.first] = lost;
643 if (!seedable) continue;
644 const std::size_t region = 1 + static_cast<std::size_t>(
645 regionOf(std::abs(truthPart[entry.second.front()]->eta())));
646 ++muonsSeedable[0];
647 ++muonsSeedable[region];
648 if (lost) {
649 ++muonsSeedLost[0];
650 ++muonsSeedLost[region];
651 }
652 }
653
654 std::array<std::size_t, kNumLossCategories * kColumns> category{};
655 std::array<std::size_t, 5> scoreBin{};
656 std::array<std::size_t, 2> sectorDelta{};
657 std::array<std::size_t, 4> layerPair{};
658 std::array<std::size_t, 4> muonSegments{};
659 std::array<std::size_t, 3> precisionHits{};
660 for (std::size_t i = 0; i < nInput; ++i) {
661 if (truthId[i] < 0 || isRetained(i)) continue;
662 const std::vector<std::uint32_t>& members = byTruth[truthId[i]];
663 const std::int32_t node = graph.inputNodeIndex[i];
664
665 std::size_t cat{};
666 if (members.size() == 1) {
667 cat = kNoPartnerSingleton;
668 } else if (bestRank[i] == 0) {
669 cat = kNoPartnerSameChamber;
670 } else if (node == kDroppedByBucketCap) {
671 cat = kBucketSelf;
672 } else if (node >= 0 && thresholded[static_cast<std::size_t>(node)]) {
673 switch (nodeDropReason[static_cast<std::size_t>(node)]) {
674 case kReasonChamberDedup: cat = kDedupChamber; break;
675 case kReasonComponentRejected: cat = kComponentRejected; break;
676 default: cat = kNotSelected; break;
677 }
678 } else {
679 switch (bestRank[i]) {
680 case 5: cat = kBelowThreshold; break;
681 case 4: cat = kEdgeCaps; break;
682 case 3: cat = kAngleWindow; break;
683 case 2: cat = kSectorWindow; break;
684 default: cat = kBucketPartners; break;
685 }
686 }
687
688 const std::size_t region = 1 + static_cast<std::size_t>(
689 regionOf(std::abs(truthPart[i]->eta())));
690 ++category[cat * kColumns];
691 ++category[cat * kColumns + region];
692 if (seedLostByTruth[truthId[i]]) ++category[cat * kColumns + 4];
693
694 if (cat != kBelowThreshold) continue;
695 if (bestScoredPair[i] >= 0) {
696 const PairFate& fate = graph.truthPairFates[static_cast<std::size_t>(bestScoredPair[i])];
697 const float score = bestScore[i];
698 ++scoreBin[score < 1e-3f ? 0 : score < 1e-2f ? 1 : score < 2e-2f ? 2
699 : score < 5e-2f ? 3 : 4];
700 ++sectorDelta[fate.sectorDelta == 0 ? 0 : 1];
701 const std::uint32_t other = fate.first == i ? fate.second : fate.first;
702 int rankA = layerRadialRank(Muon::MuonStationIndex::toLayerIndex(
703 segments[i]->chamberIndex()));
704 int rankB = layerRadialRank(Muon::MuonStationIndex::toLayerIndex(
705 segments[other]->chamberIndex()));
706 if (rankA > rankB) std::swap(rankA, rankB);
707 ++layerPair[(rankA == 0 && rankB == 1) ? 0
708 : (rankA == 1 && rankB == 2) ? 1
709 : (rankA == 0 && rankB == 2) ? 2 : 3];
710 }
711 ++muonSegments[std::min<std::size_t>(members.size(), 5) - 2];
712 const unsigned int hits = segments[i]->nPrecisionHits();
713 ++precisionHits[hits <= 4 ? 0 : (hits <= 6 ? 1 : 2)];
714 }
715
716 for (std::size_t c = 0; c < category.size(); ++c) m_truthLoss.category[c] += category[c];
717 for (std::size_t c = 0; c < scoreBin.size(); ++c) m_truthLoss.scoreBin[c] += scoreBin[c];
718 for (std::size_t c = 0; c < sectorDelta.size(); ++c) m_truthLoss.sectorDelta[c] += sectorDelta[c];
719 for (std::size_t c = 0; c < layerPair.size(); ++c) m_truthLoss.layerPair[c] += layerPair[c];
720 for (std::size_t c = 0; c < muonSegments.size(); ++c) m_truthLoss.muonSegments[c] += muonSegments[c];
721 for (std::size_t c = 0; c < precisionHits.size(); ++c) m_truthLoss.precisionHits[c] += precisionHits[c];
722 for (std::size_t c = 0; c < muonsSeedable.size(); ++c) {
723 m_truthLoss.muonsSeedable[c] += muonsSeedable[c];
724 m_truthLoss.muonsSeedLost[c] += muonsSeedLost[c];
725 }
726}
Scalar eta() const
pseudorapidity method
#define ATH_MSG_WARNING(x,...)
size_type size() const noexcept
Returns the number of elements in the collection.
PairGate
Gate that stopped (or did not stop) a pair of input segments from reaching ONNX.
@ Scored
the pair was sent to ONNX
@ SameChamber
same-chamber pair dropped by design
@ AngleWindow
cos(opening angle) below MaxDeltaThetaDeg
@ EdgeCaps
candidate evicted by the pre-ONNX per-node / per-target-chamber caps
@ SectorWindow
|delta sector| > MaxDeltaSector
@ BucketCap
an endpoint was dropped by MaxSegmentsPerBucket
constexpr std::int32_t kDroppedByBucketCap
Values of SegmentEdgeGraph::inputNodeIndex for input segments without a node.
const xAOD::TruthParticle * getTruthMatchedParticle(const xAOD::MuonSegment &segment)
Returns the particle truth-matched to the segment.
LayerIndex toLayerIndex(ChIndex index)
convert ChIndex into LayerIndex
void swap(ElementLinkVector< DOBJ > &lhs, ElementLinkVector< DOBJ > &rhs)
TruthParticle_v1 TruthParticle
Typedef to implementation.
MuonSegment_v1 MuonSegment
Reference the current persistent version:

◆ declareGaudiProperty()

Gaudi::Details::PropertyBase & AthCommonDataStore< AthCommonMsg< Gaudi::Algorithm > >::declareGaudiProperty ( Gaudi::Property< T, V, H > & hndl,
const SG::VarHandleKeyType &  )
inlineprivateinherited

specialization for handling Gaudi::Property<SG::VarHandleKey>

Definition at line 156 of file AthCommonDataStore.h.

158 {
160 hndl.value(),
161 hndl.documentation());
162
163 }
Gaudi::Details::PropertyBase & declareProperty(Gaudi::Property< T, V, H > &t)

◆ declareProperty()

Gaudi::Details::PropertyBase & AthCommonDataStore< AthCommonMsg< Gaudi::Algorithm > >::declareProperty ( Gaudi::Property< T, V, H > & t)
inlineinherited

Definition at line 145 of file AthCommonDataStore.h.

145 {
146 typedef typename SG::HandleClassifier<T>::type htype;
148 }
Gaudi::Details::PropertyBase & declareGaudiProperty(Gaudi::Property< T, V, H > &hndl, const SG::VarHandleKeyType &)
specialization for handling Gaudi::Property<SG::VarHandleKey>

◆ detStore()

const ServiceHandle< StoreGateSvc > & AthCommonDataStore< AthCommonMsg< Gaudi::Algorithm > >::detStore ( ) const
inlineinherited

The standard StoreGateSvc/DetectorStore Returns (kind of) a pointer to the StoreGateSvc.

Definition at line 95 of file AthCommonDataStore.h.

◆ evtStore()

ServiceHandle< StoreGateSvc > & AthCommonDataStore< AthCommonMsg< Gaudi::Algorithm > >::evtStore ( )
inlineinherited

The standard StoreGateSvc (event store) Returns (kind of) a pointer to the StoreGateSvc.

Definition at line 85 of file AthCommonDataStore.h.

◆ execute()

StatusCode MuonML::SegmentEdgeInferenceAlg::execute ( const EventContext & ctx) const
override

Definition at line 125 of file SegmentEdgeInferenceAlg.cxx.

125 {
126 const xAOD::MuonSegmentContainer* segments{};
127 ATH_CHECK(SG::get(segments, m_segmentKey, ctx));
128 // Evaluated once per event; short-circuits without touching the message
129 // service unless the property was explicitly enabled.
130 const bool truthDiag = m_truthDiagEnabled && msgLvl(MSG::DEBUG);
131 ATH_MSG_DEBUG("Event " << ctx.eventID().event_number()
132 << ": input segments in '" << m_segmentKey.key()
133 << "' = " << segments->size());
134
135 if (truthDiag) {
136 std::size_t truthSegs = 0, bkgSegs = 0;
137 for (const xAOD::MuonSegment* seg : *segments) {
138 MuonR4::getTruthMatchedParticle(*seg) ? ++truthSegs : ++bkgSegs;
139 }
140 m_sumInputTruthSegments += truthSegs;
141 m_sumInputBkgSegments += bkgSegs;
142 }
143
144 SegmentEdgeGraph graph{};
145 std::vector<SegmentEdgeScore> scores{};
146 ATH_CHECK(m_edgeClassifier->buildGraph(ctx, *segments, graph));
147 ATH_MSG_DEBUG("Event " << ctx.eventID().event_number()
148 << ": built graph with nodes=" << graph.nNodes
149 << ", edges=" << graph.nEdges);
150
151 // Per-node truth-particle grouping key (-1 = unlabeled), used for the
152 // segment-level funnel and for the edge-level true/background split.
153 std::vector<int32_t> nodeTruthId;
154 if (truthDiag) {
155 nodeTruthId.assign(graph.nNodes, -1);
156 std::size_t truthSegs = 0, bkgSegs = 0;
157 for (std::size_t node = 0; node < graph.nNodes; ++node) {
158 const xAOD::TruthParticle* truthPart =
159 MuonR4::getTruthMatchedParticle(*graph.segments[node]);
160 if (truthPart) {
161 nodeTruthId[node] = static_cast<int32_t>(truthPart->index());
162 ++truthSegs;
163 } else {
164 ++bkgSegs;
165 }
166 }
167 m_sumOnnxTruthSegments += truthSegs;
168 m_sumOnnxBkgSegments += bkgSegs;
169 }
170
171 ATH_CHECK(m_edgeClassifier->classifyEdges(ctx, graph, scores));
172 if (!scores.empty()) {
173 float minProb = std::numeric_limits<float>::max();
174 float maxProb = std::numeric_limits<float>::lowest();
175 for (const SegmentEdgeScore& score : scores) {
176 minProb = std::min(minProb, score.probability);
177 maxProb = std::max(maxProb, score.probability);
178 }
179 ATH_MSG_DEBUG("Event " << ctx.eventID().event_number()
180 << ": edge scores=" << scores.size()
181 << ", prob range=[" << minProb << ", " << maxProb << "]");
182 } else {
183 ATH_MSG_DEBUG("Event " << ctx.eventID().event_number()
184 << ": no edge scores produced");
185 }
186
187 // Symmetrise directed model outputs once. Everything downstream uses the
188 // score of an undirected segment association.
189 std::unordered_map<std::uint64_t, float> pairProbability;
190 pairProbability.reserve(scores.size());
191 for (const SegmentEdgeScore& score : scores) {
192 if (score.src >= graph.nNodes || score.dst >= graph.nNodes ||
193 score.src == score.dst) {
194 continue;
195 }
196 const std::uint64_t key = undirectedPairKey(score.src, score.dst);
197 auto [it, inserted] = pairProbability.emplace(key, score.probability);
198 if (!inserted) it->second = std::max(it->second, score.probability);
199 }
200
201 // Edge-level recall/false-positive check: candidate pairs that reached ONNX
202 // true pairs reach PairGateThreshold, versus how many background pairs do.
203 if (truthDiag) {
204 std::size_t trueTotal = 0, truePassed = 0, bkgTotal = 0, bkgPassed = 0;
205 for (const auto& [key, probability] : pairProbability) {
206 const auto [first, second] = unpackPairKey(key);
207 if (first >= graph.nNodes || second >= graph.nNodes) continue;
208 const bool isTrueEdge =
209 nodeTruthId[first] >= 0 && nodeTruthId[first] == nodeTruthId[second];
210 const bool passed = probability >= m_pairGateThreshold.value();
211 if (isTrueEdge) {
212 ++trueTotal;
213 truePassed += passed;
214 } else {
215 ++bkgTotal;
216 bkgPassed += passed;
217 }
218 }
219 m_sumTrueEdgesTotal += trueTotal;
220 m_sumTrueEdgesPassed += truePassed;
221 m_sumBkgEdgesTotal += bkgTotal;
222 m_sumBkgEdgesPassed += bkgPassed;
223 }
224
225 // Form a sparse, score-ranked topology. Mutual top-K associations retain
226 // locally consistent paths while suppressing one-sided bridges.
227 using WeightedEdge = std::pair<std::uint64_t, float>;
228 const auto betterWeightedEdge = [](const WeightedEdge& first,
229 const WeightedEdge& second) {
230 const int probabilityOrder = InferenceUtils::compareFloatDescending(
231 first.second, second.second);
232 if (probabilityOrder != 0) {
233 return probabilityOrder < 0;
234 }
235 return first.first < second.first;
236 };
237 std::vector<std::vector<WeightedEdge>> edgesByNode(graph.nNodes);
238 std::size_t thresholdPairs = 0;
239 for (const auto& [key, probability] : pairProbability) {
240 if (probability < m_pairGateThreshold.value()) continue;
241 const auto [first, second] = unpackPairKey(key);
242 if (first >= graph.nNodes || second >= graph.nNodes) continue;
243 edgesByNode[first].emplace_back(key, probability);
244 edgesByNode[second].emplace_back(key, probability);
245 ++thresholdPairs;
246 }
247
248 std::size_t thresholdedNodes = 0;
249 for (const std::vector<WeightedEdge>& nodeEdges : edgesByNode) {
250 thresholdedNodes += !nodeEdges.empty();
251 }
252
253 std::unordered_map<std::uint64_t, unsigned char> nominations;
254 nominations.reserve(thresholdPairs);
255 for (std::vector<WeightedEdge>& nodeEdges : edgesByNode) {
256 std::sort(nodeEdges.begin(), nodeEdges.end(), betterWeightedEdge);
257 if (m_maxEdgesPerNode.value() != 0 &&
258 nodeEdges.size() > m_maxEdgesPerNode.value()) {
259 nodeEdges.resize(m_maxEdgesPerNode.value());
260 }
261 for (const WeightedEdge& edge : nodeEdges) {
262 ++nominations[edge.first];
263 }
264 }
265
266 std::size_t mutualTopKPairs = 0;
267 std::size_t oneSidedTopKPairs = 0;
268 for (const auto& [_, count] : nominations) {
269 if (count == 2) {
270 ++mutualTopKPairs;
271 } else {
272 ++oneSidedTopKPairs;
273 }
274 }
275
276 std::unordered_set<std::uint64_t> selectedPairKeys;
277 selectedPairKeys.reserve(thresholdPairs);
278 if (m_useDegreeCappedComponents.value()) {
279 // Apply a score-ordered global degree cap when explicitly requested.
280 std::vector<WeightedEdge> acceptedPairs;
281 acceptedPairs.reserve(thresholdPairs);
282 for (const auto& [key, probability] : pairProbability) {
283 if (probability < m_pairGateThreshold.value()) continue;
284 acceptedPairs.emplace_back(key, probability);
285 }
286 std::sort(acceptedPairs.begin(), acceptedPairs.end(), betterWeightedEdge);
287
288 const unsigned int maxDegree = m_maxEdgesPerNode.value();
289 std::vector<unsigned int> degree(graph.nNodes, 0);
290 for (const WeightedEdge& edge : acceptedPairs) {
291 const auto [first, second] = unpackPairKey(edge.first);
292 if (maxDegree != 0 &&
293 (degree[first] >= maxDegree || degree[second] >= maxDegree)) {
294 continue;
295 }
296 selectedPairKeys.insert(edge.first);
297 ++degree[first];
298 ++degree[second];
299 }
300 } else {
301 for (const auto& [key, count] : nominations) {
302 if (m_requireMutualTopKEdges.value() && count != 2) continue;
303 selectedPairKeys.insert(key);
304 }
305 }
306
307 // A mutual top-K selection can leave an endpoint without an association.
308 // Add its best thresholded edge, at most once per orphaned endpoint.
309 std::size_t orphanRecoveryPairs = 0;
310 if (!m_useDegreeCappedComponents.value() &&
311 m_requireMutualTopKEdges.value() &&
312 m_recoverOrphanNodes.value()) {
313 std::vector<unsigned char> selectedNode(graph.nNodes, 0);
314 for (const std::uint64_t key : selectedPairKeys) {
315 const auto [first, second] = unpackPairKey(key);
316 if (first < graph.nNodes) selectedNode[first] = 1;
317 if (second < graph.nNodes) selectedNode[second] = 1;
318 }
319 for (std::size_t node = 0; node < graph.nNodes; ++node) {
320 if (selectedNode[node] || edgesByNode[node].empty()) continue;
321 const std::uint64_t key = edgesByNode[node].front().first;
322 const auto [first, second] = unpackPairKey(key);
323 if (first >= graph.nNodes || second >= graph.nNodes) continue;
324 if (selectedPairKeys.insert(key).second) ++orphanRecoveryPairs;
325 selectedNode[first] = 1;
326 selectedNode[second] = 1;
327 }
328 }
329
330 std::vector<std::uint64_t> selectedPairs{selectedPairKeys.begin(),
331 selectedPairKeys.end()};
332 std::sort(selectedPairs.begin(), selectedPairs.end());
333
334 DisjointSet components{graph.nNodes};
335 std::vector<bool> activeNode(graph.nNodes, false);
336 for (const std::uint64_t key : selectedPairs) {
337 const auto [first, second] = unpackPairKey(key);
338 components.unite(first, second);
339 activeNode[first] = true;
340 activeNode[second] = true;
341 }
342
343 std::unordered_map<std::size_t, std::vector<std::size_t>> byRoot;
344 byRoot.reserve(graph.nNodes);
345 for (std::size_t node = 0; node < graph.nNodes; ++node) {
346 if (activeNode[node]) byRoot[components.find(node)].push_back(node);
347 }
348
349 // Deterministic component IDs make debugging and validation reproducible.
350 std::vector<std::vector<std::size_t>> componentNodes;
351 componentNodes.reserve(byRoot.size());
352 for (auto& [_, nodes] : byRoot) {
353 std::sort(nodes.begin(), nodes.end());
354 componentNodes.push_back(std::move(nodes));
355 }
356 std::ranges::sort(componentNodes,
357 [](const auto& first, const auto& second) {
358 return first.front() < second.front();
359 });
360
361 xAOD::ContainerDecorator<xAOD::MuonSegmentContainer, std::vector<unsigned>>
362 decor{m_pairGateDecorKey, ctx};
363
364 std::vector<std::uint8_t> keptNode(graph.nNodes, false);
365 std::vector<std::uint8_t> nodeDropReason;
366 if (truthDiag) {
367 nodeDropReason.assign(graph.nNodes, kReasonNotSelected);
368 }
369
370 std::size_t topologyNodes = 0;
371 std::size_t retainedNodes = 0;
372 std::size_t chamberSuppressedNodes = 0;
373 std::size_t rejectedComponents = 0;
374 std::size_t componentsKept = 0;
375 std::size_t anchors = 0;
376 std::size_t nodesRejectedByMinComponent = 0;
377 unsigned nextComponentId = 1;
378 const InferenceUtils::SegmentQualityOrder betterSegment{};
379
380 const auto isBetterNode = [&](std::size_t candidate, std::size_t incumbent) {
381 if (betterSegment(graph.segments[candidate], graph.segments[incumbent])) {
382 return true;
383 }
384 if (betterSegment(graph.segments[incumbent], graph.segments[candidate])) {
385 return false;
386 }
387 return candidate < incumbent;
388 };
389
390 for (const std::vector<std::size_t>& rawNodes : componentNodes) {
391 topologyNodes += rawNodes.size();
392 std::vector<std::size_t> retained = rawNodes;
393
394 // The seeder resolves same-chamber alternatives while building a seed.
395 // Retaining only the highest-ranked representative is therefore optional.
396 if (m_keepBestSegmentPerChamber.value()) {
397 std::unordered_map<int, std::size_t> bestByChamber;
398 bestByChamber.reserve(rawNodes.size());
399 for (const std::size_t node : rawNodes) {
400 const int chamber =
401 static_cast<int>(graph.segments[node]->chamberIndex());
402 const auto found = bestByChamber.find(chamber);
403 if (found == bestByChamber.end() || isBetterNode(node, found->second)) {
404 bestByChamber[chamber] = node;
405 }
406 }
407 retained.clear();
408 retained.reserve(bestByChamber.size());
409 for (const auto& [_, node] : bestByChamber) retained.push_back(node);
410 std::sort(retained.begin(), retained.end());
411 chamberSuppressedNodes += rawNodes.size() - retained.size();
412 }
413
414 // Provisional reasons; nodes that end up kept are identified by keptNode.
415 if (truthDiag) {
416 for (const std::size_t node : rawNodes) {
417 nodeDropReason[node] = kReasonChamberDedup;
418 }
419 for (const std::size_t node : retained) {
420 nodeDropReason[node] = kReasonComponentRejected;
421 }
422 }
423
424 if (retained.size() < m_minSegmentsPerComponent.value()) {
425 nodesRejectedByMinComponent += retained.size();
426 ++rejectedComponents;
427 continue;
428 }
429
430 // Only ranked component members launch seeds. The edge score therefore
431 // reduces seed attempts directly rather than serving only as a label.
432 std::vector<std::size_t> rankedNodes{retained};
433 if (m_anchorInnermostLayer.value()) {
434 // Anchor only on the inner segment(s)
435 int bestRank = std::numeric_limits<int>::max();
436 for (const std::size_t node : rankedNodes) {
437 bestRank = std::min(
438 bestRank,
440 graph.segments[node]->chamberIndex())));
441 }
442 std::erase_if(rankedNodes, [&](std::size_t node) {
443 return layerRadialRank(Muon::MuonStationIndex::toLayerIndex(
444 graph.segments[node]->chamberIndex())) != bestRank;
445 });
446 } else {
447 std::ranges::sort(rankedNodes, isBetterNode);
448 const std::size_t nAnchors = m_seedAnchorsPerComponent.value() == 0
449 ? rankedNodes.size()
450 : std::min<std::size_t>(m_seedAnchorsPerComponent.value(),
451 rankedNodes.size());
452 rankedNodes.resize(nAnchors);
453 }
454 if (rankedNodes.empty()) {
455 ++rejectedComponents;
456 continue;
457 }
458 std::ranges::sort(rankedNodes);
459
460 const unsigned componentId = nextComponentId++;
461 for (const std::size_t node : retained) {
462 const bool isAnchor = Acts::rangeContainsValue(rankedNodes, node);
463 decor(*graph.segments[node]) = {
464 componentId, static_cast<unsigned int>(isAnchor)};
465 keptNode[node] = true;
466 }
467 retainedNodes += retained.size();
468 anchors += rankedNodes.size();
469 ++componentsKept;
470 }
471
472 if (truthDiag) {
473 std::size_t truthSegs = 0, bkgSegs = 0;
474 for (std::size_t node = 0; node < graph.nNodes; ++node) {
475 if (!keptNode[node]) {
476 continue;
477 }
478 nodeTruthId[node] >= 0 ? ++truthSegs : ++bkgSegs;
479 }
480 m_sumRetainedTruthSegments += truthSegs;
481 m_sumRetainedBkgSegments += bkgSegs;
482
483 std::vector<unsigned char> thresholded(graph.nNodes, 0);
484 for (std::size_t node = 0; node < graph.nNodes; ++node) {
485 thresholded[node] = !edgesByNode[node].empty();
486 }
487 classifyLostTruthSegments(*segments, graph, pairProbability, thresholded,
488 keptNode, nodeDropReason);
489 }
490
491 if (!m_filteredSegmentKey.empty()) {
492 auto connectedSegments =
493 std::make_unique<ConstDataVector<xAOD::MuonSegmentContainer>>(
495 connectedSegments->reserve(graph.nNodes);
496 for (std::size_t node = 0; node < graph.nNodes; ++node) {
497 if (!keptNode[node] || !graph.segments[node]) continue;
498 connectedSegments->push_back(graph.segments[node]);
499 }
500
501 const std::size_t nConnectedSegments = connectedSegments->size();
502 SG::WriteHandle<ConstDataVector<xAOD::MuonSegmentContainer>> connectedHandle{
504 ATH_CHECK(connectedHandle.record(std::move(connectedSegments)));
505 ATH_MSG_DEBUG("Event " << ctx.eventID().event_number()
506 << ": wrote " << nConnectedSegments
507 << " ML-connected segment(s) to '"
508 << m_filteredSegmentKey.key() << "'");
509 }
510
511 ATH_MSG_DEBUG("Event " << ctx.eventID().event_number()
512 << ": ML components graphNodes=" << graph.nNodes
513 << ", thresholdedNodes=" << thresholdedNodes
514 << ", thresholdPairs=" << thresholdPairs
515 << ", mutualTopKPairs=" << mutualTopKPairs
516 << ", oneSidedTopKPairs=" << oneSidedTopKPairs
517 << ", selectedPairs=" << selectedPairKeys.size()
518 << ", components=" << componentsKept
519 << ", topologyNodes=" << topologyNodes
520 << ", retainedNodes=" << retainedNodes
521 << ", chamberSuppressedNodes=" << chamberSuppressedNodes
522 << ", nodesRejectedByMinComponent=" << nodesRejectedByMinComponent
523 << ", keepBestSegmentPerChamber=" << m_keepBestSegmentPerChamber.value()
524 << ", seedAnchors=" << anchors
525 << ", rejectedComponents=" << rejectedComponents
526 << ", threshold=" << m_pairGateThreshold.value()
527 << ", maxEdgesPerNode=" << m_maxEdgesPerNode.value()
528 << ", orphanRecoveryPairs=" << orphanRecoveryPairs
529 << ", mutualTopK=" << m_requireMutualTopKEdges.value()
530 << ", degreeCapped=" << m_useDegreeCappedComponents.value());
531
532 // Job-summed diagnostics
533 if (msgLvl(MSG::DEBUG)) {
534 m_sumGraphNodes += graph.nNodes;
535 m_sumThresholdedNodes += thresholdedNodes;
536 m_sumMutualTopKPairs += mutualTopKPairs;
537 m_sumOneSidedTopKPairs += oneSidedTopKPairs;
538 m_sumOrphanRecoveryPairs += orphanRecoveryPairs;
539 m_sumTopologyNodes += topologyNodes;
540 m_sumRetainedNodes += retainedNodes;
541 m_sumChamberSuppressedNodes += chamberSuppressedNodes;
542 m_sumNodesRejectedByMinComponent += nodesRejectedByMinComponent;
543 m_sumRejectedComponents += rejectedComponents;
544 m_sumComponentsKept += componentsKept;
545 }
546 return StatusCode::SUCCESS;
547}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_DEBUG(x,...)
std::vector< std::vector< float > > scores
bool passed(DecisionID id, const DecisionIDContainer &)
checks if required decision ID is in the set of IDs in the container
static const Attributes_t empty
bool msgLvl(const MSG::Level lvl) const
std::atomic< std::size_t > m_sumOnnxBkgSegments
std::atomic< std::size_t > m_sumChamberSuppressedNodes
Gaudi::Property< bool > m_keepBestSegmentPerChamber
std::atomic< std::size_t > m_sumRetainedNodes
Gaudi::Property< bool > m_anchorInnermostLayer
SG::WriteDecorHandleKey< xAOD::MuonSegmentContainer > m_pairGateDecorKey
Per-segment payload consumed by MlMsTrackSeeder: [componentId, isSeedAnchor] Empty means that the seg...
Gaudi::Property< bool > m_useDegreeCappedComponents
std::atomic< std::size_t > m_sumOrphanRecoveryPairs
ToolHandle< ISegmentEdgeClassifierTool > m_edgeClassifier
Gaudi::Property< unsigned int > m_maxEdgesPerNode
std::atomic< std::size_t > m_sumThresholdedNodes
std::atomic< std::size_t > m_sumRetainedBkgSegments
Gaudi::Property< bool > m_requireMutualTopKEdges
std::atomic< std::size_t > m_sumOneSidedTopKPairs
std::atomic< std::size_t > m_sumInputBkgSegments
void classifyLostTruthSegments(const xAOD::MuonSegmentContainer &segments, const SegmentEdgeGraph &graph, const std::unordered_map< std::uint64_t, float > &pairProbability, const std::vector< unsigned char > &thresholded, const std::vector< std::uint8_t > &keptNode, const std::vector< unsigned char > &nodeDropReason) const
MC-only: classify every truth-labeled input segment that did not make it into the filtered container ...
std::atomic< std::size_t > m_sumTrueEdgesPassed
bool m_truthDiagEnabled
Whether truth-particle matching is available/enabled this job.
std::atomic< std::size_t > m_sumBkgEdgesPassed
std::atomic< std::size_t > m_sumBkgEdgesTotal
std::atomic< std::size_t > m_sumGraphNodes
Job-summed post-ONNX selection counters (see execute()).
Gaudi::Property< unsigned int > m_minSegmentsPerComponent
std::atomic< std::size_t > m_sumOnnxTruthSegments
std::atomic< std::size_t > m_sumInputTruthSegments
std::atomic< std::size_t > m_sumNodesRejectedByMinComponent
std::atomic< std::size_t > m_sumTrueEdgesTotal
std::atomic< std::size_t > m_sumRetainedTruthSegments
SG::WriteHandleKey< ConstDataVector< xAOD::MuonSegmentContainer > > m_filteredSegmentKey
Gaudi::Property< bool > m_recoverOrphanNodes
std::atomic< std::size_t > m_sumMutualTopKPairs
std::atomic< std::size_t > m_sumRejectedComponents
SG::ReadHandleKey< xAOD::MuonSegmentContainer > m_segmentKey
Gaudi::Property< unsigned int > m_seedAnchorsPerComponent
Gaudi::Property< float > m_pairGateThreshold
std::atomic< std::size_t > m_sumTopologyNodes
std::atomic< std::size_t > m_sumComponentsKept
StatusCode record(std::unique_ptr< T > data)
Record a const object to the store.
int count(std::string s, const std::string &regx)
count how many occurances of a regx are in a string
Definition hcg.cxx:148
bool first
Definition DeMoScan.py:534
int compareFloatDescending(float first, float second)
Three-way descending comparison which also orders NaN last.
@ VIEW_ELEMENTS
this data object is a view, it does not own its elmts
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.
std::size_t erase_if(T_container &container, T_Func pred)
MuonSegmentContainer_v1 MuonSegmentContainer
Definition of the current "MuonSegment container version".

◆ extraDeps_update_handler()

void AthCommonDataStore< AthCommonMsg< Gaudi::Algorithm > >::extraDeps_update_handler ( Gaudi::Details::PropertyBase & ExtraDeps)
protectedinherited

Add StoreName to extra input/output deps as needed.

use the logic of the VarHandleKey to parse the DataObjID keys supplied via the ExtraInputs and ExtraOuputs Properties to add the StoreName if it's not explicitly given

◆ extraOutputDeps()

const DataObjIDColl & AthCommonAlgorithm< Gaudi::Algorithm >::extraOutputDeps ( ) const
overridevirtualinherited

Return the list of extra output dependencies.

This list is extended to include symlinks implied by inheritance relations.

Definition at line 89 of file AthCommonAlgorithm.cxx.

54{
55 // If we didn't find any symlinks to add, just return the collection
56 // from the base class. Otherwise, return the extended collection.
57 if (!m_extendedExtraObjects.empty()) {
59 }
61}
Common base class for algorithms.

◆ filterPassed()

virtual bool AthCommonAlgorithm< Gaudi::Algorithm >::filterPassed ( const EventContext & ctx) const
inlinevirtualinherited

Get filter decision:

Definition at line 93 of file AthCommonAlgorithm.h.

93 {
94 return execState( ctx ).filterPassed();
95 }
virtual bool filterPassed(const EventContext &ctx) const
Get filter decision:

◆ finalize()

StatusCode MuonML::SegmentEdgeInferenceAlg::finalize ( )
override

Log of the post-ONNX rank/mutuality and min-component-size selection.

Definition at line 728 of file SegmentEdgeInferenceAlg.cxx.

728 {
730 "SegmentEdgeInferenceAlg post-ONNX selection summary (job-summed): "
731 << "graphNodes=" << m_sumGraphNodes
732 << ", thresholdedNodes=" << m_sumThresholdedNodes
733 << " (>=PairGateThreshold=" << m_pairGateThreshold.value() << ")"
734 << ", mutualTopKPairs=" << m_sumMutualTopKPairs
735 << ", oneSidedTopKPairs(rejected by RequireMutualTopKEdges="
737 << " (MaxEdgesPerNode=" << m_maxEdgesPerNode.value()
738 << "; a thresholded edge is dropped here purely by rank, "
739 "independent of PairGateThreshold)"
740 << ", orphanRecoveryPairs=" << m_sumOrphanRecoveryPairs
741 << ", chamberSuppressedNodes=" << m_sumChamberSuppressedNodes
742 << " (KeepBestSegmentPerChamber=" << m_keepBestSegmentPerChamber.value() << ")"
743 << ", nodesRejectedByMinComponent=" << m_sumNodesRejectedByMinComponent
744 << " (MinSegmentsPerComponent=" << m_minSegmentsPerComponent.value() << ")"
745 << ", rejectedComponents=" << m_sumRejectedComponents
746 << ", componentsKept=" << m_sumComponentsKept
747 << ", topologyNodes=" << m_sumTopologyNodes
748 << ", retainedNodes=" << m_sumRetainedNodes);
749
750 if (m_truthDiagEnabled) {
752 "SegmentEdgeInferenceAlg truth-vs-background funnel (job-summed; "
753 "truth segment = getTruthMatchedParticle(seg) != nullptr, matching "
754 "SegmentDumperAlg::m_segmentHasTruth, no isMuon() filter, no G4 "
755 "pseudo-label fallback): "
756 << "input: truth=" << m_sumInputTruthSegments
757 << " bkg=" << m_sumInputBkgSegments
758 << "; reachesONNX: truth=" << m_sumOnnxTruthSegments
759 << " bkg=" << m_sumOnnxBkgSegments
760 << "; retained: truth=" << m_sumRetainedTruthSegments
761 << " bkg=" << m_sumRetainedBkgSegments
762 << "; edge-level (same truth-particle index on both endpoints) at "
763 "PairGateThreshold=" << m_pairGateThreshold.value()
764 << ": trueEdges passed/total=" << m_sumTrueEdgesPassed << "/"
766 << ", bkgEdges passed/total=" << m_sumBkgEdgesPassed << "/"
768
769 constexpr std::array<const char*, TruthLossCounters::kCategories> names{
770 "A1 no possible partner: singleton muon segment",
771 "A2 no possible partner: same-chamber partners only",
772 "B0 never reached ONNX: segment dropped by MaxSegmentsPerBucket",
773 "B1 never reached ONNX: partners dropped by MaxSegmentsPerBucket",
774 "B2 never reached ONNX: outside sector window (MaxDeltaSector)",
775 "B3 never reached ONNX: outside angle window (MaxDeltaThetaDeg)",
776 "B4 never reached ONNX: pre-ONNX edge caps",
777 "C true pair(s) scored below PairGateThreshold",
778 "D1 passed threshold, removed: KeepBestSegmentPerChamber",
779 "D2 passed threshold, removed: component rejected (MinSegmentsPerComponent/anchors)",
780 "D3 passed threshold, removed: not selected by top-K/mutual gate"};
781 constexpr std::size_t kColumns = TruthLossCounters::kColumns;
782 ATH_MSG_DEBUG("Lost truth segments by stage (job-summed; truth segment = "
783 "input segment with a truth particle, lost = not in the filtered "
784 "container). Columns: all | barrel | transition | endcap | "
785 "belonging to a muon that lost seedability");
786 std::size_t lostTotal = 0;
787 for (std::size_t c = 0; c < names.size(); ++c) {
788 const auto value = [&](std::size_t col) { return m_truthLoss.category[c * kColumns + col].load(); };
789 lostTotal += value(0);
790 ATH_MSG_DEBUG(" " << names[c] << ": " << value(0) << " | " << value(1)
791 << " | " << value(2) << " | " << value(3) << " | " << value(4));
792 }
793 ATH_MSG_DEBUG(" check: sum of categories=" << lostTotal
794 << ", input truth - retained truth="
796 ATH_MSG_DEBUG(" truth muons with >=2 input chambers (all|barrel|transition|endcap): "
797 << m_truthLoss.muonsSeedable[0].load() << "|" << m_truthLoss.muonsSeedable[1].load()
798 << "|" << m_truthLoss.muonsSeedable[2].load() << "|" << m_truthLoss.muonsSeedable[3].load()
799 << "; of which <2 chambers retained: "
800 << m_truthLoss.muonsSeedLost[0].load() << "|" << m_truthLoss.muonsSeedLost[1].load()
801 << "|" << m_truthLoss.muonsSeedLost[2].load() << "|" << m_truthLoss.muonsSeedLost[3].load());
802 ATH_MSG_DEBUG(" category C descriptors: best true-pair score <1e-3/1e-3..1e-2/1e-2..2e-2/2e-2..5e-2/>=5e-2: "
803 << m_truthLoss.scoreBin[0].load() << "/" << m_truthLoss.scoreBin[1].load() << "/"
804 << m_truthLoss.scoreBin[2].load() << "/" << m_truthLoss.scoreBin[3].load() << "/"
805 << m_truthLoss.scoreBin[4].load()
806 << "; pair sector delta same/adjacent: " << m_truthLoss.sectorDelta[0].load() << "/"
807 << m_truthLoss.sectorDelta[1].load()
808 << "; layer pair Inner-Middle/Middle-Outer/Inner-Outer/other: "
809 << m_truthLoss.layerPair[0].load() << "/" << m_truthLoss.layerPair[1].load() << "/"
810 << m_truthLoss.layerPair[2].load() << "/" << m_truthLoss.layerPair[3].load()
811 << "; muon input segments 2/3/4/5+: " << m_truthLoss.muonSegments[0].load() << "/"
812 << m_truthLoss.muonSegments[1].load() << "/" << m_truthLoss.muonSegments[2].load() << "/"
813 << m_truthLoss.muonSegments[3].load()
814 << "; nPrecisionHits <=4/5-6/>=7: " << m_truthLoss.precisionHits[0].load() << "/"
815 << m_truthLoss.precisionHits[1].load() << "/" << m_truthLoss.precisionHits[2].load());
816 }
817 return StatusCode::SUCCESS;
818}

◆ initialize()

StatusCode MuonML::SegmentEdgeInferenceAlg::initialize ( )
override

Definition at line 116 of file SegmentEdgeInferenceAlg.cxx.

116 {
117 ATH_CHECK(m_segmentKey.initialize());
118 ATH_CHECK(m_pairGateDecorKey.initialize());
120 ATH_CHECK(m_edgeClassifier.retrieve());
121 m_truthDiagEnabled = m_edgeClassifier->enableTruthDiagnostics();
122 return StatusCode::SUCCESS;
123}

◆ inputHandles()

virtual std::vector< Gaudi::DataHandle * > AthCommonDataStore< AthCommonMsg< Gaudi::Algorithm > >::inputHandles ( ) const
overridevirtualinherited

Return this algorithm's input handles.

We override this to include handle instances from key arrays if they have not yet been declared. See comments on updateVHKA.

◆ isClonable()

virtual bool AthCommonAlgorithm< Gaudi::Algorithm >::isClonable ( ) const
inlineoverridevirtualinherited

Specify if the algorithm is clonable.

Only relevant for non-reentrant algorithms. Actual number of clones needs to be set via the "Cardinality" property.

Reimplemented in AFP_DigiTop, AlgB, AlgT, BCM_Digitization, CscDigitBuilder, CscDigitToCscRDO, G4AtlasAlg, G4RunAlg, HGTD_Digitization, HiveAlgBase, InDet::GNNSeedingTrackMaker, InDet::SCT_Clusterization, InDet::SiSPGNNTrackMaker, InDet::SiSPSeededTrackFinder, InDet::SiTrackerSpacePointFinder, ISF::SimKernelMT, ITk::StripDigitization, ITkPixelCablingAlg, ITkStripCablingAlg, LArHitEMapMaker, LArTTL1Maker, LUCID_DigiTop, LVL1::L1TopoSimulation, MergeCalibHits, MergeGenericMuonSimHitColl, MergeHijingPars, MergeMcEventCollection, MergeTrackRecordCollection, MergeTruthJets, MergeTruthParticles, MuonDigitizer, PileUpMTAlg, PixelDigitization, RoIBResultToxAOD, SCT_ByteStreamErrorsTestAlg, SCT_CablingCondAlgFromCoraCool, SCT_CablingCondAlgFromText, SCT_ConditionsParameterTestAlg, SCT_ConditionsSummaryTestAlg, SCT_ConfigurationConditionsTestAlg, SCT_Digitization, SCT_FlaggedConditionTestAlg, SCT_LinkMaskingTestAlg, SCT_MajorityConditionsTestAlg, SCT_ModuleVetoTestAlg, SCT_MonitorConditionsTestAlg, SCT_PrepDataToxAOD, SCT_RawDataToxAOD, SCT_ReadCalibChipDataTestAlg, SCT_ReadCalibDataTestAlg, SCT_RODVetoTestAlg, SCT_SensorsTestAlg, SCT_SiliconConditionsTestAlg, SCT_StripVetoTestAlg, SCT_TdaqEnabledTestAlg, SCT_TestCablingAlg, SCTEventFlagWriter, SCTRawDataProvider, SCTSiLorentzAngleTestAlg, SCTSiPropertiesTestAlg, SGInputLoader, Simulation::BeamEffectsAlg, TileHitVecToCnt, TileMuonFitter, TilePulseForTileMuonReceiver, TileRawChannelMaker, TRTDigitization, and ZDC_DigiTop.

Definition at line 68 of file AthCommonAlgorithm.h.

68 {
69 return true;
70 }

◆ msg()

MsgStream & AthCommonMsg< Gaudi::Algorithm >::msg ( ) const
inlineinherited

Definition at line 24 of file AthCommonMsg.h.

24 {
25 return this->msgStream();
26 }

◆ msgLvl()

bool AthCommonMsg< Gaudi::Algorithm >::msgLvl ( const MSG::Level lvl) const
inlineinherited

Definition at line 30 of file AthCommonMsg.h.

30 {
31 return this->msgLevel(lvl);
32 }

◆ outputHandles()

virtual std::vector< Gaudi::DataHandle * > AthCommonDataStore< AthCommonMsg< Gaudi::Algorithm > >::outputHandles ( ) const
overridevirtualinherited

Return this algorithm's output handles.

We override this to include handle instances from key arrays if they have not yet been declared. See comments on updateVHKA.

◆ renounce()

std::enable_if_t< std::is_void_v< std::result_of_t< decltype(&T::renounce)(T)> > &&!std::is_base_of_v< SG::VarHandleKeyArray, T > &&std::is_base_of_v< Gaudi::DataHandle, T >, void > AthCommonDataStore< AthCommonMsg< Gaudi::Algorithm > >::renounce ( T & h)
inlineprotectedinherited

Definition at line 368 of file AthCommonDataStore.h.

369 {
370 h.renounce();
372 }
std::enable_if_t< std::is_void_v< std::result_of_t< decltype(&T::renounce)(T)> > &&!std::is_base_of_v< SG::VarHandleKeyArray, T > &&std::is_base_of_v< Gaudi::DataHandle, T >, void > renounce(T &h)

◆ renounceArray()

void AthCommonDataStore< AthCommonMsg< Gaudi::Algorithm > >::renounceArray ( SG::VarHandleKeyArray & handlesArray)
inlineprotectedinherited

remove all handles from I/O resolution

Definition at line 352 of file AthCommonDataStore.h.

352 {
354 }

◆ setFilterPassed()

virtual void AthCommonAlgorithm< Gaudi::Algorithm >::setFilterPassed ( bool state,
const EventContext & ctx ) const
inlinevirtualinherited

Set filter decision:

Definition at line 99 of file AthCommonAlgorithm.h.

99 {
101 }
virtual void setFilterPassed(bool state, const EventContext &ctx) const
Set filter decision:

◆ sysExecute()

StatusCode AthCommonAlgorithm< Gaudi::Algorithm >::sysExecute ( const EventContext & ctx)
overridevirtualinherited

Execute an algorithm.

We override this in order to work around an issue with the Algorithm base class storing the event context in a member variable that can cause crashes in MT jobs.

Reimplemented in AthAnalysisAlgorithm.

Definition at line 80 of file AthCommonAlgorithm.cxx.

41{
42 return BaseAlg::sysExecute (ctx);
43}

◆ sysInitialize()

StatusCode AthCommonAlgorithm< Gaudi::Algorithm >::sysInitialize ( )
overridevirtualinherited

Override sysInitialize.

Override sysInitialize from the base class.

Loop through all output handles, and if they're WriteCondHandles, automatically register them and this Algorithm with the CondSvc

Scan through all outputHandles, and if they're WriteCondHandles, register them with the CondSvc

Reimplemented from AthCommonDataStore< AthCommonMsg< Gaudi::Algorithm > >.

Reimplemented in AthAnalysisAlgorithm, AthHistogramAlgorithm, HypoBase, InputMakerBase, and PyAthena::Alg.

Definition at line 60 of file AthCommonAlgorithm.cxx.

71 {
73
74 if (sc.isFailure()) {
75 return sc;
76 }
77
78 ServiceHandle<ICondSvc> cs("CondSvc",name());
79 for (auto h : outputHandles()) {
80 if (h->isCondition() && h->mode() == Gaudi::DataHandle::Writer) {
81 // do this inside the loop so we don't create the CondSvc until needed
82 if ( cs.retrieve().isFailure() ) {
83 ATH_MSG_WARNING("no CondSvc found: won't autoreg WriteCondHandles");
85 }
86 if (cs->regHandle(this,*h).isFailure()) {
88 ATH_MSG_ERROR("unable to register WriteCondHandle " << h->fullKey()
89 << " with CondSvc");
90 }
91 }
92 }
93 return sc;
94}
#define ATH_MSG_ERROR(x,...)
virtual StatusCode sysInitialize() override
virtual std::vector< Gaudi::DataHandle * > outputHandles() const override

◆ sysStart()

virtual StatusCode AthCommonDataStore< AthCommonMsg< Gaudi::Algorithm > >::sysStart ( )
overridevirtualinherited

Handle START transition.

We override this in order to make sure that conditions handle keys can cache a pointer to the conditions container.

◆ updateVHKA()

void AthCommonDataStore< AthCommonMsg< Gaudi::Algorithm > >::updateVHKA ( Gaudi::Details::PropertyBase & )
inlineinherited

Definition at line 298 of file AthCommonDataStore.h.

298 {
299 for (auto &a : m_vhka) {
301 for (auto k : keys) {
302 k->setOwner(this);
303 }
304 }
305 }

Member Data Documentation

◆ ATLAS_THREAD_SAFE

TruthLossCounters m_truthLoss MuonML::SegmentEdgeInferenceAlg::ATLAS_THREAD_SAFE
mutableprivate

Every element is a std::atomic, updated only with atomic additions.

Definition at line 132 of file SegmentEdgeInferenceAlg.h.

◆ m_anchorInnermostLayer

Gaudi::Property<bool> MuonML::SegmentEdgeInferenceAlg::m_anchorInnermostLayer
private
Initial value:
{
this, "AnchorInnermostLayer", false,
"Restrict seed anchors to inner segment(s)."}

Definition at line 72 of file SegmentEdgeInferenceAlg.h.

72 {
73 this, "AnchorInnermostLayer", false,
74 "Restrict seed anchors to inner segment(s)."};

◆ m_detStore

StoreGateSvc_t AthCommonDataStore< AthCommonMsg< Gaudi::Algorithm > >::m_detStore
privateinherited

Pointer to StoreGate (detector store by default).

Definition at line 381 of file AthCommonDataStore.h.

◆ m_edgeClassifier

ToolHandle<ISegmentEdgeClassifierTool> MuonML::SegmentEdgeInferenceAlg::m_edgeClassifier {this, "EdgeClassifierTool", "MuonML::SegmentEdgeClassifierTool/SegmentEdgeClassifierTool"}
private

Definition at line 81 of file SegmentEdgeInferenceAlg.h.

81{this, "EdgeClassifierTool", "MuonML::SegmentEdgeClassifierTool/SegmentEdgeClassifierTool"};

◆ m_evtStore

StoreGateSvc_t AthCommonDataStore< AthCommonMsg< Gaudi::Algorithm > >::m_evtStore
privateinherited

Pointer to StoreGate (event store by default).

Definition at line 378 of file AthCommonDataStore.h.

◆ m_extendedExtraObjects

DataObjIDColl AthCommonAlgorithm< Gaudi::Algorithm >::m_extendedExtraObjects
privateinherited

Extra output dependency collection, extended by AthAlgorithmDHUpdate to add symlinks.

Empty if no symlinks were found.

Definition at line 108 of file AthCommonAlgorithm.h.

◆ m_filteredSegmentKey

SG::WriteHandleKey<ConstDataVector<xAOD::MuonSegmentContainer> > MuonML::SegmentEdgeInferenceAlg::m_filteredSegmentKey
private
Initial value:
{
this, "FilteredSegmentKey", "",
"Optional VIEW container containing only segments incident to a "
"post-classifier selected edge; empty disables the output"}

Definition at line 50 of file SegmentEdgeInferenceAlg.h.

50 {
51 this, "FilteredSegmentKey", "",
52 "Optional VIEW container containing only segments incident to a "
53 "post-classifier selected edge; empty disables the output"};

◆ m_keepBestSegmentPerChamber

Gaudi::Property<bool> MuonML::SegmentEdgeInferenceAlg::m_keepBestSegmentPerChamber
private
Initial value:
{
this, "KeepBestSegmentPerChamber", true,
"Keep only the ML-best segment in each chamber within a component"}

Definition at line 75 of file SegmentEdgeInferenceAlg.h.

75 {
76 this, "KeepBestSegmentPerChamber", true,
77 "Keep only the ML-best segment in each chamber within a component"};

◆ m_maxEdgesPerNode

Gaudi::Property<unsigned int> MuonML::SegmentEdgeInferenceAlg::m_maxEdgesPerNode
private
Initial value:
{
this, "MaxEdgesPerNode", 2,
"Keep at most this many highest-score neighbours per segment when forming the ML path graph; zero keeps all"}

Definition at line 57 of file SegmentEdgeInferenceAlg.h.

57 {
58 this, "MaxEdgesPerNode", 2,
59 "Keep at most this many highest-score neighbours per segment when forming the ML path graph; zero keeps all"};

◆ m_minSegmentsPerComponent

Gaudi::Property<unsigned int> MuonML::SegmentEdgeInferenceAlg::m_minSegmentsPerComponent
private
Initial value:
{
this, "MinSegmentsPerComponent", 2,
"Discard ML components with fewer retained segments"}

Definition at line 78 of file SegmentEdgeInferenceAlg.h.

78 {
79 this, "MinSegmentsPerComponent", 2,
80 "Discard ML components with fewer retained segments"};

◆ m_pairGateDecorKey

SG::WriteDecorHandleKey<xAOD::MuonSegmentContainer> MuonML::SegmentEdgeInferenceAlg::m_pairGateDecorKey
private
Initial value:
{
this, "PairGateDecoration", "MuonSegmentsFromR4.mlTrackComponent"}

Per-segment payload consumed by MlMsTrackSeeder: [componentId, isSeedAnchor] Empty means that the segment was rejected by the ML preselection.

Definition at line 48 of file SegmentEdgeInferenceAlg.h.

48 {
49 this, "PairGateDecoration", "MuonSegmentsFromR4.mlTrackComponent"};

◆ m_pairGateThreshold

Gaudi::Property<float> MuonML::SegmentEdgeInferenceAlg::m_pairGateThreshold
private
Initial value:
{
this, "PairGateThreshold", 0.5f,
"Minimum edge probability used to form ML track components"}

Definition at line 54 of file SegmentEdgeInferenceAlg.h.

54 {
55 this, "PairGateThreshold", 0.5f,
56 "Minimum edge probability used to form ML track components"};

◆ m_recoverOrphanNodes

Gaudi::Property<bool> MuonML::SegmentEdgeInferenceAlg::m_recoverOrphanNodes
private
Initial value:
{
this, "RecoverOrphanNodes", true,
"For a thresholded node without a mutual top-K edge, retain its best one-sided top-K association"}

Definition at line 66 of file SegmentEdgeInferenceAlg.h.

66 {
67 this, "RecoverOrphanNodes", true,
68 "For a thresholded node without a mutual top-K edge, retain its best one-sided top-K association"};

◆ m_requireMutualTopKEdges

Gaudi::Property<bool> MuonML::SegmentEdgeInferenceAlg::m_requireMutualTopKEdges
private
Initial value:
{
this, "RequireMutualTopKEdges", true,
"Retain an ML association only when both endpoint segments rank it in their top MaxEdgesPerNode scores"}

Definition at line 63 of file SegmentEdgeInferenceAlg.h.

63 {
64 this, "RequireMutualTopKEdges", true,
65 "Retain an ML association only when both endpoint segments rank it in their top MaxEdgesPerNode scores"};

◆ m_seedAnchorsPerComponent

Gaudi::Property<unsigned int> MuonML::SegmentEdgeInferenceAlg::m_seedAnchorsPerComponent
private
Initial value:
{
this, "SeedAnchorsPerComponent", 0,
"Number of highest-score segments that may seed each ML component; zero keeps every retained segment"}

Definition at line 69 of file SegmentEdgeInferenceAlg.h.

69 {
70 this, "SeedAnchorsPerComponent", 0,
71 "Number of highest-score segments that may seed each ML component; zero keeps every retained segment"};

◆ m_segmentKey

SG::ReadHandleKey<xAOD::MuonSegmentContainer> MuonML::SegmentEdgeInferenceAlg::m_segmentKey {this, "SegmentKey", "MuonSegmentsFromR4"}
private

Definition at line 43 of file SegmentEdgeInferenceAlg.h.

43{this, "SegmentKey", "MuonSegmentsFromR4"};

◆ m_sumBkgEdgesPassed

std::atomic<std::size_t> MuonML::SegmentEdgeInferenceAlg::m_sumBkgEdgesPassed {0}
mutableprivate

Definition at line 114 of file SegmentEdgeInferenceAlg.h.

114{0};

◆ m_sumBkgEdgesTotal

std::atomic<std::size_t> MuonML::SegmentEdgeInferenceAlg::m_sumBkgEdgesTotal {0}
mutableprivate

Definition at line 113 of file SegmentEdgeInferenceAlg.h.

113{0};

◆ m_sumChamberSuppressedNodes

std::atomic<std::size_t> MuonML::SegmentEdgeInferenceAlg::m_sumChamberSuppressedNodes {0}
mutableprivate

Definition at line 95 of file SegmentEdgeInferenceAlg.h.

95{0};

◆ m_sumComponentsKept

std::atomic<std::size_t> MuonML::SegmentEdgeInferenceAlg::m_sumComponentsKept {0}
mutableprivate

Definition at line 98 of file SegmentEdgeInferenceAlg.h.

98{0};

◆ m_sumGraphNodes

std::atomic<std::size_t> MuonML::SegmentEdgeInferenceAlg::m_sumGraphNodes {0}
mutableprivate

Job-summed post-ONNX selection counters (see execute()).

Definition at line 88 of file SegmentEdgeInferenceAlg.h.

88{0};

◆ m_sumInputBkgSegments

std::atomic<std::size_t> MuonML::SegmentEdgeInferenceAlg::m_sumInputBkgSegments {0}
mutableprivate

Definition at line 106 of file SegmentEdgeInferenceAlg.h.

106{0};

◆ m_sumInputTruthSegments

std::atomic<std::size_t> MuonML::SegmentEdgeInferenceAlg::m_sumInputTruthSegments {0}
mutableprivate

Definition at line 105 of file SegmentEdgeInferenceAlg.h.

105{0};

◆ m_sumMutualTopKPairs

std::atomic<std::size_t> MuonML::SegmentEdgeInferenceAlg::m_sumMutualTopKPairs {0}
mutableprivate

Definition at line 90 of file SegmentEdgeInferenceAlg.h.

90{0};

◆ m_sumNodesRejectedByMinComponent

std::atomic<std::size_t> MuonML::SegmentEdgeInferenceAlg::m_sumNodesRejectedByMinComponent {0}
mutableprivate

Definition at line 96 of file SegmentEdgeInferenceAlg.h.

96{0};

◆ m_sumOneSidedTopKPairs

std::atomic<std::size_t> MuonML::SegmentEdgeInferenceAlg::m_sumOneSidedTopKPairs {0}
mutableprivate

Definition at line 91 of file SegmentEdgeInferenceAlg.h.

91{0};

◆ m_sumOnnxBkgSegments

std::atomic<std::size_t> MuonML::SegmentEdgeInferenceAlg::m_sumOnnxBkgSegments {0}
mutableprivate

Definition at line 108 of file SegmentEdgeInferenceAlg.h.

108{0};

◆ m_sumOnnxTruthSegments

std::atomic<std::size_t> MuonML::SegmentEdgeInferenceAlg::m_sumOnnxTruthSegments {0}
mutableprivate

Definition at line 107 of file SegmentEdgeInferenceAlg.h.

107{0};

◆ m_sumOrphanRecoveryPairs

std::atomic<std::size_t> MuonML::SegmentEdgeInferenceAlg::m_sumOrphanRecoveryPairs {0}
mutableprivate

Definition at line 92 of file SegmentEdgeInferenceAlg.h.

92{0};

◆ m_sumRejectedComponents

std::atomic<std::size_t> MuonML::SegmentEdgeInferenceAlg::m_sumRejectedComponents {0}
mutableprivate

Definition at line 97 of file SegmentEdgeInferenceAlg.h.

97{0};

◆ m_sumRetainedBkgSegments

std::atomic<std::size_t> MuonML::SegmentEdgeInferenceAlg::m_sumRetainedBkgSegments {0}
mutableprivate

Definition at line 110 of file SegmentEdgeInferenceAlg.h.

110{0};

◆ m_sumRetainedNodes

std::atomic<std::size_t> MuonML::SegmentEdgeInferenceAlg::m_sumRetainedNodes {0}
mutableprivate

Definition at line 94 of file SegmentEdgeInferenceAlg.h.

94{0};

◆ m_sumRetainedTruthSegments

std::atomic<std::size_t> MuonML::SegmentEdgeInferenceAlg::m_sumRetainedTruthSegments {0}
mutableprivate

Definition at line 109 of file SegmentEdgeInferenceAlg.h.

109{0};

◆ m_sumThresholdedNodes

std::atomic<std::size_t> MuonML::SegmentEdgeInferenceAlg::m_sumThresholdedNodes {0}
mutableprivate

Definition at line 89 of file SegmentEdgeInferenceAlg.h.

89{0};

◆ m_sumTopologyNodes

std::atomic<std::size_t> MuonML::SegmentEdgeInferenceAlg::m_sumTopologyNodes {0}
mutableprivate

Definition at line 93 of file SegmentEdgeInferenceAlg.h.

93{0};

◆ m_sumTrueEdgesPassed

std::atomic<std::size_t> MuonML::SegmentEdgeInferenceAlg::m_sumTrueEdgesPassed {0}
mutableprivate

Definition at line 112 of file SegmentEdgeInferenceAlg.h.

112{0};

◆ m_sumTrueEdgesTotal

std::atomic<std::size_t> MuonML::SegmentEdgeInferenceAlg::m_sumTrueEdgesTotal {0}
mutableprivate

Definition at line 111 of file SegmentEdgeInferenceAlg.h.

111{0};

◆ m_truthDiagEnabled

bool MuonML::SegmentEdgeInferenceAlg::m_truthDiagEnabled {false}
private

Whether truth-particle matching is available/enabled this job.

Definition at line 85 of file SegmentEdgeInferenceAlg.h.

85{false};

◆ m_useDegreeCappedComponents

Gaudi::Property<bool> MuonML::SegmentEdgeInferenceAlg::m_useDegreeCappedComponents
private
Initial value:
{
this, "UseDegreeCappedComponents", false,
"Use a global greedy degree cap instead of the mutual top-K path graph"}

Definition at line 60 of file SegmentEdgeInferenceAlg.h.

60 {
61 this, "UseDegreeCappedComponents", false,
62 "Use a global greedy degree cap instead of the mutual top-K path graph"};

◆ m_varHandleArraysDeclared

bool AthCommonDataStore< AthCommonMsg< Gaudi::Algorithm > >::m_varHandleArraysDeclared
privateinherited

Definition at line 387 of file AthCommonDataStore.h.

◆ m_vhka

std::vector<SG::VarHandleKeyArray*> AthCommonDataStore< AthCommonMsg< Gaudi::Algorithm > >::m_vhka
privateinherited

Definition at line 386 of file AthCommonDataStore.h.

◆ m_warnedMissingToolDiagnostics

std::atomic<bool> MuonML::SegmentEdgeInferenceAlg::m_warnedMissingToolDiagnostics {false}
mutableprivate

Definition at line 133 of file SegmentEdgeInferenceAlg.h.

133{false};

The documentation for this class was generated from the following files: