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