ATLAS Offline Software
Loading...
Searching...
No Matches
L0Muon::TgcL0FloatingPtLut Class Reference

Immutable Floating-pT runtime calibration loaded from ASCII. More...

#include <TgcL0FloatingPtLut.h>

Collaboration diagram for L0Muon::TgcL0FloatingPtLut:

Classes

struct  Knot
struct  Bin

Public Member Functions

TgcL0FloatingPtEvaluation evaluate (float eta, float phi, float signedDTheta) const
const std::string & version () const
unsigned int etaBins () const
unsigned int phiBinsPerFold () const
std::size_t knotCount () const
float absEtaMin () const
float absEtaMax () const
bool isDevelopmentPayload () const

Static Public Member Functions

static std::unique_ptr< TgcL0FloatingPtLutloadAscii (const std::string &calibrationPath, std::string &error)
 Load the human-readable calibration payload.

Static Public Attributes

static constexpr float s_maxEncodedPtGeV = 127.5F

Private Member Functions

 TgcL0FloatingPtLut ()=default
int etaBin (float eta) const
int phiFoldBin (float phi) const
const BinfindBin (int etaBin, int phiFoldBin) const
bool invertFloatingResponse (const Bin &bin, float responseMagnitudeRad, float &inversePtGeVInv) const

Private Attributes

std::string m_version {}
unsigned int m_etaBins {0U}
unsigned int m_phiBinsPerFold {0U}
float m_absEtaMin {0.F}
float m_absEtaMax {0.F}
std::vector< Binm_bins {}
std::vector< Knotm_knots {}
bool m_isDevelopmentPayload {false}

Detailed Description

Immutable Floating-pT runtime calibration loaded from ASCII.

Loading performs all file I/O and validation. Evaluation is read-only.

Definition at line 55 of file TgcL0FloatingPtLut.h.

Constructor & Destructor Documentation

◆ TgcL0FloatingPtLut()

L0Muon::TgcL0FloatingPtLut::TgcL0FloatingPtLut ( )
privatedefault

Member Function Documentation

◆ absEtaMax()

float L0Muon::TgcL0FloatingPtLut::absEtaMax ( ) const
inline

Definition at line 71 of file TgcL0FloatingPtLut.h.

◆ absEtaMin()

float L0Muon::TgcL0FloatingPtLut::absEtaMin ( ) const
inline

Definition at line 70 of file TgcL0FloatingPtLut.h.

◆ etaBin()

int L0Muon::TgcL0FloatingPtLut::etaBin ( float eta) const
private

Definition at line 282 of file TgcL0FloatingPtLut.cxx.

282 {
283 if (!std::isfinite(eta)) return -1;
284 const float absEta = std::abs(eta);
285 if (absEta < m_absEtaMin || absEta > m_absEtaMax) return -1;
286 if (absEta == m_absEtaMax) return static_cast<int>(m_etaBins) - 1;
287 const float scaled = (absEta - m_absEtaMin) * m_etaBins /
289 const int bin = static_cast<int>(std::floor(scaled));
290 return bin >= 0 && bin < static_cast<int>(m_etaBins) ? bin : -1;
291}
Scalar eta() const
pseudorapidity method
bool absEta(const xAOD::TauJet &tau, float &out)

◆ etaBins()

unsigned int L0Muon::TgcL0FloatingPtLut::etaBins ( ) const
inline

Definition at line 67 of file TgcL0FloatingPtLut.h.

67{ return m_etaBins; }

◆ evaluate()

TgcL0FloatingPtEvaluation L0Muon::TgcL0FloatingPtLut::evaluate ( float eta,
float phi,
float signedDTheta ) const

Definition at line 358 of file TgcL0FloatingPtLut.cxx.

359 {
360 TgcL0FloatingPtEvaluation result{};
361 result.etaBin = etaBin(eta);
362 result.phiFoldBin = phiFoldBin(phi);
363 if (result.etaBin < 0 || result.phiFoldBin < 0 ||
364 !std::isfinite(signedDTheta)) {
365 return result;
366 }
367 const Bin* bin = findBin(result.etaBin, result.phiFoldBin);
368 if (bin == nullptr) return result;
369 result.modelValid = true;
370 result.estimatedCharge = chargeFromSignedDTheta(signedDTheta);
371 result.chargeEstimateValid = result.estimatedCharge != 0;
372
373 const float magnitude = std::abs(signedDTheta);
374 const float transitionMagnitude =
375 bin->linearSlopeMagnitudeRadGeV / bin->transitionPtGeV;
376 float inversePt = 0.F;
377 if (magnitude <= transitionMagnitude) {
379 if (magnitude == 0.F) {
380 // Zero bending means that only a lower bound can be represented. Keep
381 // the diagnostic raw value finite while saturating the operational pT.
382 result.rawPtEstimateGeV = s_maxEncodedPtGeV;
383 result.ptEstimateValid = true;
384 } else {
385 inversePt = magnitude / bin->linearSlopeMagnitudeRadGeV;
386 }
387 } else {
389 if (!invertFloatingResponse(*bin, magnitude, inversePt)) return result;
390 }
391 if (!result.ptEstimateValid) {
392 if (!std::isfinite(inversePt) || inversePt <= 0.F) return result;
393 result.rawPtEstimateGeV = 1.F / inversePt;
394 result.ptEstimateValid = std::isfinite(result.rawPtEstimateGeV) &&
395 result.rawPtEstimateGeV > 0.F;
396 }
397 if (!result.ptEstimateValid) return result;
398
399 // The raw floating-point estimate is diagnostic provenance. Candidate
400 // ordering, thresholds and downstream EDM values use the representable
401 // 8-bit range and therefore saturate at 127.5 GeV.
402 result.ptEstimateGeV =
403 std::min(result.rawPtEstimateGeV, s_maxEncodedPtGeV);
404 result.estimatedPtValueIndex = encodePtValue(result.ptEstimateGeV);
405 for (int code = 14; code >= 1; --code) {
406 const float cut = bin->thresholdCutsGeV[code];
407 if (result.ptEstimateGeV >= cut) {
408 result.thresholdCode = static_cast<std::uint8_t>(code);
409 result.thresholdCutGeV = cut;
410 result.thresholdCalibrationStatus = bin->thresholdStatuses[code];
411 break;
412 }
413 }
414 return result;
415}
Scalar phi() const
phi method
const Bin * findBin(int etaBin, int phiFoldBin) const
static constexpr float s_maxEncodedPtGeV
bool invertFloatingResponse(const Bin &bin, float responseMagnitudeRad, float &inversePtGeVInv) const

◆ findBin()

const TgcL0FloatingPtLut::Bin * L0Muon::TgcL0FloatingPtLut::findBin ( int etaBin,
int phiFoldBin ) const
private

Definition at line 305 of file TgcL0FloatingPtLut.cxx.

306 {
307 if (eta < 0 || eta >= static_cast<int>(m_etaBins) || phi < 0 ||
308 phi >= static_cast<int>(m_phiBinsPerFold)) {
309 return nullptr;
310 }
311 return &m_bins[static_cast<std::size_t>(eta) * m_phiBinsPerFold + phi];
312}

◆ invertFloatingResponse()

bool L0Muon::TgcL0FloatingPtLut::invertFloatingResponse ( const Bin & bin,
float responseMagnitudeRad,
float & inversePtGeVInv ) const
private

Definition at line 314 of file TgcL0FloatingPtLut.cxx.

316 {
317 const auto begin = m_knots.begin() + bin.knotOffset;
318 const auto end = begin + bin.knotCount;
319 if (responseMagnitudeRad <= begin->responseMagnitudeRad) {
320 inversePtGeVInv = begin->inversePtGeVInv;
321 return true;
322 }
323 const auto upper = std::lower_bound(
324 begin, end, responseMagnitudeRad,
325 [](const Knot& knot, const float value) {
326 return knot.responseMagnitudeRad < value;
327 });
328 if (upper != end) {
329 if (upper->responseMagnitudeRad == responseMagnitudeRad) {
330 inversePtGeVInv = upper->inversePtGeVInv;
331 return true;
332 }
333 const Knot& low = *(upper - 1);
334 const float delta = upper->responseMagnitudeRad -
335 low.responseMagnitudeRad;
336 if (!(delta > 0.F)) return false;
337 const float fraction =
338 (responseMagnitudeRad - low.responseMagnitudeRad) / delta;
339 inversePtGeVInv = low.inversePtGeVInv +
340 fraction * (upper->inversePtGeVInv -
341 low.inversePtGeVInv);
342 return true;
343 }
344 const Knot& last = *(end - 1);
345 const Knot& previous = *(end - 2);
346 const float deltaResponse =
347 last.responseMagnitudeRad - previous.responseMagnitudeRad;
348 if (!(deltaResponse > 0.F)) return false;
349 inversePtGeVInv = last.inversePtGeVInv +
350 (last.inversePtGeVInv - previous.inversePtGeVInv) /
351 deltaResponse *
352 (responseMagnitudeRad - last.responseMagnitudeRad);
353 inversePtGeVInv = std::clamp(inversePtGeVInv, 0.F,
354 last.inversePtGeVInv);
355 return true;
356}
int upper(int c)
@ previous
Definition BinningData.h:34

◆ isDevelopmentPayload()

bool L0Muon::TgcL0FloatingPtLut::isDevelopmentPayload ( ) const
inline

Definition at line 72 of file TgcL0FloatingPtLut.h.

◆ knotCount()

std::size_t L0Muon::TgcL0FloatingPtLut::knotCount ( ) const
inline

Definition at line 69 of file TgcL0FloatingPtLut.h.

69{ return m_knots.size(); }

◆ loadAscii()

std::unique_ptr< TgcL0FloatingPtLut > L0Muon::TgcL0FloatingPtLut::loadAscii ( const std::string & calibrationPath,
std::string & error )
static

Load the human-readable calibration payload.

Definition at line 63 of file TgcL0FloatingPtLut.cxx.

64 {
65 error.clear();
66 std::ifstream input{calibrationPath};
67 if (!input) {
68 error = "cannot open ASCII calibration file: " + calibrationPath;
69 return nullptr;
70 }
71
72 auto lut = std::unique_ptr<TgcL0FloatingPtLut>{new TgcL0FloatingPtLut};
73 unsigned int schemaVersion = 0U;
74 std::vector<bool> binSeen;
75 std::vector<std::array<bool, 16>> thresholdSeen;
76 std::vector<std::vector<std::pair<unsigned int, Knot>>> knotsByBin;
77 std::string line;
78 std::size_t lineNumber = 0U;
79 bool calibrationRecordsStarted = false;
80 while (std::getline(input, line)) {
81 ++lineNumber;
82 if (line.empty() || line[0] == '#') continue;
83 if (hasEmptyCsvField(line)) {
84 error = "empty CSV field at line " + std::to_string(lineNumber);
85 return nullptr;
86 }
87 const auto fields = CxxUtils::tokenize(line, ',');
88 if (fields.empty()) continue;
89 const std::string& record = fields[0];
90 if (record == "META") {
91 if (calibrationRecordsStarted) {
92 error = "META row after calibration records at line " +
93 std::to_string(lineNumber);
94 return nullptr;
95 }
96 if (fields.size() != 3U) {
97 error = "invalid META row at line " + std::to_string(lineNumber);
98 return nullptr;
99 }
100 if (fields[1] == "schemaVersion") {
101 if (!parseInteger(fields[2], schemaVersion)) {
102 error = "invalid schemaVersion";
103 return nullptr;
104 }
105 } else if (fields[1] == "payloadVersion") {
106 lut->m_version = fields[2];
107 } else if (fields[1] == "etaBins") {
108 if (!parseInteger(fields[2], lut->m_etaBins)) {
109 error = "invalid etaBins";
110 return nullptr;
111 }
112 } else if (fields[1] == "phiBinsPerFold") {
113 if (!parseInteger(fields[2], lut->m_phiBinsPerFold)) {
114 error = "invalid phiBinsPerFold";
115 return nullptr;
116 }
117 } else if (fields[1] == "absEtaMin") {
118 if (!parseFloatingPoint(fields[2], lut->m_absEtaMin)) {
119 error = "invalid absEtaMin";
120 return nullptr;
121 }
122 } else if (fields[1] == "absEtaMax") {
123 if (!parseFloatingPoint(fields[2], lut->m_absEtaMax)) {
124 error = "invalid absEtaMax";
125 return nullptr;
126 }
127 } else if (fields[1] == "payloadMode") {
128 lut->m_isDevelopmentPayload = fields[2] == "development";
129 }
130 continue;
131 }
132 calibrationRecordsStarted = true;
133
134 if (lut->m_etaBins == 0U || lut->m_phiBinsPerFold == 0U) {
135 error = "META dimensions must precede calibration records";
136 return nullptr;
137 }
138 const std::size_t count = static_cast<std::size_t>(lut->m_etaBins) *
139 lut->m_phiBinsPerFold;
140 if (lut->m_bins.empty()) {
141 lut->m_bins.resize(count);
142 binSeen.assign(count, false);
143 thresholdSeen.resize(count);
144 knotsByBin.resize(count);
145 }
146
147 int eta = -1;
148 int phi = -1;
149 if (fields.size() < 3U || !parseInteger(fields[1], eta) ||
150 !parseInteger(fields[2], phi) || eta < 0 || phi < 0 ||
151 eta >= static_cast<int>(lut->m_etaBins) ||
152 phi >= static_cast<int>(lut->m_phiBinsPerFold)) {
153 error = "invalid calibration bin at line " +
154 std::to_string(lineNumber);
155 return nullptr;
156 }
157 const std::size_t index = static_cast<std::size_t>(eta) *
158 lut->m_phiBinsPerFold +
159 static_cast<std::size_t>(phi);
160 Bin& bin = lut->m_bins[index];
161 if (record == "BIN") {
162 if (fields.size() != 5U || binSeen[index] ||
163 !parseFloatingPoint(fields[3], bin.linearSlopeMagnitudeRadGeV) ||
164 !parseFloatingPoint(fields[4], bin.transitionPtGeV)) {
165 error = "invalid BIN row at line " + std::to_string(lineNumber);
166 return nullptr;
167 }
168 binSeen[index] = true;
169 } else if (record == "THRESHOLD") {
170 int code = 0;
171 int status = 0;
172 float cut = 0.F;
173 if (fields.size() != 6U || !parseInteger(fields[3], code) ||
174 !parseFloatingPoint(fields[4], cut) ||
175 !parseInteger(fields[5], status) || code < 1 || code > 14 ||
176 status < 1 || status > 5 || thresholdSeen[index][code]) {
177 error = "invalid THRESHOLD row at line " +
178 std::to_string(lineNumber);
179 return nullptr;
180 }
181 bin.thresholdCutsGeV[code] = cut;
182 bin.thresholdStatuses[code] =
184 thresholdSeen[index][code] = true;
185 } else if (record == "KNOT") {
186 unsigned int knotIndex = 0U;
187 Knot knot;
188 if (fields.size() != 6U ||
189 !parseInteger(fields[3], knotIndex) ||
190 !parseFloatingPoint(fields[4], knot.inversePtGeVInv) ||
191 !parseFloatingPoint(fields[5], knot.responseMagnitudeRad)) {
192 error = "invalid KNOT row at line " + std::to_string(lineNumber);
193 return nullptr;
194 }
195 knotsByBin[index].emplace_back(knotIndex, knot);
196 } else {
197 error = "unknown calibration record at line " +
198 std::to_string(lineNumber) + ": " + record;
199 return nullptr;
200 }
201 }
202
203 if (schemaVersion != 1U || lut->m_version.empty() ||
204 !std::isfinite(lut->m_absEtaMin) ||
205 !std::isfinite(lut->m_absEtaMax) ||
206 !(lut->m_absEtaMax > lut->m_absEtaMin) || lut->m_bins.empty()) {
207 error = "incomplete or unsupported ASCII calibration metadata";
208 return nullptr;
209 }
210 for (std::size_t index = 0U; index < lut->m_bins.size(); ++index) {
211 Bin& bin = lut->m_bins[index];
212 if (!binSeen[index] ||
213 !std::isfinite(bin.linearSlopeMagnitudeRadGeV) ||
214 !std::isfinite(bin.transitionPtGeV) ||
215 !(bin.linearSlopeMagnitudeRadGeV > 0.F) ||
216 !(bin.transitionPtGeV > 0.F)) {
217 error = "missing or invalid BIN record for bin " +
218 std::to_string(index);
219 return nullptr;
220 }
221 float previousCut = 0.F;
222 for (unsigned int code = 1U; code <= 14U; ++code) {
223 const float cut = bin.thresholdCutsGeV[code];
224 if (!thresholdSeen[index][code] || !std::isfinite(cut) ||
225 !(cut > 0.F) || cut < previousCut) {
226 error = "missing or invalid threshold for bin " +
227 std::to_string(index);
228 return nullptr;
229 }
230 previousCut = cut;
231 }
232 auto& indexedKnots = knotsByBin[index];
233 std::sort(indexedKnots.begin(), indexedKnots.end(),
234 [](const auto& left, const auto& right) {
235 return left.first < right.first;
236 });
237 if (indexedKnots.size() < 2U) {
238 error = "fewer than two knots for bin " + std::to_string(index);
239 return nullptr;
240 }
241 bin.knotOffset = static_cast<std::uint32_t>(lut->m_knots.size());
242 unsigned int expectedIndex = 0U;
243 float previousInversePt = -1.F;
244 float previousResponse = -1.F;
245 for (const auto& [knotIndex, knot] : indexedKnots) {
246 if (knotIndex != expectedIndex++ ||
247 !std::isfinite(knot.inversePtGeVInv) ||
248 !std::isfinite(knot.responseMagnitudeRad) ||
249 !(knot.inversePtGeVInv > 0.F) ||
250 !(knot.responseMagnitudeRad >= 0.F) ||
251 knot.inversePtGeVInv < previousInversePt ||
252 knot.responseMagnitudeRad < previousResponse) {
253 error = "invalid or non-monotonic knot sequence for bin " +
254 std::to_string(index);
255 return nullptr;
256 }
257 previousInversePt = knot.inversePtGeVInv;
258 previousResponse = knot.responseMagnitudeRad;
259 if (lut->m_knots.size() > bin.knotOffset &&
260 knot.responseMagnitudeRad ==
261 lut->m_knots.back().responseMagnitudeRad) {
262 // A monotonic calibration can contain a response plateau. Its
263 // inverse is ambiguous, so retain the largest inverse-pT value and
264 // therefore the most conservative pT estimate.
265 lut->m_knots.back() = knot;
266 } else {
267 lut->m_knots.push_back(knot);
268 }
269 }
270 bin.knotCount = static_cast<std::uint32_t>(lut->m_knots.size() -
271 bin.knotOffset);
272 if (bin.knotCount < 2U) {
273 error = "fewer than two distinct knot responses for bin " +
274 std::to_string(index);
275 return nullptr;
276 }
277 }
278
279 return lut;
280}
int count(std::string s, const std::string &regx)
count how many occurances of a regx are in a string
Definition hcg.cxx:148
std::vector< std::string > tokenize(std::string_view the_str, std::string_view delimiters)
Splits the string into smaller substrings.
str index
Definition DeMoScan.py:362
constexpr auto lut(Generator &&f)
TgcL0FloatingThresholdCalibrationStatus
status
Definition merge.py:16
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.

◆ phiBinsPerFold()

unsigned int L0Muon::TgcL0FloatingPtLut::phiBinsPerFold ( ) const
inline

Definition at line 68 of file TgcL0FloatingPtLut.h.

68{ return m_phiBinsPerFold; }

◆ phiFoldBin()

int L0Muon::TgcL0FloatingPtLut::phiFoldBin ( float phi) const
private

Definition at line 293 of file TgcL0FloatingPtLut.cxx.

293 {
294 if (!std::isfinite(phi)) return -1;
295 const float period = 2.F * std::numbers::pi_v<float> / 8.F;
296 float folded = std::fmod(phi, period);
297 if (folded < 0.F) folded += period;
298 float fraction = folded / period;
299 if (fraction >= 1.F) fraction = 0.F;
300 return std::clamp(
301 static_cast<int>(std::floor(fraction * m_phiBinsPerFold)), 0,
302 static_cast<int>(m_phiBinsPerFold) - 1);
303}

◆ version()

const std::string & L0Muon::TgcL0FloatingPtLut::version ( ) const
inline

Definition at line 66 of file TgcL0FloatingPtLut.h.

66{ return m_version; }

Member Data Documentation

◆ m_absEtaMax

float L0Muon::TgcL0FloatingPtLut::m_absEtaMax {0.F}
private

Definition at line 102 of file TgcL0FloatingPtLut.h.

102{0.F};

◆ m_absEtaMin

float L0Muon::TgcL0FloatingPtLut::m_absEtaMin {0.F}
private

Definition at line 101 of file TgcL0FloatingPtLut.h.

101{0.F};

◆ m_bins

std::vector<Bin> L0Muon::TgcL0FloatingPtLut::m_bins {}
private

Definition at line 103 of file TgcL0FloatingPtLut.h.

103{};

◆ m_etaBins

unsigned int L0Muon::TgcL0FloatingPtLut::m_etaBins {0U}
private

Definition at line 99 of file TgcL0FloatingPtLut.h.

99{0U};

◆ m_isDevelopmentPayload

bool L0Muon::TgcL0FloatingPtLut::m_isDevelopmentPayload {false}
private

Definition at line 105 of file TgcL0FloatingPtLut.h.

105{false};

◆ m_knots

std::vector<Knot> L0Muon::TgcL0FloatingPtLut::m_knots {}
private

Definition at line 104 of file TgcL0FloatingPtLut.h.

104{};

◆ m_phiBinsPerFold

unsigned int L0Muon::TgcL0FloatingPtLut::m_phiBinsPerFold {0U}
private

Definition at line 100 of file TgcL0FloatingPtLut.h.

100{0U};

◆ m_version

std::string L0Muon::TgcL0FloatingPtLut::m_version {}
private

Definition at line 98 of file TgcL0FloatingPtLut.h.

98{};

◆ s_maxEncodedPtGeV

float L0Muon::TgcL0FloatingPtLut::s_maxEncodedPtGeV = 127.5F
staticconstexpr

Definition at line 57 of file TgcL0FloatingPtLut.h.


The documentation for this class was generated from the following files: