ATLAS Offline Software
Loading...
Searching...
No Matches
TgcL0StationCoincidence.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
7#include <algorithm>
8#include <array>
9#include <bit>
10#include <cmath>
11#include <cstdint>
12#include <limits>
13#include <vector>
14
15namespace {
16
19
20std::size_t stationIndex(const Station station) {
21 if (station == Station::M1) return 0U;
22 if (station == Station::M2) return 1U;
23 return 2U;
24}
25
26bool isBigWheelStation(const Station station) {
27 return station == Station::M1 || station == Station::M2 ||
28 station == Station::M3;
29}
30
31bool betterRepresentativeHit(const Hit* lhs, const Hit* rhs,
32 const std::uint16_t representativeChannel) {
33 if (rhs == nullptr) return true;
34 if (lhs == nullptr) return false;
35 const int lhsDifference = std::abs(static_cast<int>(lhs->channel) -
36 static_cast<int>(representativeChannel));
37 const int rhsDifference = std::abs(static_cast<int>(rhs->channel) -
38 static_cast<int>(representativeChannel));
39 if (lhsDifference != rhsDifference) return lhsDifference < rhsDifference;
40 if (lhs->gasGap != rhs->gasGap) return lhs->gasGap < rhs->gasGap;
41 return lhs->channel < rhs->channel;
42}
43
44} // namespace
45
46namespace L0Muon {
47namespace TgcL0Floating {
48
50 const Station station, const bool isStrip) {
51 if (station == Station::M1 && !isStrip) return 3U;
52 if (station == Station::M1 || station == Station::M2 ||
53 station == Station::M3) {
54 return 2U;
55 }
56 return 0U;
57}
58
60 const HitGroups& hitGroups,
61 StationCoincidenceContainer& coincidences) const {
62 coincidences.clear();
63
64 for (const auto& [key, groupHits] : hitGroups) {
65 std::array<std::array<std::vector<const Hit*>, 2>, 3> grouped{};
66 for (const Hit& hit : groupHits) {
67 if (!isBigWheelStation(hit.station)) continue;
68 grouped[stationIndex(hit.station)][hit.isStrip ? 1U : 0U].emplace_back(&hit);
69 }
70
71 for (std::size_t stationPosition = 0U; stationPosition < grouped.size();
72 ++stationPosition) {
73 const Station hitStation = static_cast<Station>(stationPosition);
74 for (std::size_t projection = 0U; projection < 2U; ++projection) {
75 const bool isStrip = projection == 1U;
76 std::vector<const Hit*>& hits = grouped[stationPosition][projection];
77 if (hits.empty()) continue;
78
79 std::sort(hits.begin(), hits.end(),
80 [](const Hit* lhs, const Hit* rhs) {
81 if (lhs->channel != rhs->channel) {
82 return lhs->channel < rhs->channel;
83 }
84 if (lhs->gasGap != rhs->gasGap) {
85 return lhs->gasGap < rhs->gasGap;
86 }
87 if (lhs->stationEta != rhs->stationEta) {
88 return lhs->stationEta < rhs->stationEta;
89 }
90 return lhs->stationPhi < rhs->stationPhi;
91 });
92
93 std::vector<const Hit*> cluster;
94 auto flushCluster = [&]() {
95 if (cluster.empty()) return;
96
97 std::uint32_t channelSum = 0U;
98 std::uint8_t layerMask = 0U;
99 float etaSum = 0.F;
100 float sinPhiSum = 0.F;
101 float cosPhiSum = 0.F;
102 float rSum = 0.F;
103 float zSum = 0.F;
104 for (const Hit* hit : cluster) {
105 channelSum += hit->channel;
106 if (hit->gasGap > 0U && hit->gasGap <= 3U) {
107 layerMask |= static_cast<std::uint8_t>(1U << (hit->gasGap - 1U));
108 }
109 etaSum += hit->eta;
110 sinPhiSum += std::sin(hit->phi);
111 cosPhiSum += std::cos(hit->phi);
112 rSum += hit->r;
113 zSum += hit->z;
114 }
115
116 const std::uint8_t observedLayers = static_cast<std::uint8_t>(
117 std::popcount(static_cast<unsigned int>(layerMask)));
118 if (observedLayers == 0U) {
119 cluster.clear();
120 return;
121 }
122
123 const std::uint16_t representativeChannel =
124 static_cast<std::uint16_t>(std::lround(
125 static_cast<double>(channelSum) /
126 static_cast<double>(cluster.size())));
127 const Hit* representativeHit = nullptr;
128 for (const Hit* hit : cluster) {
129 if (betterRepresentativeHit(hit, representativeHit,
130 representativeChannel)) {
131 representativeHit = hit;
132 }
133 }
134 if (representativeHit == nullptr) {
135 cluster.clear();
136 return;
137 }
138
139 const float scale = 1.F / static_cast<float>(cluster.size());
140 coincidences.emplace_back(
141 key, hitStation, isStrip, representativeHit->detectorSector,
142 representativeHit->stationEta, representativeHit->stationPhi,
143 representativeChannel, layerMask, observedLayers,
144 nominalLayers(hitStation, isStrip), etaSum * scale,
145 std::atan2(sinPhiSum, cosPhiSum), rSum * scale, zSum * scale);
146 cluster.clear();
147 };
148
149 std::uint16_t previousChannel = 0U;
150 bool hasPreviousChannel = false;
151 for (const Hit* hit : hits) {
152 if (!hasPreviousChannel ||
153 std::abs(static_cast<int>(hit->channel) -
154 static_cast<int>(previousChannel)) <= 1) {
155 cluster.emplace_back(hit);
156 } else {
157 flushCluster();
158 cluster.emplace_back(hit);
159 }
160 previousChannel = hit->channel;
161 hasPreviousChannel = true;
162 }
163 flushCluster();
164 }
165 }
166 }
167
168 std::sort(coincidences.begin(), coincidences.end(),
169 [](const StationCoincidence& lhs,
170 const StationCoincidence& rhs) {
171 if (lhs.key.tie() != rhs.key.tie()) {
172 return lhs.key.tie() < rhs.key.tie();
173 }
174 if (lhs.station != rhs.station) return lhs.station < rhs.station;
175 if (lhs.isStrip != rhs.isStrip) return lhs.isStrip < rhs.isStrip;
176 if (lhs.observedLayers != rhs.observedLayers) {
177 return lhs.observedLayers > rhs.observedLayers;
178 }
179 return lhs.channel < rhs.channel;
180 });
181 return StatusCode::SUCCESS;
182}
183
184} // namespace TgcL0Floating
185} // namespace L0Muon
if(pathvar)
StatusCode build(const HitGroups &hitGroups, StationCoincidenceContainer &coincidences) const
static std::uint8_t nominalLayers(Station station, bool isStrip)
std::map< HitGroupKey, HitContainer > HitGroups
std::vector< StationCoincidence > StationCoincidenceContainer
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.