Load the human-readable calibration payload.
64 {
66 std::ifstream
input{calibrationPath};
67 if (!input) {
68 error =
"cannot open ASCII calibration file: " + calibrationPath;
69 return nullptr;
70 }
71
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;
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 }
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 }
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") {
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()) {
142 binSeen.assign(
count,
false);
143 thresholdSeen.resize(
count);
144 knotsByBin.resize(
count);
145 }
146
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);
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") {
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 }
185 } else if (record == "KNOT") {
186 unsigned int knotIndex = 0U;
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) {
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) {
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 }
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
263
264
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() -
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
280}
TgcL0FloatingPtLut()=default
int count(std::string s, const std::string ®x)
count how many occurances of a regx are in a string
std::vector< std::string > tokenize(std::string_view the_str, std::string_view delimiters)
Splits the string into smaller substrings.
constexpr auto lut(Generator &&f)
TgcL0FloatingThresholdCalibrationStatus
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.