ATLAS Offline Software
Loading...
Searching...
No Matches
SegmentDumperAlg.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
5#include "SegmentDumperAlg.h"
6
11#include "xAODMuonPrepData/UtilFunctions.h" // getTruthMatchedParticle
14
19
20#include <algorithm>
21#include <cmath>
22#include <limits>
23#include <iterator>
24#include <map>
25#include <set>
26#include <unordered_map>
27#include <vector>
28
29namespace {
30static const SG::ConstAccessor<int> g4TrackIdAcc{"MuonSim_G4TrkId"};
31
32constexpr int32_t noTruthLabel() { return -1; }
33constexpr int32_t noTruthSource() { return 0; }
34constexpr int32_t g4TruthSource() { return 2; }
35constexpr int32_t truthParticleSource() { return 3; }
36constexpr int32_t firstG4PseudoLabel() { return 2000000; }
37
38struct G4Key {
39 // type = 0: HepMC/genParticleLink id, type = 1: MuonSim_G4TrkId decoration.
40 int type{0};
41 int id{0};
42};
43
44bool operator<(const G4Key& a, const G4Key& b) {
45 if (a.type != b.type) return a.type < b.type;
46 return a.id < b.id;
47}
48
49using G4KeyCounts_t = std::map<G4Key, unsigned int>;
50using LabelSupport_t = std::map<int32_t, unsigned int>;
51
52struct SegmentTruthLabel {
53 int32_t id{noTruthLabel()};
54 int32_t source{noTruthSource()};
55 int32_t pdgId{-1};
56 float pt{std::numeric_limits<float>::quiet_NaN()};
57 float eta{std::numeric_limits<float>::quiet_NaN()};
58 float phi{std::numeric_limits<float>::quiet_NaN()};
59 uint8_t ambiguous{0};
60 float weight{0.f};
61 std::vector<int32_t> altIds{};
62};
63
64int simHitHepMcId(const xAOD::MuonSimHit& hit) {
65 const HepMcParticleLink& link = hit.genParticleLink();
66 if (link.isValid()) {
67 const auto genParticle = link.cptr();
68 if (genParticle) {
69 const int uid = HepMC::uniqueID(genParticle);
70 if (uid > 0) {
71 return uid;
72 }
73 }
74 }
75 return link.id() > 0 ? link.id() : 0;
76}
77
78G4KeyCounts_t collectG4Keys(const xAOD::MuonSegment& segment) {
79 G4KeyCounts_t g4Keys{};
80 for (const xAOD::MuonSimHit* hit : MuonR4::getMatchingSimHits(segment)) {
81 const xAOD::MuonSimHit& simHit{*hit};
82 if (std::abs(simHit.pdgId()) != 13) {
83 continue;
84 }
85
86 const int hepMcId = simHitHepMcId(simHit);
87 if (hepMcId > 0) {
88 ++g4Keys[G4Key{0, hepMcId}];
89 }
90
91 if (g4TrackIdAcc.isAvailable(simHit)) {
92 const int g4TrackId = g4TrackIdAcc(simHit);
93 if (g4TrackId > 0) {
94 ++g4Keys[G4Key{1, g4TrackId}];
95 }
96 }
97 }
98 return g4Keys;
99}
100
101SegmentTruthLabel labelFromSupport(const LabelSupport_t& support,
102 const unsigned int totalSupport,
103 const int32_t source) {
104 SegmentTruthLabel label{};
105 if (support.empty()) {
106 return label;
107 }
108
109 std::vector<std::pair<int32_t, unsigned int>> ranked{support.begin(), support.end()};
110 std::sort(ranked.begin(), ranked.end(), [](const auto& a, const auto& b) {
111 if (a.second != b.second) return a.second > b.second;
112 return a.first < b.first;
113 });
114
115 label.id = ranked.front().first;
116 label.source = source;
117 label.pdgId = 13;
118 label.weight = totalSupport > 0
119 ? static_cast<float>(ranked.front().second) / static_cast<float>(totalSupport)
120 : 0.f;
121 label.ambiguous = ranked.size() > 1 && ranked[0].second == ranked[1].second ? 1 : 0;
122 label.altIds.reserve(ranked.size() > 0 ? ranked.size() - 1 : 0);
123 std::transform(std::next(ranked.begin()), ranked.end(), std::back_inserter(label.altIds),
124 [](const auto& entry) { return entry.first; });
125 return label;
126}
127
128struct LocalSegSorter {
129 bool operator()(const xAOD::MuonSegment* a,
130 const xAOD::MuonSegment* b) const {
131 if (a == b) return false;
132 if (a->chamberIndex() != b->chamberIndex())
133 return a->chamberIndex() < b->chamberIndex();
134 if (a->sector() != b->sector())
135 return a->sector() < b->sector();
136 if (a->etaIndex() != b->etaIndex())
137 return a->etaIndex() < b->etaIndex();
138 using namespace MuonR4::SegmentFit;
139 auto la = localSegmentPars(*a);
140 auto lb = localSegmentPars(*b);
141 return la < lb;
142 }
143};
144} // namespace
145
146namespace MuonR4 {
147
149 ATH_MSG_INFO("Initializing SegmentDumperAlg (MC)");
150
151 ATH_CHECK(m_spacePointKeys.initialize());
152 ATH_CHECK(m_segmentKeys.initialize());
153
154 // Truth decoration on MC
155 m_truthDecorKeys.emplace_back(m_segmentKeys, "truthParticleLink");
156 ATH_CHECK(m_truthDecorKeys.initialize());
157
158 ATH_CHECK(m_geoCtxKey.initialize());
159 m_tree.addBranch(std::make_shared<MuonVal::EventHashBranch>(m_tree.tree()));
160 ATH_CHECK(m_tree.init(this));
161 return StatusCode::SUCCESS;
162}
163
165 ATH_CHECK(m_tree.write());
166 return StatusCode::SUCCESS;
167}
168
169StatusCode SegmentDumperAlg::execute(const EventContext& ctx) {
170
171 using SegmentsPerBucket_t =
172 std::unordered_map<const SpacePointBucket*,
173 std::set<const xAOD::MuonSegment*, LocalSegSorter>>;
174
175 const ActsTrk::GeometryContext* gctx{nullptr};
176 ATH_CHECK(SG::get(gctx, m_geoCtxKey, ctx));
177
178 const xAOD::MuonSegmentContainer* segContainer{nullptr};
179 ATH_CHECK(SG::get(segContainer, m_segmentKeys, ctx));
180
181 std::map<const xAOD::MuonSegment*, const xAOD::TruthParticle*> directTruth{};
182 std::map<const xAOD::MuonSegment*, G4KeyCounts_t> segmentG4Keys{};
183 std::map<G4Key, unsigned int> g4SegmentMultiplicity{};
184 std::map<G4Key, LabelSupport_t> g4TruthSupport{};
185 std::map<G4Key, int32_t> g4PseudoLabels{};
186 int32_t nextG4PseudoLabel = firstG4PseudoLabel();
187
188 for (const xAOD::MuonSegment* seg : *segContainer) {
190 directTruth.emplace(seg, tp);
191
192 if (!m_includeG4TrackTruth.value()) {
193 continue;
194 }
195
196 G4KeyCounts_t g4Keys = collectG4Keys(*seg);
197 for (const auto& keyCount : g4Keys) {
198 ++g4SegmentMultiplicity[keyCount.first];
199 }
200 if (tp) {
201 const int32_t truthIdx = static_cast<int32_t>(tp->index());
202 for (const auto& [key, count] : g4Keys) {
203 g4TruthSupport[key][truthIdx] += count;
204 }
205 }
206 segmentG4Keys.emplace(seg, std::move(g4Keys));
207 }
208
209 auto hasEnoughG4SegmentSupport = [&](const G4Key& key) {
210 const unsigned int minSegments = m_minG4TrackTruthSegments.value();
211 if (minSegments <= 1) {
212 return true;
213 }
214 const auto multItr = g4SegmentMultiplicity.find(key);
215 return multItr != g4SegmentMultiplicity.end() && multItr->second >= minSegments;
216 };
217
218 auto g4PseudoLabel = [&](const G4Key& key) {
219 auto [itr, inserted] = g4PseudoLabels.try_emplace(key, nextG4PseudoLabel);
220 if (inserted) {
221 ++nextG4PseudoLabel;
222 }
223 return itr->second;
224 };
225
226 auto g4Label = [&](const xAOD::MuonSegment& segment) {
227 SegmentTruthLabel label{};
228 const auto keyItr = segmentG4Keys.find(&segment);
229 if (keyItr == segmentG4Keys.end() || keyItr->second.empty()) {
230 return label;
231 }
232
233 const G4KeyCounts_t& g4Keys = keyItr->second;
234 LabelSupport_t propagatedSupport{};
235 unsigned int totalPropagatedSupport{0};
236 for (const auto& [key, count] : g4Keys) {
237 if (!hasEnoughG4SegmentSupport(key)) {
238 continue;
239 }
240 const auto supportItr = g4TruthSupport.find(key);
241 if (supportItr == g4TruthSupport.end()) {
242 continue;
243 }
244 for (const auto& [truthIdx, truthCount] : supportItr->second) {
245 const unsigned int support = std::max(count, truthCount);
246 propagatedSupport[truthIdx] += support;
247 totalPropagatedSupport += support;
248 }
249 }
250 if (!propagatedSupport.empty()) {
251 return labelFromSupport(propagatedSupport, totalPropagatedSupport, g4TruthSource());
252 }
253
254 G4KeyCounts_t eligibleG4Keys{};
255 for (const auto& [key, count] : g4Keys) {
256 if (hasEnoughG4SegmentSupport(key)) {
257 eligibleG4Keys[key] = count;
258 }
259 }
260 const auto bestKey = std::max_element(eligibleG4Keys.begin(), eligibleG4Keys.end(),
261 [](const auto& a, const auto& b) {
262 if (a.second != b.second) return a.second < b.second;
263 return b.first < a.first;
264 });
265 if (bestKey == eligibleG4Keys.end()) {
266 return label;
267 }
268
269 unsigned int totalKeys{0};
270 for (const auto& [key, count] : eligibleG4Keys) {
271 totalKeys += count;
272 }
273
274 label.id = g4PseudoLabel(bestKey->first);
275 label.source = g4TruthSource();
276 label.pdgId = 13;
277 label.weight = totalKeys > 0
278 ? static_cast<float>(bestKey->second) / static_cast<float>(totalKeys)
279 : 0.f;
280 label.ambiguous = eligibleG4Keys.size() > 1 ? 1 : 0;
281 for (const auto& keyCount : eligibleG4Keys) {
282 const G4Key& key = keyCount.first;
283 if (key.type == bestKey->first.type && key.id == bestKey->first.id) {
284 continue;
285 }
286 label.altIds.push_back(g4PseudoLabel(key));
287 }
288 return label;
289 };
290
291 for (unsigned iKey = 0; iKey < m_spacePointKeys.size(); ++iKey) {
292 const auto& spKey = m_spacePointKeys[iKey];
293
294 const SpacePointContainer* spContainer{nullptr};
295 ATH_CHECK(SG::get(spContainer, spKey, ctx));
296
297 SegmentsPerBucket_t segPerBucket{};
298 if (segContainer) {
299 for (const xAOD::MuonSegment* seg : *segContainer) {
300 const auto* detSeg = MuonR4::detailedSegment(*seg);
301 segPerBucket[detSeg->parent()->parentBucket()].insert(seg);
302 }
303 }
304
305 for (const SpacePointBucket* bucket : *spContainer) {
306 m_bucket_spacePoints = static_cast<uint16_t>(bucket->size());
307 m_bucket_chamberIdx = static_cast<uint8_t>(bucket->msSector()->chamberIndex());
308 m_bucket_sector = static_cast<uint8_t>(bucket->msSector()->sector());
310
311 const auto it = segPerBucket.find(bucket);
312 m_bucket_segments = (it != segPerBucket.end()) ? static_cast<uint16_t>(it->second.size()) : 0;
313 // Flattened ragged array for alternative truth labels. The offsets vector
314 // has length nSegments+1 for each dumped bucket.
315 m_segmentTruthAltPartOffsets.push_back(0);
316 if (it != segPerBucket.end()) {
317 for (const xAOD::MuonSegment* seg : it->second) {
318 // Reco-level outputs (always aligned to segments)
319 m_segmentPos.push_back(seg->position());
320 m_segmentDir.push_back(seg->direction());
321 m_segment_chiSquared.push_back(seg->chiSquared());
322 m_segment_numberDoF.push_back(seg->numberDoF());
323
324 // --- Reco eta/phi (GLOBAL): direction is already in global coordinates
325 const Amg::Vector3D& globDir = seg->direction();
326 const float segEta = static_cast<float>(globDir.eta());
327 const float segPhi = static_cast<float>(globDir.phi());
328 m_segmentRecoEta.push_back(segEta);
329 m_segmentRecoPhi.push_back(segPhi);
330
331 SegmentTruthLabel label{};
332 const auto truthItr = directTruth.find(seg);
333 const xAOD::TruthParticle* tp =
334 truthItr != directTruth.end() ? truthItr->second : getTruthMatchedParticle(*seg);
335 if (tp) {
336 label.id = static_cast<int32_t>(tp->index());
337 label.source = truthParticleSource();
338 label.pdgId = static_cast<int32_t>(tp->pdgId());
339 label.pt = static_cast<float>(tp->pt());
340 label.eta = static_cast<float>(tp->eta());
341 label.phi = static_cast<float>(tp->phi());
342 label.weight = 1.f;
343 } else if (m_includeG4TrackTruth.value()) {
344 label = g4Label(*seg);
345 if (label.id >= 0) {
346 label.eta = segEta;
347 label.phi = segPhi;
348 }
349 }
350
351 m_segmentHasTruth.push_back(label.id >= 0 ? 1 : 0);
352 m_segmentTruthIdx.push_back(label.id);
353 m_segmentTruthSource.push_back(label.source);
354 m_segmentTruthAmbiguous.push_back(label.ambiguous);
355 m_segmentTruthLabelWeight.push_back(label.weight);
356 for (const int32_t altId : label.altIds) {
357 m_segmentTruthAltParts.push_back(altId);
358 }
360 static_cast<int32_t>(m_segmentTruthAltParts.size()));
361 m_segmentTruthPDGId.push_back(label.pdgId);
362 m_segmentTruthPt.push_back(label.pt);
363 m_segmentTruthEta.push_back(label.eta);
364 m_segmentTruthPhi.push_back(label.phi);
365 }
366 }
367
368 if (!m_tree.fill(ctx)) {
369 ATH_MSG_ERROR("Failed to fill output tree");
370 return StatusCode::FAILURE;
371 }
372 }
373 }
374
375 return StatusCode::SUCCESS;
376}
377
380 std::vector<unsigned int> uniqueLayers;
381 uniqueLayers.reserve(bucket.size());
382
383 for (const SpacePointBucket::value_type& sp : bucket) {
384 const unsigned layNum = sorter.sectorLayerNum(*sp);
385 if (std::find(uniqueLayers.begin(), uniqueLayers.end(), layNum) == uniqueLayers.end()) {
386 uniqueLayers.push_back(layNum);
387 }
388 }
389 return static_cast<uint16_t>(uniqueLayers.size());
390}
391
392} // namespace MuonR4
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x,...)
#define ATH_MSG_INFO(x,...)
bool operator<(const DataVector< T > &a, const DataVector< T > &b)
Vector ordering relation.
ATLAS-specific HepMC functions.
bool hit(const Container &ids, int pdgId)
static Double_t sp
static Double_t a
void operator()(T1)
Handle class for reading from StoreGate.
MuonVal::ThreeVectorBranch m_segmentPos
MuonVal::VectorBranch< int32_t > & m_segmentTruthAltPartOffsets
ActsTrk::GeoContextReadKey_t m_geoCtxKey
MuonVal::VectorBranch< float > & m_segment_numberDoF
SG::ReadDecorHandleKeyArray< xAOD::MuonSegmentContainer > m_truthDecorKeys
Truth decoration (MC).
MuonVal::VectorBranch< float > & m_segmentTruthPhi
MuonVal::VectorBranch< float > & m_segmentTruthEta
SG::ReadHandleKeyArray< SpacePointContainer > m_spacePointKeys
Inputs.
SG::ReadHandleKey< xAOD::MuonSegmentContainer > m_segmentKeys
MuonVal::VectorBranch< float > & m_segmentTruthPt
MuonVal::VectorBranch< int32_t > & m_segmentTruthSource
MuonVal::ScalarBranch< uint16_t > & m_bucket_spacePoints
StatusCode execute(const EventContext &ctx) override final
Execute method.
MuonVal::MuonTesterTree m_tree
Output tree.
MuonVal::VectorBranch< float > & m_segmentRecoEta
MuonVal::ScalarBranch< uint16_t > & m_bucket_segments
MuonVal::ScalarBranch< uint8_t > & m_bucket_chamberIdx
MuonVal::VectorBranch< int32_t > & m_segmentTruthPDGId
uint16_t countLayersInBucket(const SpacePointBucket &bucket) const
MuonVal::ScalarBranch< uint8_t > & m_bucket_sector
MuonVal::VectorBranch< float > & m_segmentRecoPhi
Gaudi::Property< bool > m_includeG4TrackTruth
Allow the dumper to label otherwise-unmatched reco segments from sim-hit G4/HepMC ids.
StatusCode finalize() override final
MuonVal::VectorBranch< int32_t > & m_segmentTruthAltParts
MuonVal::VectorBranch< uint8_t > & m_segmentHasTruth
MuonVal::VectorBranch< float > & m_segmentTruthLabelWeight
StatusCode initialize() override final
MuonVal::VectorBranch< float > & m_segment_chiSquared
MuonVal::VectorBranch< uint8_t > & m_segmentTruthAmbiguous
MuonVal::VectorBranch< int32_t > & m_segmentTruthIdx
MuonVal::ThreeVectorBranch m_segmentDir
Gaudi::Property< unsigned int > m_minG4TrackTruthSegments
Minimum number of reco segments that must share the same G4/HepMC id.
MuonVal::ScalarBranch< uint16_t > & m_bucket_layers
: The muon space point bucket represents a collection of points that will bre processed together in t...
The SpacePointPerLayerSorter sort two given space points by their layer Identifier.
Helper class to provide constant type-safe access to aux data.
bool isAvailable(const ELT &e) const
Test to see if this variable exists in the store.
float numberDoF() const
Returns the numberDoF.
Amg::Vector3D direction() const
Returns the direction as Amg::Vector.
float chiSquared() const
Amg::Vector3D position() const
Returns the position as Amg::Vector.
int pdgId() const
Returns the pdgID of the traversing particle.
int pdgId() const
PDG ID code.
virtual double pt() const override final
The transverse momentum ( ) of the particle.
virtual double eta() const override final
The pseudorapidity ( ) of the particle.
virtual double phi() const override final
The azimuthal angle ( ) of the particle.
int lb
Definition globals.cxx:23
int count(std::string s, const std::string &regx)
count how many occurances of a regx are in a string
Definition hcg.cxx:148
std::string label(const std::string &format, int i)
Definition label.h:19
Eigen::Matrix< double, 3, 1 > Vector3D
int uniqueID(const T &p)
Parameters localSegmentPars(const xAOD::MuonSegment &seg)
Returns the localSegPars decoration from a xAODMuon::Segment.
This header ties the generic definitions in this package.
const xAOD::TruthParticle * getTruthMatchedParticle(const xAOD::MuonSegment &segment)
Returns the particle truth-matched to the segment.
std::unordered_set< const xAOD::MuonSimHit * > getMatchingSimHits(const xAOD::MuonSegment &segment)
: Returns all sim hits matched to a xAOD::MuonSegment
DataVector< SpacePointBucket > SpacePointContainer
Abrivation of the space point container type.
const Segment * detailedSegment(const xAOD::MuonSegment &seg)
Helper function to navigate from the xAOD::MuonSegment to the MuonR4::Segment.
pointer & link(pointer p) const
Return a reference to the link for an element.
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.
MuonSegmentContainer_v1 MuonSegmentContainer
Definition of the current "MuonSegment container version".
MuonSimHit_v1 MuonSimHit
Defined the version of the MuonSimHit.
Definition MuonSimHit.h:12
TruthParticle_v1 TruthParticle
Typedef to implementation.
MuonSegment_v1 MuonSegment
Reference the current persistent version: