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 275 of file TgcL0FloatingPtLut.cxx.

275 {
276 if (!std::isfinite(eta)) return -1;
277 const float absEta = std::abs(eta);
278 if (absEta < m_absEtaMin || absEta > m_absEtaMax) return -1;
279 if (absEta == m_absEtaMax) return static_cast<int>(m_etaBins) - 1;
280 const float scaled = (absEta - m_absEtaMin) * m_etaBins /
282 const int bin = static_cast<int>(std::floor(scaled));
283 return bin >= 0 && bin < static_cast<int>(m_etaBins) ? bin : -1;
284}
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 351 of file TgcL0FloatingPtLut.cxx.

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

299 {
300 if (eta < 0 || eta >= static_cast<int>(m_etaBins) || phi < 0 ||
301 phi >= static_cast<int>(m_phiBinsPerFold)) {
302 return nullptr;
303 }
304 return &m_bins[static_cast<std::size_t>(eta) * m_phiBinsPerFold + phi];
305}

◆ invertFloatingResponse()

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

Definition at line 307 of file TgcL0FloatingPtLut.cxx.

309 {
310 const auto begin = m_knots.begin() + bin.knotOffset;
311 const auto end = begin + bin.knotCount;
312 if (responseMagnitudeRad <= begin->responseMagnitudeRad) {
313 inversePtGeVInv = begin->inversePtGeVInv;
314 return true;
315 }
316 const auto upper = std::lower_bound(
317 begin, end, responseMagnitudeRad,
318 [](const Knot& knot, const float value) {
319 return knot.responseMagnitudeRad < value;
320 });
321 if (upper != end) {
322 if (upper->responseMagnitudeRad == responseMagnitudeRad) {
323 inversePtGeVInv = upper->inversePtGeVInv;
324 return true;
325 }
326 const Knot& low = *(upper - 1);
327 const float delta = upper->responseMagnitudeRad -
328 low.responseMagnitudeRad;
329 if (!(delta > 0.F)) return false;
330 const float fraction =
331 (responseMagnitudeRad - low.responseMagnitudeRad) / delta;
332 inversePtGeVInv = low.inversePtGeVInv +
333 fraction * (upper->inversePtGeVInv -
334 low.inversePtGeVInv);
335 return true;
336 }
337 const Knot& last = *(end - 1);
338 const Knot& previous = *(end - 2);
339 const float deltaResponse =
340 last.responseMagnitudeRad - previous.responseMagnitudeRad;
341 if (!(deltaResponse > 0.F)) return false;
342 inversePtGeVInv = last.inversePtGeVInv +
343 (last.inversePtGeVInv - previous.inversePtGeVInv) /
344 deltaResponse *
345 (responseMagnitudeRad - last.responseMagnitudeRad);
346 inversePtGeVInv = std::clamp(inversePtGeVInv, 0.F,
347 last.inversePtGeVInv);
348 return true;
349}
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 71 of file TgcL0FloatingPtLut.cxx.

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

286 {
287 if (!std::isfinite(phi)) return -1;
288 const float period = 2.F * std::numbers::pi_v<float> / 8.F;
289 float folded = std::fmod(phi, period);
290 if (folded < 0.F) folded += period;
291 float fraction = folded / period;
292 if (fraction >= 1.F) fraction = 0.F;
293 return std::clamp(
294 static_cast<int>(std::floor(fraction * m_phiBinsPerFold)), 0,
295 static_cast<int>(m_phiBinsPerFold) - 1);
296}

◆ 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: