ATLAS Offline Software
Loading...
Searching...
No Matches
TgcL0ValidationEventCheck.h
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4#ifndef L0MUONS1TGCVALIDATION_TGCL0VALIDATIONEVENTCHECK_H
5#define L0MUONS1TGCVALIDATION_TGCL0VALIDATIONEVENTCHECK_H
6
8
9#include <cmath>
10#include <cstddef>
11#include <string>
12#include <utility>
13#include <vector>
14
15namespace L0Muon {
16
19 bool valid{true};
20 std::string message;
21};
22
25 const TgcL0ValidationEvent& event) {
26 const std::size_t nTruth = event.truth.pdgId.size();
27 const std::size_t nSegments = event.segments.subdetectorId.size();
28 const std::size_t nCandidates = event.candidates.subdetectorId.size();
29
30 const auto invalid = [](std::string message) {
31 return TgcL0ValidationCheckResult{false, std::move(message)};
32 };
33 const auto checkTruthSize = [&](const std::size_t size,
34 const char* field) {
35 return size == nTruth
37 : invalid(std::string{"Truth block size mismatch for "} +
38 field);
39 };
40 const auto checkSegmentSize = [&](const std::size_t size,
41 const char* field) {
42 return size == nSegments
44 : invalid(std::string{"Segment block size mismatch for "} +
45 field);
46 };
47 const auto checkCandidateSize = [&](const std::size_t size,
48 const char* field) {
49 return size == nCandidates
51 : invalid(std::string{"Candidate block size mismatch for "} +
52 field);
53 };
54
55#define TGC_CHECK_TRUTH_SIZE(FIELD) \
56 do { \
57 const auto result = checkTruthSize( \
58 event.truth.FIELD.size(), #FIELD); \
59 if (!result.valid) return result; \
60 } while (false)
61
62 TGC_CHECK_TRUTH_SIZE(barcode);
67 TGC_CHECK_TRUTH_SIZE(extrapolatedStationMask);
74 TGC_CHECK_TRUTH_SIZE(matched);
75 TGC_CHECK_TRUTH_SIZE(matchedCandidateIndex);
76 TGC_CHECK_TRUTH_SIZE(matchMeanDeltaR);
77 TGC_CHECK_TRUTH_SIZE(unmatchedReason);
78 TGC_CHECK_TRUTH_SIZE(wireSegmentMatched);
79 TGC_CHECK_TRUTH_SIZE(stripSegmentMatched);
80 TGC_CHECK_TRUTH_SIZE(matchedWireSegmentIndex);
81 TGC_CHECK_TRUTH_SIZE(matchedStripSegmentIndex);
82 TGC_CHECK_TRUTH_SIZE(wireSegmentMatchResidual);
83 TGC_CHECK_TRUTH_SIZE(stripSegmentMatchResidual);
84#undef TGC_CHECK_TRUTH_SIZE
85
86#define TGC_CHECK_SEGMENT_SIZE(FIELD) \
87 do { \
88 const auto result = checkSegmentSize( \
89 event.segments.FIELD.size(), #FIELD); \
90 if (!result.valid) return result; \
91 } while (false)
92
93 TGC_CHECK_SEGMENT_SIZE(triggerSector);
95 TGC_CHECK_SEGMENT_SIZE(projection);
96 TGC_CHECK_SEGMENT_SIZE(stationMask);
97 TGC_CHECK_SEGMENT_SIZE(summedQuality);
98 TGC_CHECK_SEGMENT_SIZE(nStations);
101 TGC_CHECK_SEGMENT_SIZE(residual);
102 TGC_CHECK_SEGMENT_SIZE(outputResidual);
103 TGC_CHECK_SEGMENT_SIZE(consistency);
104 TGC_CHECK_SEGMENT_SIZE(pivotChannel);
105 TGC_CHECK_SEGMENT_SIZE(truthIndex);
106 TGC_CHECK_SEGMENT_SIZE(truthMatchResidual);
107#undef TGC_CHECK_SEGMENT_SIZE
108
109#define TGC_CHECK_CANDIDATE_SIZE(FIELD) \
110 do { \
111 const auto result = checkCandidateSize( \
112 event.candidates.FIELD.size(), #FIELD); \
113 if (!result.valid) return result; \
114 } while (false)
115
116 TGC_CHECK_CANDIDATE_SIZE(triggerSector);
117 TGC_CHECK_CANDIDATE_SIZE(readoutSector);
119 TGC_CHECK_CANDIDATE_SIZE(stationMask);
120 TGC_CHECK_CANDIDATE_SIZE(wireStationMask);
121 TGC_CHECK_CANDIDATE_SIZE(stripStationMask);
124 TGC_CHECK_CANDIDATE_SIZE(deltaTheta);
126 TGC_CHECK_CANDIDATE_SIZE(truthIndex);
127#undef TGC_CHECK_CANDIDATE_SIZE
128
129 const auto wireProjection =
130 static_cast<std::uint8_t>(TgcL0ValidationProjection::Wire);
131 const auto stripProjection =
132 static_cast<std::uint8_t>(TgcL0ValidationProjection::Strip);
133
134 for (std::size_t segment = 0U; segment < nSegments; ++segment) {
135 const std::uint8_t projection = event.segments.projection[segment];
136 if (projection != wireProjection && projection != stripProjection) {
137 return invalid("Segment projection is not wire or strip");
138 }
139 if (event.segments.nStations[segment] == 0U ||
140 event.segments.nStations[segment] > 3U) {
141 return invalid("Segment station count is out of range");
142 }
143 const int truth = event.segments.truthIndex[segment];
144 if (truth >= 0 && static_cast<std::size_t>(truth) >= nTruth) {
145 return invalid("Segment truth index is out of range");
146 }
147 const float residual = event.segments.truthMatchResidual[segment];
148 if (truth >= 0 &&
149 (!std::isfinite(residual) ||
150 residual == TgcL0ValidationInvalidValue)) {
151 return invalid("Matched segment has an invalid truth residual");
152 }
153 if (truth < 0 && residual != TgcL0ValidationInvalidValue) {
154 return invalid("Unmatched segment has a truth residual");
155 }
156 }
157
158 for (const int truth : event.candidates.truthIndex) {
159 if (truth >= 0 && static_cast<std::size_t>(truth) >= nTruth) {
160 return invalid("Candidate truth index is out of range");
161 }
162 }
163
164 std::vector<int> candidateOwner(nCandidates, -1);
165 for (std::size_t truth = 0U; truth < nTruth; ++truth) {
166 if (event.truth.matched[truth] > 1U) {
167 return invalid("Truth matched flag is not binary");
168 }
169 const bool matched = event.truth.matched[truth] != 0U;
170 const int candidate = event.truth.matchedCandidateIndex[truth];
171 if (candidate >= 0 && static_cast<std::size_t>(candidate) >= nCandidates) {
172 return invalid("Truth matched-candidate index is out of range");
173 }
174 if (matched && candidate < 0) {
175 return invalid("Matched truth has no candidate index");
176 }
177 if (!matched && candidate >= 0) {
178 return invalid("Unmatched truth has a candidate index");
179 }
180 if (matched &&
181 event.truth.unmatchedReason[truth] !=
183 return invalid("Matched truth has an unmatched-reason code");
184 }
185 if (!matched &&
186 event.truth.unmatchedReason[truth] ==
188 return invalid("Unmatched truth has no unmatched-reason code");
189 }
190 if (!matched &&
191 event.truth.unmatchedReason[truth] >
192 static_cast<std::uint8_t>(
194 return invalid("Truth unmatched-reason code is out of range");
195 }
196 if (matched &&
197 (!std::isfinite(event.truth.matchMeanDeltaR[truth]) ||
198 event.truth.matchMeanDeltaR[truth] == TgcL0ValidationInvalidValue)) {
199 return invalid("Matched truth has an invalid candidate residual");
200 }
201 if (!matched &&
202 event.truth.matchMeanDeltaR[truth] != TgcL0ValidationInvalidValue) {
203 return invalid("Unmatched truth has a candidate residual");
204 }
205 if (candidate >= 0) {
206 const auto candidateIndex = static_cast<std::size_t>(candidate);
207 if (candidateOwner[candidateIndex] >= 0) {
208 return invalid("Candidate is matched to more than one truth muon");
209 }
210 candidateOwner[candidateIndex] = static_cast<int>(truth);
211 if (event.candidates.truthIndex[candidateIndex] !=
212 static_cast<int>(truth)) {
213 return invalid("Truth-candidate matching indices are not reciprocal");
214 }
215 }
216
217 if (event.truth.wireSegmentMatched[truth] > 1U ||
218 event.truth.stripSegmentMatched[truth] > 1U) {
219 return invalid("Truth segment-matched flag is not binary");
220 }
221 const bool wireMatched = event.truth.wireSegmentMatched[truth] != 0U;
222 const bool stripMatched = event.truth.stripSegmentMatched[truth] != 0U;
223 const int wireSegment = event.truth.matchedWireSegmentIndex[truth];
224 const int stripSegment = event.truth.matchedStripSegmentIndex[truth];
225 if (wireSegment >= 0 &&
226 static_cast<std::size_t>(wireSegment) >= nSegments) {
227 return invalid("Truth matched-wire-segment index is out of range");
228 }
229 if (stripSegment >= 0 &&
230 static_cast<std::size_t>(stripSegment) >= nSegments) {
231 return invalid("Truth matched-strip-segment index is out of range");
232 }
233 if (wireMatched && wireSegment < 0) {
234 return invalid("Wire-segment-matched truth has no segment index");
235 }
236 if (stripMatched && stripSegment < 0) {
237 return invalid("Strip-segment-matched truth has no segment index");
238 }
239 if (!wireMatched && wireSegment >= 0) {
240 return invalid("Wire-segment-unmatched truth has a segment index");
241 }
242 if (!stripMatched && stripSegment >= 0) {
243 return invalid("Strip-segment-unmatched truth has a segment index");
244 }
245 if (wireMatched &&
246 (!std::isfinite(event.truth.wireSegmentMatchResidual[truth]) ||
247 event.truth.wireSegmentMatchResidual[truth] ==
249 return invalid("Wire-segment-matched truth has an invalid residual");
250 }
251 if (!wireMatched &&
252 event.truth.wireSegmentMatchResidual[truth] !=
254 return invalid("Wire-segment-unmatched truth has a residual");
255 }
256 if (stripMatched &&
257 (!std::isfinite(event.truth.stripSegmentMatchResidual[truth]) ||
258 event.truth.stripSegmentMatchResidual[truth] ==
260 return invalid("Strip-segment-matched truth has an invalid residual");
261 }
262 if (!stripMatched &&
263 event.truth.stripSegmentMatchResidual[truth] !=
265 return invalid("Strip-segment-unmatched truth has a residual");
266 }
267 if (wireSegment >= 0 &&
268 event.segments.projection[wireSegment] != wireProjection) {
269 return invalid("Truth wire-segment index points to a strip segment");
270 }
271 if (stripSegment >= 0 &&
272 event.segments.projection[stripSegment] != stripProjection) {
273 return invalid("Truth strip-segment index points to a wire segment");
274 }
275 if ((wireSegment >= 0 &&
276 event.segments.truthIndex[wireSegment] != static_cast<int>(truth)) ||
277 (stripSegment >= 0 &&
278 event.segments.truthIndex[stripSegment] != static_cast<int>(truth))) {
279 return invalid("Truth-segment matching indices are not reciprocal");
280 }
281 }
282
283 for (std::size_t candidate = 0U; candidate < nCandidates; ++candidate) {
284 const int truth = event.candidates.truthIndex[candidate];
285 if (truth < 0) continue;
286 if (event.truth.matched[truth] == 0U ||
287 event.truth.matchedCandidateIndex[truth] !=
288 static_cast<int>(candidate)) {
289 return invalid("Truth-candidate matching indices are not reciprocal");
290 }
291 }
292
293 for (std::size_t segment = 0U; segment < nSegments; ++segment) {
294 const int truth = event.segments.truthIndex[segment];
295 if (truth < 0) continue;
296 const bool isWire = event.segments.projection[segment] == wireProjection;
297 const int reverseIndex =
298 isWire ? event.truth.matchedWireSegmentIndex[truth]
299 : event.truth.matchedStripSegmentIndex[truth];
300 if (reverseIndex != static_cast<int>(segment)) {
301 return invalid("Truth-segment matching indices are not reciprocal");
302 }
303 }
304
305 return {};
306}
307
308} // namespace L0Muon
309
310#endif
Scalar eta() const
pseudorapidity method
Scalar deltaPhi(const MatrixBase< Derived > &vec) const
Scalar phi() const
phi method
double charge(const T &p)
Definition AtlasPID.h:997
size_t size() const
Number of registered mappings.
unsigned bcTag(unsigned bcBitMap)
#define TGC_CHECK_SEGMENT_SIZE(FIELD)
#define TGC_CHECK_TRUTH_SIZE(FIELD)
#define TGC_CHECK_CANDIDATE_SIZE(FIELD)
TgcL0ValidationCheckResult checkTgcL0ValidationEvent(const TgcL0ValidationEvent &event)
Check vector sizes and reciprocal cross-block indices.
static constexpr float TgcL0ValidationInvalidValue
Common sentinel for unavailable floating-point validation data.
static constexpr std::uint8_t TgcL0ValidationNoUnmatchedReason
Sentinel indicating that a matched truth object has no failure code.
Result of a structural validation-data consistency check.
ROOT-independent validation data for one event.