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