Load the human-readable calibration payload.
72 {
74 std::ifstream
input{calibrationPath};
75 if (!input) {
76 error =
"cannot open ASCII calibration file: " + calibrationPath;
77 return nullptr;
78 }
79
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;
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 }
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") {
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()) {
150 binSeen.assign(
count,
false);
151 thresholdSeen.resize(
count);
152 knotsByBin.resize(
count);
153 }
154
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);
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") {
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 }
193 } else if (record == "KNOT") {
194 unsigned int knotIndex = 0U;
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) {
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) {
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 }
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
273}
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.