ATLAS Offline Software
Loading...
Searching...
No Matches
TgcL0FloatingCandidateBuilderTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
6
9#include "TgcL0FloatingData.h"
10#include "TgcL0FloatingPtLut.h"
11#include "TgcL0RdoDecoder.h"
15
16#include <sstream>
17#include <utility>
18#include <vector>
19
20namespace {
21
22struct CoincidenceQualityCounts {
23 std::size_t nThreeOfThree{0};
24 std::size_t nTwoOfThree{0};
25 std::size_t nOneOfThree{0};
26 std::size_t nTwoOfTwo{0};
27 std::size_t nOneOfTwo{0};
28};
29
30void countCoincidenceQuality(
32 CoincidenceQualityCounts& counts) {
33 if (coincidence.nominalLayers == 3U) {
34 if (coincidence.observedLayers == 3U) {
35 ++counts.nThreeOfThree;
36 } else if (coincidence.observedLayers == 2U) {
37 ++counts.nTwoOfThree;
38 } else if (coincidence.observedLayers == 1U) {
39 ++counts.nOneOfThree;
40 }
41 } else if (coincidence.nominalLayers == 2U) {
42 if (coincidence.observedLayers == 2U) {
43 ++counts.nTwoOfTwo;
44 } else if (coincidence.observedLayers == 1U) {
45 ++counts.nOneOfTwo;
46 }
47 }
48}
49
50} // namespace
51
52namespace L0Muon {
53
55 ATH_CHECK(m_idHelperSvc.retrieve());
56 ATH_CHECK(m_cablingKey.initialize());
57 ATH_CHECK(m_detectorManagerKey.initialize());
58
59 const std::string calibrationDirectory =
61 if (calibrationDirectory.empty()) {
62 ATH_MSG_ERROR("Could not resolve the GroupData development directory");
63 return StatusCode::FAILURE;
64 }
65 const std::string calibrationPath =
66 calibrationDirectory + "/" + m_ptCalibrationFile.value();
67 std::string error;
69 if (!m_ptLut) {
70 ATH_MSG_ERROR("Failed to load Floating-pT calibration: " << error);
71 return StatusCode::FAILURE;
72 }
73 ATH_MSG_INFO("Loaded Floating-pT calibration "
74 << m_ptLut->version() << " from " << calibrationPath
75 << " (eta bins=" << m_ptLut->etaBins()
76 << ", folded-phi bins=" << m_ptLut->phiBinsPerFold()
77 << ", knots=" << m_ptLut->knotCount()
78 << ", mode="
79 << (m_ptLut->isDevelopmentPayload() ? "development"
80 : "production")
81 << ")");
82 return StatusCode::SUCCESS;
83}
84
86 const TgcRdoContainer& rdos, TgcL0CandidateContainer& candidates,
87 const EventContext& ctx) const {
88 return build(rdos, candidates, nullptr, ctx);
89}
90
92 const TgcRdoContainer& rdos, TgcL0CandidateContainer& candidates,
93 TgcL0SegmentContainer* segments, const EventContext& ctx) const {
94
95 const Muon::TgcCablingMap* cabling{};
96 ATH_CHECK(SG::get(cabling, m_cablingKey, ctx));
97
100 if (!detectorManagerHandle.isValid()) {
101 ATH_MSG_ERROR("Failed to retrieve " << m_detectorManagerKey.fullKey());
102 return StatusCode::FAILURE;
103 }
104 const MuonGM::MuonDetectorManager* detectorManager =
105 detectorManagerHandle.cptr();
106
107 TgcL0Floating::HitGroups hitGroups;
109 const TgcL0Floating::RdoDecoder decoder;
110 ATH_CHECK(decoder.decode(rdos, *cabling, *m_idHelperSvc, *detectorManager,
111 hitGroups, statistics));
112
114 const TgcL0Floating::StationCoincidenceBuilder coincidenceBuilder;
115 ATH_CHECK(coincidenceBuilder.build(hitGroups, coincidences));
116
117
119 segmentConfig.maxPivotWireStripDeltaEta =
121 segmentConfig.maxPivotWireStripDeltaPhi =
123 segmentConfig.maxSegmentCombinationsPerGroup =
125 segmentConfig.maxCandidatesPerLocalBin =
127 TgcL0Floating::SegmentStatistics segmentStatistics;
128 const TgcL0Floating::SegmentReconstruction segmentReconstruction{
129 std::move(segmentConfig)};
130 ATH_CHECK(segmentReconstruction.build(coincidences, candidates,
131 segmentStatistics, segments));
132
134 const TgcL0Floating::OverlapClassification overlapClassification;
135 overlapClassification.classify(candidates, overlapStatistics);
136
137 std::size_t nValidPtEstimates = 0U;
138 for (TgcL0Candidate& candidate : candidates) {
139 const TgcL0FloatingPtEvaluation evaluation =
140 m_ptLut->evaluate(candidate.eta, candidate.phi, candidate.deltaTheta);
141 if (!evaluation.ptEstimateValid) continue;
142 candidate.preInnerCoincidencePt = evaluation.ptEstimateGeV;
143 candidate.preInnerCoincidenceThreshold = evaluation.thresholdCode;
144 candidate.charge = evaluation.estimatedCharge;
145 ++nValidPtEstimates;
146 }
147 const std::size_t nInvalidPtEstimates =
148 candidates.size() - nValidPtEstimates;
149 ATH_MSG_DEBUG("Floating-pT evaluation: valid=" << nValidPtEstimates
150 << ", invalid="
151 << nInvalidPtEstimates);
152
153 CoincidenceQualityCounts totalCounts;
154 CoincidenceQualityCounts m1WireCounts;
155 CoincidenceQualityCounts m1StripCounts;
156 CoincidenceQualityCounts m2WireCounts;
157 CoincidenceQualityCounts m2StripCounts;
158 CoincidenceQualityCounts m3WireCounts;
159 CoincidenceQualityCounts m3StripCounts;
160
161 for (const TgcL0Floating::StationCoincidence& coincidence : coincidences) {
162 countCoincidenceQuality(coincidence, totalCounts);
163
164 CoincidenceQualityCounts* stationCounts{nullptr};
165 if (coincidence.station == TgcL0Floating::Station::M1) {
166 stationCounts = coincidence.isStrip ? &m1StripCounts : &m1WireCounts;
167 } else if (coincidence.station == TgcL0Floating::Station::M2) {
168 stationCounts = coincidence.isStrip ? &m2StripCounts : &m2WireCounts;
169 } else if (coincidence.station == TgcL0Floating::Station::M3) {
170 stationCounts = coincidence.isStrip ? &m3StripCounts : &m3WireCounts;
171 }
172 if (stationCounts != nullptr) {
173 countCoincidenceQuality(coincidence, *stationCounts);
174 }
175 }
176
177 ATH_MSG_DEBUG("Decoded " << statistics.nHits << " TGC hits from "
178 << statistics.nRawData << " raw-data words in "
179 << hitGroups.size() << " groups: wire="
180 << statistics.nWireHits << ", strip="
181 << statistics.nStripHits << ", M1="
182 << statistics.nM1Hits << ", M2="
183 << statistics.nM2Hits << ", M3="
184 << statistics.nM3Hits << ", inner="
185 << statistics.nInnerHits << ", unknown="
186 << statistics.nUnknownStation
187 << ", mapping failures="
188 << statistics.nMappingFailures
189 << "; station coincidences="
190 << coincidences.size() << " (3/3="
191 << totalCounts.nThreeOfThree << ", 2/3="
192 << totalCounts.nTwoOfThree << ", 1/3="
193 << totalCounts.nOneOfThree << ", 2/2="
194 << totalCounts.nTwoOfTwo << ", 1/2="
195 << totalCounts.nOneOfTwo << ")");
196
197 ATH_MSG_DEBUG("Station coincidence breakdown: M1 wire=(3/3="
198 << m1WireCounts.nThreeOfThree << ", 2/3="
199 << m1WireCounts.nTwoOfThree << ", 1/3="
200 << m1WireCounts.nOneOfThree << "), M1 strip=(2/2="
201 << m1StripCounts.nTwoOfTwo << ", 1/2="
202 << m1StripCounts.nOneOfTwo << "), M2 wire=(2/2="
203 << m2WireCounts.nTwoOfTwo << ", 1/2="
204 << m2WireCounts.nOneOfTwo << "), M2 strip=(2/2="
205 << m2StripCounts.nTwoOfTwo << ", 1/2="
206 << m2StripCounts.nOneOfTwo << "), M3 wire=(2/2="
207 << m3WireCounts.nTwoOfTwo << ", 1/2="
208 << m3WireCounts.nOneOfTwo << "), M3 strip=(2/2="
209 << m3StripCounts.nTwoOfTwo << ", 1/2="
210 << m3StripCounts.nOneOfTwo << ")");
211
212 ATH_MSG_DEBUG("Segment reconstruction: wire="
213 << segmentStatistics.nWireSegments << ", strip="
214 << segmentStatistics.nStripSegments << ", candidates="
215 << candidates.size() << " (M1M2M3="
216 << segmentStatistics.nM1M2M3Candidates << ", M1M2="
217 << segmentStatistics.nM1M2Candidates << ", M1M3="
218 << segmentStatistics.nM1M3Candidates << ", M2M3="
219 << segmentStatistics.nM2M3Candidates
220 << ", M1only="
221 << segmentStatistics.nM1OnlyPositionCandidates
222 << ", M2only="
223 << segmentStatistics.nM2OnlyPositionCandidates
224 << ", M3only="
225 << segmentStatistics.nM3OnlyPositionCandidates
226 << "), projectionRejected="
227 << segmentStatistics.nRejectedProjectionCombinations
228 << ", projectionDuplicates="
229 << segmentStatistics.nDuplicateProjectionSegments
230 << ", projectionLimited="
231 << segmentStatistics.nLimitedProjectionSegments
232 << ", pairRejected=" << segmentStatistics.nRejectedPairs
233 << ", candidateDuplicates="
234 << segmentStatistics.nDuplicateCandidates
235 << ", localDuplicates="
236 << segmentStatistics.nLocalDuplicateCandidates
237 << ", candidateLimited="
238 << segmentStatistics.nLimitedCandidates);
239
240 ATH_MSG_DEBUG("Overlap classification: groups="
241 << overlapStatistics.nGroups << ", candidates="
242 << overlapStatistics.nCandidates << ", maxMultiplicity="
243 << overlapStatistics.maxMultiplicity);
244
245 for (std::size_t groupId = 1U;
246 groupId <= overlapStatistics.nGroups; ++groupId) {
247 std::vector<std::size_t> memberIndices;
248 for (std::size_t index = 0U; index < candidates.size(); ++index) {
249 if (candidates[index].overlapGroupId == groupId) {
250 memberIndices.emplace_back(index);
251 }
252 }
253 if (memberIndices.empty()) continue;
254 std::ostringstream members;
255 for (std::size_t position = 0U; position < memberIndices.size(); ++position) {
256 if (position != 0U) members << ",";
257 members << memberIndices[position];
258 }
259 ATH_MSG_DEBUG("Overlap group detail: groupId="
260 << groupId << ", multiplicity=" << memberIndices.size()
261 << ", members=[" << members.str() << "]");
262 }
263
264 for (std::size_t candidateIndex = 0; candidateIndex < candidates.size();
265 ++candidateIndex) {
266 const TgcL0Candidate& candidate = candidates[candidateIndex];
267 ATH_MSG_DEBUG("Reconstructed candidate: index="
268 << candidateIndex << ", bcTag=" << candidate.bcTag
269 << ", subdetectorId=" << candidate.subdetectorId
270 << ", triggerSector=" << candidate.sectorId
271 << ", readoutSector=" << candidate.readoutSector
272 << ", stationMask="
273 << static_cast<unsigned int>(candidate.stationMask)
274 << ", wireStationMask="
275 << static_cast<unsigned int>(candidate.wireStationMask)
276 << ", stripStationMask="
277 << static_cast<unsigned int>(candidate.stripStationMask)
278 << ", positionStationMask="
279 << static_cast<unsigned int>(
280 candidate.positionStationMask)
281 << ", eta=" << candidate.eta << ", phi="
282 << candidate.phi << ", deltaTheta="
283 << candidate.deltaTheta << ", deltaPhi="
284 << candidate.deltaPhi << ", preInnerCoincidencePt="
285 << candidate.preInnerCoincidencePt
286 << ", preInnerCoincidenceThreshold="
287 << static_cast<unsigned int>(
289 << ", charge=" << static_cast<int>(candidate.charge)
290 << ", wireQuality="
291 << static_cast<unsigned int>(candidate.wireQuality)
292 << ", wireQualities=("
293 << static_cast<unsigned int>(candidate.m1WireQuality)
294 << ","
295 << static_cast<unsigned int>(candidate.m2WireQuality)
296 << ","
297 << static_cast<unsigned int>(candidate.m3WireQuality)
298 << "), stripQualities=("
299 << static_cast<unsigned int>(candidate.m1StripQuality)
300 << ","
301 << static_cast<unsigned int>(candidate.m2StripQuality)
302 << ","
303 << static_cast<unsigned int>(candidate.m3StripQuality)
304 << "), chambers=(M1:" << candidate.m1StationEta << "/"
305 << candidate.m1StationPhi << ", M2:"
306 << candidate.m2StationEta << "/" << candidate.m2StationPhi
307 << ", M3:" << candidate.m3StationEta << "/"
308 << candidate.m3StationPhi << "), overlapGroupId="
309 << candidate.overlapGroupId << ", overlapMultiplicity="
310 << static_cast<unsigned int>(candidate.overlapMultiplicity)
311 << ", inChamberOverlap=" << candidate.inChamberOverlap);
312 }
313
314
315
316 return StatusCode::SUCCESS;
317}
318
319} // namespace L0Muon
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_DEBUG(x)
std::unique_ptr< const TgcL0FloatingPtLut > m_ptLut
StatusCode build(const TgcRdoContainer &rdos, TgcL0CandidateContainer &candidates, const EventContext &ctx) const override
Build candidates from TGC RDO data.
ServiceHandle< Muon::IMuonIdHelperSvc > m_idHelperSvc
SG::ReadCondHandleKey< MuonGM::MuonDetectorManager > m_detectorManagerKey
SG::ReadCondHandleKey< Muon::TgcCablingMap > m_cablingKey
static std::unique_ptr< TgcL0FloatingPtLut > loadAscii(const std::string &calibrationPath, std::string &error)
Load the human-readable calibration payload.
Tag nearby candidates from distinct chamber paths.
void classify(TgcL0CandidateContainer &candidates, OverlapClassificationStatistics &statistics) const
Reconstruct same-BC inter-station segments and transient candidates.
StatusCode build(const StationCoincidenceContainer &coincidences, TgcL0CandidateContainer &candidates, SegmentStatistics &statistics, TgcL0SegmentContainer *validationSegments=nullptr) const
Build station representative points from layer-level TGC hits.
StatusCode build(const HitGroups &hitGroups, StationCoincidenceContainer &coincidences) const
The MuonDetectorManager stores the transient representation of the Muon Spectrometer geometry and pro...
static std::string find_calib_directory(const std::string &logical_file_name)
const_pointer_type cptr()
std::map< HitGroupKey, HitContainer > HitGroups
std::vector< StationCoincidence > StationCoincidenceContainer
std::vector< TgcL0Segment > TgcL0SegmentContainer
std::vector< TgcL0Candidate > TgcL0CandidateContainer
Event-local candidate collection.
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
Definition index.py:1
Event-local candidate used by the TGC simulation tools.
bool inChamberOverlap
True when the candidate has a nearby partner from a different chamber path.
std::int16_t m1StationEta
Offline chamber identifiers retained for overlap diagnostics.
std::uint8_t m2StripQuality
std::uint8_t m1StripQuality
Station-coincidence qualities for strip projections.
float preInnerCoincidencePt
TGC pT estimate before Inner Coincidence, in GeV.
float deltaPhi
Signed azimuthal-angle residual, in radians.
std::uint16_t m1StationPhi
std::uint8_t positionStationMask
Alias of stationMask retained for explicit validation/debug use.
std::uint8_t m1WireQuality
Station-coincidence qualities for wire projections.
std::uint8_t preInnerCoincidenceThreshold
Highest pT-threshold index before Inner Coincidence.
std::uint16_t readoutSector
Run-3 detector/readout sector retained for diagnostics.
std::uint16_t bcTag
Bunch-crossing tag.
std::int8_t charge
Charge sign: -1, 0, or +1.
std::uint8_t stripStationMask
Stations used by the strip projection segment.
std::uint8_t m3StripQuality
std::uint8_t stationMask
Stations with a complete two-dimensional wire-and-strip position.
std::uint16_t m3StationPhi
std::uint16_t sectorId
Trigger Sector identifier.
std::uint16_t m2StationPhi
std::uint8_t wireStationMask
Stations used by the wire projection segment.
float eta
Pseudorapidity at the TGC pivot plane.
std::uint8_t overlapMultiplicity
Number of candidates in the classified overlap group.
float phi
Azimuth at the TGC pivot plane, in radians.
std::uint16_t subdetectorId
Subdetector identifier.
std::uint16_t overlapGroupId
Event-local overlap group. Zero means no classified overlap partner.
float deltaTheta
Signed polar-angle residual, in radians.
std::uint8_t wireQuality
Wire quality.
float ptEstimateGeV
Operational pT value, saturated to the downstream 8-bit range.
float maxPivotWireStripDeltaEta
Common-pivot wire-strip association.
std::size_t maxCandidatesPerLocalBin
Old Floating local eta-phi-pivot candidate-bin pruning.
std::size_t maxSegmentCombinationsPerGroup
Validated Floating working-set size per side/Trigger-Sector/BC chain.