File indexing completed on 2025-08-06 08:10:46
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "ActsExamples/Io/Csv/CsvMeasurementReader.hpp"
0010
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/TrackParametrization.hpp"
0013 #include "Acts/Geometry/GeometryIdentifier.hpp"
0014 #include "ActsExamples/Digitization/MeasurementCreation.hpp"
0015 #include "ActsExamples/EventData/Cluster.hpp"
0016 #include "ActsExamples/EventData/GeometryContainers.hpp"
0017 #include "ActsExamples/EventData/Index.hpp"
0018 #include "ActsExamples/EventData/IndexSourceLink.hpp"
0019 #include "ActsExamples/EventData/Measurement.hpp"
0020 #include "ActsExamples/Framework/AlgorithmContext.hpp"
0021 #include "ActsExamples/Utilities/Paths.hpp"
0022
0023 #include <algorithm>
0024 #include <array>
0025 #include <cstdint>
0026 #include <functional>
0027 #include <iterator>
0028 #include <list>
0029 #include <stdexcept>
0030 #include <vector>
0031
0032 #include <dfe/dfe_io_dsv.hpp>
0033
0034 #include "CsvOutputData.hpp"
0035
0036 ActsExamples::CsvMeasurementReader::CsvMeasurementReader(
0037 const ActsExamples::CsvMeasurementReader::Config& config,
0038 Acts::Logging::Level level)
0039 : m_cfg(config),
0040 m_eventsRange(
0041 determineEventFilesRange(m_cfg.inputDir, "measurements.csv")),
0042 m_logger(Acts::getDefaultLogger("CsvMeasurementReader", level)) {
0043 if (m_cfg.outputMeasurements.empty()) {
0044 throw std::invalid_argument("Missing measurement output collection");
0045 }
0046
0047 m_outputMeasurements.initialize(m_cfg.outputMeasurements);
0048 m_outputMeasurementSimHitsMap.initialize(m_cfg.outputMeasurementSimHitsMap);
0049 m_outputSourceLinks.initialize(m_cfg.outputSourceLinks);
0050 m_outputClusters.maybeInitialize(m_cfg.outputClusters);
0051 m_outputMeasurementParticlesMap.maybeInitialize(
0052 m_cfg.outputMeasurementParticlesMap);
0053 m_inputHits.maybeInitialize(m_cfg.inputSimHits);
0054
0055
0056 auto checkRange = [&](const std::string& fileStem) {
0057 const auto hitmapRange = determineEventFilesRange(m_cfg.inputDir, fileStem);
0058 if (hitmapRange.first > m_eventsRange.first ||
0059 hitmapRange.second < m_eventsRange.second) {
0060 throw std::runtime_error("event range mismatch for 'event**-" + fileStem +
0061 "'");
0062 }
0063 };
0064
0065 checkRange("measurement-simhit-map.csv");
0066 if (!m_cfg.outputClusters.empty()) {
0067 checkRange("cells.csv");
0068 }
0069 }
0070
0071 std::string ActsExamples::CsvMeasurementReader::CsvMeasurementReader::name()
0072 const {
0073 return "CsvMeasurementReader";
0074 }
0075
0076 std::pair<std::size_t, std::size_t>
0077 ActsExamples::CsvMeasurementReader::availableEvents() const {
0078 return m_eventsRange;
0079 }
0080
0081 namespace {
0082 struct CompareHitId {
0083
0084 using is_transparent = void;
0085 template <typename T>
0086 constexpr bool operator()(const T& left, const T& right) const {
0087 return left.hit_id < right.hit_id;
0088 }
0089 template <typename T>
0090 constexpr bool operator()(uint64_t left_id, const T& right) const {
0091 return left_id < right.hit_id;
0092 }
0093 template <typename T>
0094 constexpr bool operator()(const T& left, uint64_t right_id) const {
0095 return left.hit_id < right_id;
0096 }
0097 };
0098
0099 struct CompareGeometryId {
0100 bool operator()(const ActsExamples::MeasurementData& left,
0101 const ActsExamples::MeasurementData& right) const {
0102 return left.geometry_id < right.geometry_id;
0103 }
0104 };
0105
0106 template <typename Data>
0107 inline std::vector<Data> readEverything(
0108 const std::string& inputDir, const std::string& filename,
0109 const std::vector<std::string>& optionalColumns, std::size_t event) {
0110 std::string path = ActsExamples::perEventFilepath(inputDir, filename, event);
0111 dfe::NamedTupleCsvReader<Data> reader(path, optionalColumns);
0112
0113 std::vector<Data> everything;
0114 Data one;
0115 while (reader.read(one)) {
0116 everything.push_back(one);
0117 }
0118
0119 return everything;
0120 }
0121
0122 std::vector<ActsExamples::MeasurementData> readMeasurementsByGeometryId(
0123 const std::string& inputDir, std::size_t event) {
0124
0125 auto measurements = readEverything<ActsExamples::MeasurementData>(
0126 inputDir, "measurements.csv", {"geometry_id", "t"}, event);
0127
0128 std::sort(measurements.begin(), measurements.end(), CompareGeometryId{});
0129 return measurements;
0130 }
0131
0132 ActsExamples::ClusterContainer makeClusters(
0133 const std::unordered_multimap<std::size_t, ActsExamples::CellData>&
0134 cellDataMap,
0135 std::size_t nMeasurements) {
0136 using namespace ActsExamples;
0137 ClusterContainer clusters;
0138
0139 for (auto index = 0ul; index < nMeasurements; ++index) {
0140 auto [begin, end] = cellDataMap.equal_range(index);
0141
0142
0143 Cluster cluster;
0144 cluster.channels.reserve(std::distance(begin, end));
0145
0146 for (auto it = begin; it != end; ++it) {
0147 const auto& cellData = it->second;
0148 ActsFatras::Segmentizer::Segment2D dummySegment = {Acts::Vector2::Zero(),
0149 Acts::Vector2::Zero()};
0150
0151 ActsFatras::Segmentizer::Bin2D bin{
0152 static_cast<unsigned int>(cellData.channel0),
0153 static_cast<unsigned int>(cellData.channel1)};
0154
0155 cluster.channels.emplace_back(bin, dummySegment, cellData.value);
0156 }
0157
0158
0159
0160
0161 if (!cluster.channels.empty()) {
0162 auto compareX = [](const auto& a, const auto& b) {
0163 return a.bin[0] < b.bin[0];
0164 };
0165 auto compareY = [](const auto& a, const auto& b) {
0166 return a.bin[1] < b.bin[1];
0167 };
0168
0169 auto [minX, maxX] = std::minmax_element(cluster.channels.begin(),
0170 cluster.channels.end(), compareX);
0171 auto [minY, maxY] = std::minmax_element(cluster.channels.begin(),
0172 cluster.channels.end(), compareY);
0173 cluster.sizeLoc0 = 1 + maxX->bin[0] - minX->bin[0];
0174 cluster.sizeLoc1 = 1 + maxY->bin[1] - minY->bin[1];
0175 }
0176
0177 clusters.push_back(cluster);
0178 }
0179 return clusters;
0180 }
0181
0182 }
0183
0184 ActsExamples::ProcessCode ActsExamples::CsvMeasurementReader::read(
0185 const ActsExamples::AlgorithmContext& ctx) {
0186
0187
0188
0189
0190
0191
0192
0193 auto measurementData =
0194 readMeasurementsByGeometryId(m_cfg.inputDir, ctx.eventNumber);
0195
0196
0197 GeometryIdMultimap<Measurement> orderedMeasurements;
0198 IndexMultimap<Index> measurementSimHitsMap;
0199 IndexSourceLinkContainer sourceLinks;
0200
0201 std::list<IndexSourceLink> sourceLinkStorage;
0202 orderedMeasurements.reserve(measurementData.size());
0203
0204 measurementSimHitsMap.reserve(measurementData.size());
0205 sourceLinks.reserve(measurementData.size());
0206
0207 auto measurementSimHitLinkData =
0208 readEverything<ActsExamples::MeasurementSimHitLink>(
0209 m_cfg.inputDir, "measurement-simhit-map.csv", {}, ctx.eventNumber);
0210 for (auto mshLink : measurementSimHitLinkData) {
0211 measurementSimHitsMap.emplace_hint(measurementSimHitsMap.end(),
0212 mshLink.measurement_id, mshLink.hit_id);
0213 }
0214
0215 for (const MeasurementData& m : measurementData) {
0216 Acts::GeometryIdentifier geoId = m.geometry_id;
0217
0218
0219 DigitizedParameters dParameters;
0220 for (unsigned int ipar = 0;
0221 ipar < static_cast<unsigned int>(Acts::eBoundSize); ++ipar) {
0222 if (((m.local_key) & (1 << (ipar + 1))) != 0) {
0223 dParameters.indices.push_back(static_cast<Acts::BoundIndices>(ipar));
0224 switch (ipar) {
0225 case static_cast<unsigned int>(Acts::eBoundLoc0): {
0226 dParameters.values.push_back(m.local0);
0227 dParameters.variances.push_back(m.var_local0);
0228 }; break;
0229 case static_cast<unsigned int>(Acts::eBoundLoc1): {
0230 dParameters.values.push_back(m.local1);
0231 dParameters.variances.push_back(m.var_local1);
0232 }; break;
0233 case static_cast<unsigned int>(Acts::eBoundPhi): {
0234 dParameters.values.push_back(m.phi);
0235 dParameters.variances.push_back(m.var_phi);
0236 }; break;
0237 case static_cast<unsigned int>(Acts::eBoundTheta): {
0238 dParameters.values.push_back(m.theta);
0239 dParameters.variances.push_back(m.var_theta);
0240 }; break;
0241 case static_cast<unsigned int>(Acts::eBoundTime): {
0242 dParameters.values.push_back(m.time);
0243 dParameters.variances.push_back(m.var_time);
0244 }; break;
0245 default:
0246 break;
0247 }
0248 }
0249 }
0250
0251
0252
0253 const Index index = orderedMeasurements.size();
0254 IndexSourceLink& sourceLink = sourceLinkStorage.emplace_back(geoId, index);
0255 auto measurement = createMeasurement(dParameters, sourceLink);
0256
0257
0258
0259
0260
0261 auto inserted = orderedMeasurements.emplace_hint(
0262 orderedMeasurements.end(), geoId, std::move(measurement));
0263 if (std::next(inserted) != orderedMeasurements.end()) {
0264 ACTS_FATAL("Something went horribly wrong with the hit sorting");
0265 return ProcessCode::ABORT;
0266 }
0267
0268 sourceLinks.insert(sourceLinks.end(), std::cref(sourceLink));
0269 }
0270
0271 MeasurementContainer measurements;
0272 for (auto& [_, meas] : orderedMeasurements) {
0273 measurements.emplace_back(std::move(meas));
0274 }
0275
0276
0277 if (m_inputHits.isInitialized() &&
0278 m_outputMeasurementParticlesMap.isInitialized()) {
0279 const auto hits = m_inputHits(ctx);
0280
0281 IndexMultimap<ActsFatras::Barcode> outputMap;
0282
0283 for (const auto& [measIdx, hitIdx] : measurementSimHitsMap) {
0284 const auto& hit = hits.nth(hitIdx);
0285 outputMap.emplace(measIdx, hit->particleId());
0286 }
0287
0288 m_outputMeasurementParticlesMap(ctx, std::move(outputMap));
0289 }
0290
0291
0292 m_outputMeasurements(ctx, std::move(measurements));
0293 m_outputMeasurementSimHitsMap(ctx, std::move(measurementSimHitsMap));
0294 m_outputSourceLinks(ctx, std::move(sourceLinks));
0295
0296
0297
0298
0299
0300 if (m_cfg.outputClusters.empty()) {
0301 return ActsExamples::ProcessCode::SUCCESS;
0302 }
0303
0304 std::vector<ActsExamples::CellData> cellData;
0305
0306
0307
0308 try {
0309 cellData = readEverything<ActsExamples::CellData>(
0310 m_cfg.inputDir, "cells.csv", {"timestamp"}, ctx.eventNumber);
0311 } catch (std::runtime_error& e) {
0312
0313 if (std::string(e.what()).find("Missing header column 'measurement_id'") ==
0314 std::string::npos) {
0315 throw;
0316 }
0317
0318 const auto oldCellData = readEverything<ActsExamples::CellDataLegacy>(
0319 m_cfg.inputDir, "cells.csv", {"timestamp"}, ctx.eventNumber);
0320
0321 auto fromLegacy = [](const CellDataLegacy& old) {
0322 return CellData{old.geometry_id, old.hit_id, old.channel0,
0323 old.channel1, old.timestamp, old.value};
0324 };
0325
0326 cellData.resize(oldCellData.size());
0327 std::transform(oldCellData.begin(), oldCellData.end(), cellData.begin(),
0328 fromLegacy);
0329 }
0330
0331 std::unordered_multimap<std::size_t, ActsExamples::CellData> cellDataMap;
0332 for (const auto& cd : cellData) {
0333 cellDataMap.emplace(cd.measurement_id, cd);
0334 }
0335
0336 auto clusters = makeClusters(cellDataMap, orderedMeasurements.size());
0337 m_outputClusters(ctx, std::move(clusters));
0338
0339 return ActsExamples::ProcessCode::SUCCESS;
0340 }