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 ptCalibrationPath =
66 calibrationDirectory + "/" + m_ptCalibrationFile.value();
67 std::string error;
68 m_ptLut = TgcL0FloatingPtLut::loadAscii(ptCalibrationPath, 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 " << ptCalibrationPath
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
83 const std::string goodMagMapPath =
84 calibrationDirectory + "/" + m_goodMagMapFile.value();
85 error.clear();
87 if (!m_goodMagMap) {
88 ATH_MSG_ERROR("Failed to load GoodMag map: " << error);
89 return StatusCode::FAILURE;
90 }
91 ATH_MSG_INFO("Loaded GoodMag map "
92 << m_goodMagMap->version() << " from " << goodMagMapPath
93 << " (eta bins=" << m_goodMagMap->etaBins()
94 << ", folded-phi bins=" << m_goodMagMap->phiBinsPerFold()
95 << ", poor bins=" << m_goodMagMap->poorBinCount() << ")");
96 return StatusCode::SUCCESS;
97}
98
100 const TgcRdoContainer& rdos, TgcL0CandidateContainer& candidates,
101 const EventContext& ctx) const {
102 return build(rdos, candidates, nullptr, ctx);
103}
104
106 const TgcRdoContainer& rdos, TgcL0CandidateContainer& candidates,
107 TgcL0SegmentContainer* segments, const EventContext& ctx) const {
108
109 const Muon::TgcCablingMap* cabling{};
110 ATH_CHECK(SG::get(cabling, m_cablingKey, ctx));
111
114 if (!detectorManagerHandle.isValid()) {
115 ATH_MSG_ERROR("Failed to retrieve " << m_detectorManagerKey.fullKey());
116 return StatusCode::FAILURE;
117 }
118 const MuonGM::MuonDetectorManager* detectorManager =
119 detectorManagerHandle.cptr();
120
121 TgcL0Floating::HitGroups hitGroups;
123 const TgcL0Floating::RdoDecoder decoder;
124 ATH_CHECK(decoder.decode(rdos, *cabling, *m_idHelperSvc, *detectorManager,
125 hitGroups, statistics));
126
128 const TgcL0Floating::StationCoincidenceBuilder coincidenceBuilder;
129 ATH_CHECK(coincidenceBuilder.build(hitGroups, coincidences));
130
131
133 segmentConfig.maxPivotWireStripDeltaEta =
135 segmentConfig.maxPivotWireStripDeltaPhi =
137 segmentConfig.maxSegmentCombinationsPerGroup =
139 segmentConfig.maxCandidatesPerLocalBin =
141 TgcL0Floating::SegmentStatistics segmentStatistics;
142 const TgcL0Floating::SegmentReconstruction segmentReconstruction{
143 std::move(segmentConfig)};
144 ATH_CHECK(segmentReconstruction.build(coincidences, candidates,
145 segmentStatistics, segments));
146
148 const TgcL0Floating::OverlapClassification overlapClassification;
149 overlapClassification.classify(candidates, overlapStatistics);
150
151 std::size_t nValidPtEstimates = 0U;
152 for (TgcL0Candidate& candidate : candidates) {
153 const TgcL0FloatingPtEvaluation evaluation =
154 m_ptLut->evaluate(candidate.eta, candidate.phi, candidate.deltaTheta);
155 candidate.goodMagneticField = m_goodMagMap->isGood(
156 candidate.eta, candidate.phi, m_ptLut->absEtaMin(),
157 m_ptLut->absEtaMax());
158 if (!evaluation.ptEstimateValid) continue;
159 candidate.preInnerCoincidencePt = evaluation.ptEstimateGeV;
160 candidate.preInnerCoincidenceThreshold = evaluation.thresholdCode;
161 candidate.charge = evaluation.estimatedCharge;
162 ++nValidPtEstimates;
163 }
164 const std::size_t nInvalidPtEstimates =
165 candidates.size() - nValidPtEstimates;
166 ATH_MSG_DEBUG("Floating-pT evaluation: valid=" << nValidPtEstimates
167 << ", invalid="
168 << nInvalidPtEstimates);
169
170 CoincidenceQualityCounts totalCounts;
171 CoincidenceQualityCounts m1WireCounts;
172 CoincidenceQualityCounts m1StripCounts;
173 CoincidenceQualityCounts m2WireCounts;
174 CoincidenceQualityCounts m2StripCounts;
175 CoincidenceQualityCounts m3WireCounts;
176 CoincidenceQualityCounts m3StripCounts;
177
178 for (const TgcL0Floating::StationCoincidence& coincidence : coincidences) {
179 countCoincidenceQuality(coincidence, totalCounts);
180
181 CoincidenceQualityCounts* stationCounts{nullptr};
182 if (coincidence.station == TgcL0Floating::Station::M1) {
183 stationCounts = coincidence.isStrip ? &m1StripCounts : &m1WireCounts;
184 } else if (coincidence.station == TgcL0Floating::Station::M2) {
185 stationCounts = coincidence.isStrip ? &m2StripCounts : &m2WireCounts;
186 } else if (coincidence.station == TgcL0Floating::Station::M3) {
187 stationCounts = coincidence.isStrip ? &m3StripCounts : &m3WireCounts;
188 }
189 if (stationCounts != nullptr) {
190 countCoincidenceQuality(coincidence, *stationCounts);
191 }
192 }
193
194 ATH_MSG_DEBUG("Decoded " << statistics.nHits << " TGC hits from "
195 << statistics.nRawData << " raw-data words in "
196 << hitGroups.size() << " groups: wire="
197 << statistics.nWireHits << ", strip="
198 << statistics.nStripHits << ", M1="
199 << statistics.nM1Hits << ", M2="
200 << statistics.nM2Hits << ", M3="
201 << statistics.nM3Hits << ", inner="
202 << statistics.nInnerHits << ", unknown="
203 << statistics.nUnknownStation
204 << ", mapping failures="
205 << statistics.nMappingFailures
206 << "; station coincidences="
207 << coincidences.size() << " (3/3="
208 << totalCounts.nThreeOfThree << ", 2/3="
209 << totalCounts.nTwoOfThree << ", 1/3="
210 << totalCounts.nOneOfThree << ", 2/2="
211 << totalCounts.nTwoOfTwo << ", 1/2="
212 << totalCounts.nOneOfTwo << ")");
213
214 ATH_MSG_DEBUG("Station coincidence breakdown: M1 wire=(3/3="
215 << m1WireCounts.nThreeOfThree << ", 2/3="
216 << m1WireCounts.nTwoOfThree << ", 1/3="
217 << m1WireCounts.nOneOfThree << "), M1 strip=(2/2="
218 << m1StripCounts.nTwoOfTwo << ", 1/2="
219 << m1StripCounts.nOneOfTwo << "), M2 wire=(2/2="
220 << m2WireCounts.nTwoOfTwo << ", 1/2="
221 << m2WireCounts.nOneOfTwo << "), M2 strip=(2/2="
222 << m2StripCounts.nTwoOfTwo << ", 1/2="
223 << m2StripCounts.nOneOfTwo << "), M3 wire=(2/2="
224 << m3WireCounts.nTwoOfTwo << ", 1/2="
225 << m3WireCounts.nOneOfTwo << "), M3 strip=(2/2="
226 << m3StripCounts.nTwoOfTwo << ", 1/2="
227 << m3StripCounts.nOneOfTwo << ")");
228
229 ATH_MSG_DEBUG("Segment reconstruction: wire="
230 << segmentStatistics.nWireSegments << ", strip="
231 << segmentStatistics.nStripSegments << ", candidates="
232 << candidates.size() << " (M1M2M3="
233 << segmentStatistics.nM1M2M3Candidates << ", M1M2="
234 << segmentStatistics.nM1M2Candidates << ", M1M3="
235 << segmentStatistics.nM1M3Candidates << ", M2M3="
236 << segmentStatistics.nM2M3Candidates
237 << ", M1only="
238 << segmentStatistics.nM1OnlyPositionCandidates
239 << ", M2only="
240 << segmentStatistics.nM2OnlyPositionCandidates
241 << ", M3only="
242 << segmentStatistics.nM3OnlyPositionCandidates
243 << "), projectionRejected="
244 << segmentStatistics.nRejectedProjectionCombinations
245 << ", projectionDuplicates="
246 << segmentStatistics.nDuplicateProjectionSegments
247 << ", projectionLimited="
248 << segmentStatistics.nLimitedProjectionSegments
249 << ", pairRejected=" << segmentStatistics.nRejectedPairs
250 << ", candidateDuplicates="
251 << segmentStatistics.nDuplicateCandidates
252 << ", localDuplicates="
253 << segmentStatistics.nLocalDuplicateCandidates
254 << ", candidateLimited="
255 << segmentStatistics.nLimitedCandidates);
256
257 ATH_MSG_DEBUG("Overlap classification: groups="
258 << overlapStatistics.nGroups << ", candidates="
259 << overlapStatistics.nCandidates << ", maxMultiplicity="
260 << overlapStatistics.maxMultiplicity);
261
262 for (std::size_t groupId = 1U;
263 groupId <= overlapStatistics.nGroups; ++groupId) {
264 std::vector<std::size_t> memberIndices;
265 for (std::size_t index = 0U; index < candidates.size(); ++index) {
266 if (candidates[index].overlapGroupId == groupId) {
267 memberIndices.emplace_back(index);
268 }
269 }
270 if (memberIndices.empty()) continue;
271 std::ostringstream members;
272 for (std::size_t position = 0U; position < memberIndices.size(); ++position) {
273 if (position != 0U) members << ",";
274 members << memberIndices[position];
275 }
276 ATH_MSG_DEBUG("Overlap group detail: groupId="
277 << groupId << ", multiplicity=" << memberIndices.size()
278 << ", members=[" << members.str() << "]");
279 }
280
281 for (std::size_t candidateIndex = 0; candidateIndex < candidates.size();
282 ++candidateIndex) {
283 const TgcL0Candidate& candidate = candidates[candidateIndex];
284 ATH_MSG_DEBUG("Reconstructed candidate: index="
285 << candidateIndex << ", bcTag=" << candidate.bcTag
286 << ", subdetectorId=" << candidate.subdetectorId
287 << ", triggerSector=" << candidate.sectorId
288 << ", readoutSector=" << candidate.readoutSector
289 << ", stationMask="
290 << static_cast<unsigned int>(candidate.stationMask)
291 << ", wireStationMask="
292 << static_cast<unsigned int>(candidate.wireStationMask)
293 << ", stripStationMask="
294 << static_cast<unsigned int>(candidate.stripStationMask)
295 << ", positionStationMask="
296 << static_cast<unsigned int>(
297 candidate.positionStationMask)
298 << ", eta=" << candidate.eta << ", phi="
299 << candidate.phi << ", deltaTheta="
300 << candidate.deltaTheta << ", deltaPhi="
301 << candidate.deltaPhi << ", preInnerCoincidencePt="
302 << candidate.preInnerCoincidencePt
303 << ", preInnerCoincidenceThreshold="
304 << static_cast<unsigned int>(
306 << ", charge=" << static_cast<int>(candidate.charge)
307 << ", goodMagneticField="
308 << candidate.goodMagneticField
309 << ", wireQuality="
310 << static_cast<unsigned int>(candidate.wireQuality)
311 << ", wireQualities=("
312 << static_cast<unsigned int>(candidate.m1WireQuality)
313 << ","
314 << static_cast<unsigned int>(candidate.m2WireQuality)
315 << ","
316 << static_cast<unsigned int>(candidate.m3WireQuality)
317 << "), stripQualities=("
318 << static_cast<unsigned int>(candidate.m1StripQuality)
319 << ","
320 << static_cast<unsigned int>(candidate.m2StripQuality)
321 << ","
322 << static_cast<unsigned int>(candidate.m3StripQuality)
323 << "), chambers=(M1:" << candidate.m1StationEta << "/"
324 << candidate.m1StationPhi << ", M2:"
325 << candidate.m2StationEta << "/" << candidate.m2StationPhi
326 << ", M3:" << candidate.m3StationEta << "/"
327 << candidate.m3StationPhi << "), overlapGroupId="
328 << candidate.overlapGroupId << ", overlapMultiplicity="
329 << static_cast<unsigned int>(candidate.overlapMultiplicity)
330 << ", inChamberOverlap=" << candidate.inChamberOverlap);
331 }
332
333
334
335 return StatusCode::SUCCESS;
336}
337
338} // namespace L0Muon
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_DEBUG(x,...)
#define ATH_MSG_ERROR(x,...)
#define ATH_MSG_INFO(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
std::unique_ptr< const TgcL0GoodMagMap > m_goodMagMap
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
static std::unique_ptr< TgcL0GoodMagMap > loadAscii(const std::string &calibrationPath, std::string &error)
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.
bool goodMagneticField
GoodMag flag.
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.