ATLAS Offline Software
Loading...
Searching...
No Matches
TgcL0SegmentReconstruction.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
8
9#include <algorithm>
10#include <array>
11#include <bit>
12#include <cmath>
13#include <cstdint>
14#include <map>
15#include <numbers>
16#include <set>
17#include <tuple>
18#include <utility>
19#include <vector>
20
21namespace {
22
28using OutputSegment = L0Muon::TgcL0Segment;
29using OutputSegments = L0Muon::TgcL0SegmentContainer;
30using Projection = L0Muon::TgcL0SegmentProjection;
31
32constexpr std::uint8_t stationBit(const Station station) {
33 if (station == Station::M1) return 0x1U;
34 if (station == Station::M2) return 0x2U;
35 if (station == Station::M3) return 0x4U;
36 return 0U;
37}
38
39constexpr std::size_t stationIndex(const Station station) {
40 if (station == Station::M1) return 0U;
41 if (station == Station::M2) return 1U;
42 return 2U;
43}
44
45float measuringCoordinate(const Coincidence& point) {
46 return point.isStrip ? point.phi : point.eta;
47}
48
49float coordinateDifference(const bool isStrip, const float lhs,
50 const float rhs) {
51 if (isStrip) {
52 return static_cast<float>(xAOD::P4Helpers::deltaPhi(lhs, rhs));
53 }
54 return lhs - rhs;
55}
56
57float physicalDeltaTheta(const Coincidence& inner,
58 const Coincidence& outer) {
59 const float deltaR = outer.r - inner.r;
60 const float deltaZ = outer.z - inner.z;
61 if (deltaR == 0.F && deltaZ == 0.F) return 0.F;
62 const float segmentTheta = std::atan2(deltaR, deltaZ);
63 const float pivotTheta = std::atan2(outer.r, outer.z);
64 return segmentTheta - pivotTheta;
65}
66
67struct ProjectionSegment {
68 std::array<const Coincidence*, 3> points{};
69 bool isStrip{false};
70 std::uint8_t stationMask{0};
71 std::uint8_t summedQuality{0};
72 std::uint8_t nStations{0};
73 float eta{0.F};
74 float phi{0.F};
76 float residual{0.F};
78 float outputResidual{0.F};
79 float consistency{0.F};
80 std::uint16_t pivotChannel{0};
81};
82
83
84void appendValidationSegments(const GroupKey& groupKey,
85 const std::vector<ProjectionSegment>& segments,
86 OutputSegments& output) {
87 output.reserve(output.size() + segments.size());
88 for (const ProjectionSegment& segment : segments) {
89 OutputSegment value;
90 value.subdetectorId = groupKey.subDetectorId;
91 value.triggerSector = groupKey.triggerSector;
92 value.bcTag = groupKey.bcTag;
93 value.projection = segment.isStrip ? Projection::Strip : Projection::Wire;
94 value.stationMask = segment.stationMask;
95 value.summedQuality = segment.summedQuality;
96 value.nStations = segment.nStations;
97 value.eta = segment.eta;
98 value.phi = segment.phi;
99 value.residual = segment.residual;
100 value.outputResidual = segment.outputResidual;
101 value.consistency = segment.consistency;
102 value.pivotChannel = segment.pivotChannel;
103 output.emplace_back(value);
104 }
105}
106
107const Coincidence* projectionPivot(const ProjectionSegment& segment) {
108 if (segment.points[2] != nullptr) return segment.points[2];
109 if (segment.points[1] != nullptr) return segment.points[1];
110 return segment.points[0];
111}
112
113ProjectionSegment makeSegment(const Coincidence* m1, const Coincidence* m2,
114 const Coincidence* m3, const bool isStrip) {
115 ProjectionSegment segment;
116 segment.points = {m1, m2, m3};
117 segment.isStrip = isStrip;
118 for (const Coincidence* point : segment.points) {
119 if (point == nullptr) continue;
120 segment.stationMask |= stationBit(point->station);
121 segment.summedQuality = static_cast<std::uint8_t>(
122 segment.summedQuality + point->observedLayers);
123 ++segment.nStations;
124 }
125
126 const Coincidence* pivot = projectionPivot(segment);
127 if (pivot != nullptr) {
128 segment.eta = pivot->eta;
129 segment.phi = pivot->phi;
130 segment.pivotChannel = pivot->channel;
131 }
132
133 std::vector<const Coincidence*> points;
134 for (const Coincidence* point : segment.points) {
135 if (point != nullptr) points.emplace_back(point);
136 }
137 if (points.size() >= 2U) {
138 const float first = measuringCoordinate(*points.front());
139 const float last = measuringCoordinate(*points.back());
140 segment.residual = coordinateDifference(isStrip, first, last);
141 segment.outputResidual = isStrip
142 ? segment.residual
143 : physicalDeltaTheta(*points.front(),
144 *points.back());
145 segment.consistency = std::abs(segment.residual);
146 }
147 if (m1 != nullptr && m2 != nullptr && m3 != nullptr) {
148 const float c1 = measuringCoordinate(*m1);
149 const float c2 = measuringCoordinate(*m2);
150 const float c3 = measuringCoordinate(*m3);
151 const float predictedM1 = isStrip
152 ? c2 - static_cast<float>(
154 : c2 - (c3 - c2);
155 segment.consistency = std::abs(
156 coordinateDifference(isStrip, c1, predictedM1));
157 segment.residual = coordinateDifference(isStrip, c1, c3);
158 segment.outputResidual = isStrip
159 ? segment.residual
160 : physicalDeltaTheta(*m1, *m3);
161 }
162
163 if (!isStrip) {
164 float sinPhi = 0.F;
165 float cosPhi = 0.F;
166 for (const Coincidence* point : points) {
167 sinPhi += std::sin(point->phi);
168 cosPhi += std::cos(point->phi);
169 }
170 segment.phi = std::atan2(sinPhi, cosPhi);
171 } else if (!points.empty()) {
172 float etaSum = 0.F;
173 for (const Coincidence* point : points) etaSum += point->eta;
174 segment.eta = etaSum / static_cast<float>(points.size());
175 }
176 return segment;
177}
178
179int coordinateKey(const Coincidence* point, const bool isStrip) {
180 if (point == nullptr) return 0;
181 const float coordinate = isStrip ? point->phi : point->eta;
182 return static_cast<int>(std::lround(100000.F * coordinate));
183}
184
185using SegmentKey = std::tuple<std::uint8_t, bool, int, int, int>;
186
187SegmentKey segmentKey(const ProjectionSegment& segment) {
188 return {segment.stationMask, segment.isStrip,
189 coordinateKey(segment.points[0], segment.isStrip),
190 coordinateKey(segment.points[1], segment.isStrip),
191 coordinateKey(segment.points[2], segment.isStrip)};
192}
193
194bool betterSegment(const ProjectionSegment& lhs,
195 const ProjectionSegment& rhs) {
196 if (lhs.nStations != rhs.nStations) return lhs.nStations > rhs.nStations;
197 if (lhs.summedQuality != rhs.summedQuality) {
198 return lhs.summedQuality > rhs.summedQuality;
199 }
200 const bool lhsHasM1 = (lhs.stationMask & 0x1U) != 0U;
201 const bool rhsHasM1 = (rhs.stationMask & 0x1U) != 0U;
202 if (lhsHasM1 != rhsHasM1) return lhsHasM1;
203 const bool lhsHasM3 = (lhs.stationMask & 0x4U) != 0U;
204 const bool rhsHasM3 = (rhs.stationMask & 0x4U) != 0U;
205 if (lhsHasM3 != rhsHasM3) return lhsHasM3;
206 if (lhs.consistency != rhs.consistency) {
207 return lhs.consistency < rhs.consistency;
208 }
209 return segmentKey(lhs) < segmentKey(rhs);
210}
211
212void addSegment(std::vector<ProjectionSegment>& segments,
213 std::set<SegmentKey>& keys, const Coincidence* m1,
214 const Coincidence* m2, const Coincidence* m3,
215 const bool isStrip, Statistics& statistics) {
216 const ProjectionSegment segment = makeSegment(m1, m2, m3, isStrip);
217 if (!keys.insert(segmentKey(segment)).second) {
218 ++statistics.nDuplicateProjectionSegments;
219 return;
220 }
221 segments.emplace_back(segment);
222}
223
224void retainProjectionWorkingSet(std::vector<ProjectionSegment>& segments,
225 const std::size_t maximum,
226 Statistics& statistics) {
227 std::stable_sort(segments.begin(), segments.end(), betterSegment);
228 if (maximum == 0U || segments.size() <= maximum) return;
229
230 std::array<bool, 8> retainedMask{};
231 std::vector<ProjectionSegment> retained;
232 retained.reserve(maximum);
233 for (const ProjectionSegment& segment : segments) {
234 if (segment.stationMask >= retainedMask.size() ||
235 retainedMask[segment.stationMask]) {
236 continue;
237 }
238 retained.emplace_back(segment);
239 retainedMask[segment.stationMask] = true;
240 if (retained.size() == maximum) break;
241 }
242 for (const ProjectionSegment& segment : segments) {
243 if (retained.size() == maximum) break;
244 const bool alreadyRetained = std::any_of(
245 retained.begin(), retained.end(),
246 [&segment](const ProjectionSegment& selected) {
247 return segmentKey(selected) == segmentKey(segment);
248 });
249 if (!alreadyRetained) retained.emplace_back(segment);
250 }
251 statistics.nLimitedProjectionSegments += segments.size() - retained.size();
252 segments = std::move(retained);
253 std::stable_sort(segments.begin(), segments.end(), betterSegment);
254}
255
256std::vector<ProjectionSegment> buildProjectionSegments(
257 const std::array<std::vector<const Coincidence*>, 3>& points,
258 const bool isStrip, const Config& config, Statistics& statistics) {
259 std::vector<ProjectionSegment> segments;
260 std::set<SegmentKey> keys;
261
262 for (const Coincidence* m1 : points[0]) {
263 for (const Coincidence* m2 : points[1]) {
264 for (const Coincidence* m3 : points[2]) {
265 addSegment(segments, keys, m1, m2, m3, isStrip, statistics);
266 }
267 }
268 }
269 for (const Coincidence* m1 : points[0]) {
270 for (const Coincidence* m3 : points[2]) {
271 addSegment(segments, keys, m1, nullptr, m3, isStrip, statistics);
272 }
273 for (const Coincidence* m2 : points[1]) {
274 addSegment(segments, keys, m1, m2, nullptr, isStrip, statistics);
275 }
276 }
277 for (const Coincidence* m2 : points[1]) {
278 for (const Coincidence* m3 : points[2]) {
279 addSegment(segments, keys, nullptr, m2, m3, isStrip, statistics);
280 }
281 }
282
283 retainProjectionWorkingSet(segments,
284 config.maxSegmentCombinationsPerGroup,
285 statistics);
286 return segments;
287}
288
289std::uint8_t quality(const ProjectionSegment& segment, const Station station) {
290 const Coincidence* point = segment.points[stationIndex(station)];
291 return point == nullptr ? 0U : point->observedLayers;
292}
293
294struct PositionPair {
295 const Coincidence* wire{nullptr};
296 const Coincidence* strip{nullptr};
297 std::size_t stationIndex{0U};
298 float deltaEta{0.F};
299 float deltaPhi{0.F};
300 float coordinateResidual{0.F};
301
302 explicit operator bool() const { return wire != nullptr && strip != nullptr; }
303};
304
305PositionPair selectPositionPair(const ProjectionSegment& wire,
306 const ProjectionSegment& strip,
307 const Config& config) {
308 // Validated Floating convention: common pivot M3 -> M2 -> M1, eta measured
309 // by wire and phi measured by strip. Channel IDs are provenance only.
310 for (std::size_t index = 3U; index > 0U; --index) {
311 const std::size_t station = index - 1U;
312 const Coincidence* wirePoint = wire.points[station];
313 const Coincidence* stripPoint = strip.points[station];
314 if (wirePoint == nullptr || stripPoint == nullptr) continue;
315
316 const float deltaEta = std::abs(wirePoint->eta - stripPoint->eta);
317 const float deltaPhi = std::abs(static_cast<float>(
318 xAOD::P4Helpers::deltaPhi(wirePoint->phi, stripPoint->phi)));
319 if (config.maxPivotWireStripDeltaEta >= 0.F &&
320 deltaEta > config.maxPivotWireStripDeltaEta) {
321 continue;
322 }
323 if (config.maxPivotWireStripDeltaPhi >= 0.F &&
324 deltaPhi > config.maxPivotWireStripDeltaPhi) {
325 continue;
326 }
327 return {wirePoint, stripPoint, station, deltaEta, deltaPhi,
328 std::hypot(deltaEta, deltaPhi)};
329 }
330 return {};
331}
332
333struct CandidatePair {
334 const ProjectionSegment* wire{nullptr};
335 const ProjectionSegment* strip{nullptr};
336 PositionPair position{};
337 std::uint8_t positionStationMask{0};
338 std::uint8_t combinedStationMask{0};
339};
340
341int bendScoreMilli(const CandidatePair& pair) {
342 const float bend = std::sqrt(
343 pair.wire->residual * pair.wire->residual +
344 0.25F * pair.strip->residual * pair.strip->residual);
345 return static_cast<int>(std::lround(1000.F * bend));
346}
347
348bool betterCandidatePair(const CandidatePair& lhs, const CandidatePair& rhs) {
349 const std::uint16_t lhsQuality = static_cast<std::uint16_t>(
350 lhs.wire->summedQuality + lhs.strip->summedQuality);
351 const std::uint16_t rhsQuality = static_cast<std::uint16_t>(
352 rhs.wire->summedQuality + rhs.strip->summedQuality);
353 if (lhsQuality != rhsQuality) return lhsQuality > rhsQuality;
354 const unsigned int lhsStations =
355 std::popcount(static_cast<unsigned int>(lhs.combinedStationMask));
356 const unsigned int rhsStations =
357 std::popcount(static_cast<unsigned int>(rhs.combinedStationMask));
358 if (lhsStations != rhsStations) return lhsStations > rhsStations;
359 if (lhs.position.stationIndex != rhs.position.stationIndex) {
360 return lhs.position.stationIndex > rhs.position.stationIndex;
361 }
362 const int lhsBend = bendScoreMilli(lhs);
363 const int rhsBend = bendScoreMilli(rhs);
364 if (lhsBend != rhsBend) return lhsBend < rhsBend;
365 if (lhs.position.coordinateResidual != rhs.position.coordinateResidual) {
366 return lhs.position.coordinateResidual < rhs.position.coordinateResidual;
367 }
368 if (lhs.position.wire->eta != rhs.position.wire->eta) {
369 return lhs.position.wire->eta < rhs.position.wire->eta;
370 }
371 return lhs.position.strip->phi < rhs.position.strip->phi;
372}
373
374using CandidateKey = std::tuple<
375 std::uint16_t, std::uint16_t, std::uint16_t, std::uint16_t,
376 std::int16_t, std::uint16_t, std::uint16_t, std::int16_t,
377 std::uint16_t, std::uint8_t, std::uint8_t, std::uint8_t, int, int>;
378
379CandidateKey candidateKey(const GroupKey& group, const CandidatePair& pair) {
380 return {group.subDetectorId,
381 group.triggerSector,
382 group.bcTag,
383 pair.position.wire->detectorSector,
384 pair.position.wire->stationEta,
385 pair.position.wire->stationPhi,
386 pair.position.strip->detectorSector,
387 pair.position.strip->stationEta,
388 pair.position.strip->stationPhi,
389 pair.wire->stationMask,
390 pair.strip->stationMask,
391 pair.positionStationMask,
392 static_cast<int>(std::lround(100000.F * pair.position.wire->eta)),
393 static_cast<int>(std::lround(100000.F * pair.position.strip->phi))};
394}
395
396void pruneLocalCandidateBins(std::vector<CandidatePair>& pairs,
397 const Config& config,
398 Statistics& statistics) {
399 if (config.maxCandidatesPerLocalBin == 0U ||
400 config.localCandidateEtaBinWidth <= 0.F ||
401 config.localCandidatePhiBinWidth <= 0.F || pairs.empty()) {
402 return;
403 }
404 std::stable_sort(pairs.begin(), pairs.end(), betterCandidatePair);
405 std::map<std::tuple<int, int, std::size_t>, std::size_t> keptPerBin;
406 std::vector<CandidatePair> retained;
407 retained.reserve(pairs.size());
408 for (const CandidatePair& pair : pairs) {
409 const float phi = static_cast<float>(
410 xAOD::P4Helpers::deltaPhi(pair.position.strip->phi, 0.));
411 const int etaBin = static_cast<int>(std::floor(
412 pair.position.wire->eta / config.localCandidateEtaBinWidth));
413 const int phiBin = static_cast<int>(std::floor(
414 (phi + std::numbers::pi_v<float>) /
415 config.localCandidatePhiBinWidth));
416 const auto key =
417 std::make_tuple(etaBin, phiBin, pair.position.stationIndex);
418 std::size_t& count = keptPerBin[key];
419 if (count >= config.maxCandidatesPerLocalBin) {
420 ++statistics.nLimitedCandidates;
421 continue;
422 }
423 retained.emplace_back(pair);
424 ++count;
425 }
426 pairs = std::move(retained);
427}
428
429bool sameChamber(const Coincidence& lhs, const Coincidence& rhs) {
430 return lhs.station == rhs.station && lhs.stationEta == rhs.stationEta &&
431 lhs.stationPhi == rhs.stationPhi;
432}
433
434bool sameCandidateChamberPath(const CandidatePair& lhs,
435 const CandidatePair& rhs) {
436 unsigned int commonStations = 0U;
437 for (std::size_t station = 0U; station < lhs.wire->points.size(); ++station) {
438 const Coincidence* lhsPoint = lhs.wire->points[station];
439 const Coincidence* rhsPoint = rhs.wire->points[station];
440 if (lhsPoint == nullptr || rhsPoint == nullptr) continue;
441 if (!sameChamber(*lhsPoint, *rhsPoint)) return false;
442 ++commonStations;
443 }
444 return commonStations >= 2U;
445}
446
447bool sameCoarseLocalPosition(const CandidatePair& lhs,
448 const CandidatePair& rhs,
449 const Config& config) {
450 if (!sameCandidateChamberPath(lhs, rhs)) return false;
451 if (config.localDuplicateEtaWindow <= 0.F ||
452 config.localDuplicatePhiWindow <= 0.F) {
453 return lhs.position.wire->eta == rhs.position.wire->eta &&
454 lhs.position.strip->phi == rhs.position.strip->phi;
455 }
456 return std::abs(lhs.position.wire->eta - rhs.position.wire->eta) <
457 config.localDuplicateEtaWindow &&
458 std::abs(xAOD::P4Helpers::deltaPhi(lhs.position.strip->phi,
459 rhs.position.strip->phi)) <
460 config.localDuplicatePhiWindow;
461}
462
463void suppressLocalPositionDuplicates(std::vector<CandidatePair>& pairs,
464 const Config& config,
465 Statistics& statistics) {
466 if (config.maxCandidatesPerLocalPosition == 0U || pairs.empty()) return;
467 std::stable_sort(pairs.begin(), pairs.end(), betterCandidatePair);
468 std::vector<CandidatePair> retained;
469 retained.reserve(pairs.size());
470 for (const CandidatePair& pair : pairs) {
471 std::size_t count = 0U;
472 for (const CandidatePair& selected : retained) {
473 if (sameCoarseLocalPosition(pair, selected, config)) {
474 ++count;
475 if (count >= config.maxCandidatesPerLocalPosition) break;
476 }
477 }
478 if (count >= config.maxCandidatesPerLocalPosition) {
479 ++statistics.nLocalDuplicateCandidates;
480 continue;
481 }
482 retained.emplace_back(pair);
483 }
484 pairs = std::move(retained);
485}
486
487void retainCandidateWorkingSet(std::vector<CandidatePair>& pairs,
488 const std::size_t maximum,
489 Statistics& statistics) {
490 std::stable_sort(pairs.begin(), pairs.end(), betterCandidatePair);
491 if (maximum == 0U || pairs.size() <= maximum) return;
492
493 std::array<bool, 8> retainedMask{};
494 std::vector<CandidatePair> retained;
495 retained.reserve(maximum);
496 for (const CandidatePair& pair : pairs) {
497 if (pair.positionStationMask >= retainedMask.size() ||
498 retainedMask[pair.positionStationMask]) {
499 continue;
500 }
501 retained.emplace_back(pair);
502 retainedMask[pair.positionStationMask] = true;
503 if (retained.size() == maximum) break;
504 }
505 for (const CandidatePair& pair : pairs) {
506 if (retained.size() == maximum) break;
507 const bool alreadyRetained = std::any_of(
508 retained.begin(), retained.end(),
509 [&pair](const CandidatePair& selected) {
510 return selected.wire == pair.wire && selected.strip == pair.strip &&
511 selected.position.stationIndex == pair.position.stationIndex;
512 });
513 if (!alreadyRetained) retained.emplace_back(pair);
514 }
515 statistics.nLimitedCandidates += pairs.size() - retained.size();
516 pairs = std::move(retained);
517 std::stable_sort(pairs.begin(), pairs.end(), betterCandidatePair);
518}
519
520void countPositionMask(const std::uint8_t mask, Statistics& statistics) {
521 if (mask == 0x7U) ++statistics.nM1M2M3Candidates;
522 else if (mask == 0x3U) ++statistics.nM1M2Candidates;
523 else if (mask == 0x5U) ++statistics.nM1M3Candidates;
524 else if (mask == 0x6U) ++statistics.nM2M3Candidates;
525 else if (mask == 0x1U) ++statistics.nM1OnlyPositionCandidates;
526 else if (mask == 0x2U) ++statistics.nM2OnlyPositionCandidates;
527 else if (mask == 0x4U) ++statistics.nM3OnlyPositionCandidates;
528}
529
530} // namespace
531
532namespace L0Muon {
533namespace TgcL0Floating {
534
537
539 const StationCoincidenceContainer& coincidences,
540 TgcL0CandidateContainer& candidates,
541 SegmentStatistics& statistics,
542 TgcL0SegmentContainer* validationSegments) const {
543 candidates.clear();
544 statistics = SegmentStatistics{};
545 if (validationSegments != nullptr) validationSegments->clear();
546
547 using Projections =
548 std::array<std::array<std::vector<const Coincidence*>, 3>, 2>;
549 std::map<GroupKey, Projections> grouped;
550 for (const Coincidence& coincidence : coincidences) {
551 if (stationBit(coincidence.station) == 0U) continue;
552 const std::size_t projection = coincidence.isStrip ? 1U : 0U;
553 grouped[coincidence.key][projection][stationIndex(coincidence.station)]
554 .emplace_back(&coincidence);
555 }
556
557 std::set<CandidateKey> candidateKeys;
558 for (const auto& [groupKey, projections] : grouped) {
559 const std::vector<ProjectionSegment> wireSegments =
560 buildProjectionSegments(projections[0], false, m_config, statistics);
561 const std::vector<ProjectionSegment> stripSegments =
562 buildProjectionSegments(projections[1], true, m_config, statistics);
563 statistics.nWireSegments += wireSegments.size();
564 statistics.nStripSegments += stripSegments.size();
565 if (validationSegments != nullptr) {
566 appendValidationSegments(groupKey, wireSegments, *validationSegments);
567 appendValidationSegments(groupKey, stripSegments, *validationSegments);
568 }
569
570 std::vector<CandidatePair> pairs;
571 pairs.reserve(wireSegments.size() * stripSegments.size());
572 for (const ProjectionSegment& wire : wireSegments) {
573 if (m_config.maxCandidateDThetaAbs >= 0.F &&
574 std::abs(wire.residual) > m_config.maxCandidateDThetaAbs) {
575 statistics.nRejectedPairs += stripSegments.size();
576 continue;
577 }
578 for (const ProjectionSegment& strip : stripSegments) {
579 if (m_config.maxCandidateDPhiAbs >= 0.F &&
580 std::abs(strip.residual) > m_config.maxCandidateDPhiAbs) {
581 ++statistics.nRejectedPairs;
582 continue;
583 }
584 const PositionPair position =
585 selectPositionPair(wire, strip, m_config);
586 if (!position) {
587 ++statistics.nRejectedPairs;
588 continue;
589 }
590 const std::uint8_t positionMask = static_cast<std::uint8_t>(
591 wire.stationMask & strip.stationMask);
592 const std::uint8_t combinedMask = static_cast<std::uint8_t>(
593 wire.stationMask | strip.stationMask);
594 if (std::popcount(static_cast<unsigned int>(combinedMask)) < 2) {
595 ++statistics.nRejectedPairs;
596 continue;
597 }
598 pairs.emplace_back(
599 CandidatePair{&wire, &strip, position, positionMask, combinedMask});
600 }
601 }
602
603 pruneLocalCandidateBins(pairs, m_config, statistics);
604 suppressLocalPositionDuplicates(pairs, m_config, statistics);
605 retainCandidateWorkingSet(pairs,
606 m_config.maxSegmentCombinationsPerGroup,
607 statistics);
608
609 for (const CandidatePair& pair : pairs) {
610 const CandidateKey keyValue = candidateKey(groupKey, pair);
611 if (!candidateKeys.insert(keyValue).second) {
612 ++statistics.nDuplicateCandidates;
613 continue;
614 }
615
616 const ProjectionSegment& wire = *pair.wire;
617 const ProjectionSegment& strip = *pair.strip;
618 TgcL0Candidate candidate;
619 candidate.subdetectorId = groupKey.subDetectorId;
620 candidate.sectorId = groupKey.triggerSector;
621 candidate.readoutSector = pair.position.wire->detectorSector;
622 candidate.bcTag = groupKey.bcTag;
623 candidate.eta = pair.position.wire->eta;
624 candidate.phi = pair.position.strip->phi;
625 candidate.deltaTheta = wire.outputResidual;
626 candidate.deltaPhi = strip.outputResidual;
627 candidate.wireStationMask = wire.stationMask;
628 candidate.stripStationMask = strip.stationMask;
629 candidate.positionStationMask = pair.positionStationMask;
630 candidate.stationMask = candidate.positionStationMask;
631 candidate.m1WireQuality = quality(wire, Station::M1);
632 candidate.m2WireQuality = quality(wire, Station::M2);
633 candidate.m3WireQuality = quality(wire, Station::M3);
634 candidate.m1StripQuality = quality(strip, Station::M1);
635 candidate.m2StripQuality = quality(strip, Station::M2);
636 candidate.m3StripQuality = quality(strip, Station::M3);
637 candidate.pivotWireChannel = pair.position.wire->channel;
638 candidate.pivotStripChannel = pair.position.strip->channel;
639
640 if ((candidate.positionStationMask & 0x1U) != 0U) {
641 candidate.m1Eta = wire.points[0]->eta;
642 candidate.m1Phi = strip.points[0]->phi;
643 candidate.m1StationEta = wire.points[0]->stationEta;
644 candidate.m1StationPhi = wire.points[0]->stationPhi;
645 }
646 if ((candidate.positionStationMask & 0x2U) != 0U) {
647 candidate.m2Eta = wire.points[1]->eta;
648 candidate.m2Phi = strip.points[1]->phi;
649 candidate.m2StationEta = wire.points[1]->stationEta;
650 candidate.m2StationPhi = wire.points[1]->stationPhi;
651 }
652 if ((candidate.positionStationMask & 0x4U) != 0U) {
653 candidate.m3Eta = wire.points[2]->eta;
654 candidate.m3Phi = strip.points[2]->phi;
655 candidate.m3StationEta = wire.points[2]->stationEta;
656 candidate.m3StationPhi = wire.points[2]->stationPhi;
657 }
658 candidate.wireQuality = wire.summedQuality;
659 candidate.selectorPriority = static_cast<std::uint8_t>(
660 2U * std::popcount(
661 static_cast<unsigned int>(pair.combinedStationMask)) +
662 wire.summedQuality + strip.summedQuality);
663 candidates.emplace_back(candidate);
664 countPositionMask(candidate.positionStationMask, statistics);
665 }
666 }
667 return StatusCode::SUCCESS;
668}
669
670} // namespace TgcL0Floating
671} // namespace L0Muon
Scalar deltaPhi(const MatrixBase< Derived > &vec) const
Scalar deltaR(const MatrixBase< Derived > &vec) const
Scalar phi() const
phi method
StatusCode build(const StationCoincidenceContainer &coincidences, TgcL0CandidateContainer &candidates, SegmentStatistics &statistics, TgcL0SegmentContainer *validationSegments=nullptr) const
SegmentReconstruction(SegmentReconstructionConfig config=SegmentReconstructionConfig{})
STL class.
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
std::vector< StationCoincidence > StationCoincidenceContainer
std::vector< TgcL0Segment > TgcL0SegmentContainer
TgcL0SegmentProjection
Projection represented by a transient TGC segment.
std::vector< TgcL0Candidate > TgcL0CandidateContainer
Event-local candidate collection.
double deltaEta(const I4Momentum &p1, const I4Momentum &p2)
Computes efficiently .
Definition P4Helpers.h:66
Definition index.py:1
output
Definition merge.py:16
STL namespace.
void stable_sort(DataModel_detail::iterator< DVL > beg, DataModel_detail::iterator< DVL > end)
Specialization of stable_sort for DataVector/List.
double deltaPhi(double phiA, double phiB)
delta Phi in range [-pi,pi[
setSAddress setEtaMS setDirPhiMS setDirZMS setBarrelRadius setEndcapAlpha setEndcapRadius setPhiMap phiBin
setSAddress setEtaMS setDirPhiMS setDirZMS setBarrelRadius setEndcapAlpha setEndcapRadius setInterceptInner setEtaMap etaBin
setBGCode setTAP setLVL2ErrorBits bool
Event-local candidate used by the TGC simulation tools.
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 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::uint16_t readoutSector
Run-3 detector/readout sector retained for diagnostics.
float m1Eta
Reconstructed station positions used only by validation/debug code.
std::uint16_t bcTag
Bunch-crossing tag.
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.
float phi
Azimuth at the TGC pivot plane, in radians.
std::uint16_t pivotStripChannel
std::uint16_t subdetectorId
Subdetector identifier.
std::uint8_t selectorPriority
Track-Selector priority.
std::uint16_t pivotWireChannel
Representative pivot channels retained for diagnostics.
float deltaTheta
Signed polar-angle residual, in radians.
std::uint8_t wireQuality
Wire quality.
Event-local hardware processing key.
Event-local projection segment optionally exposed for validation.