File indexing completed on 2025-09-17 08:03:05
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "ActsExamples/Io/Root/RootMeasurementWriter.hpp"
0010
0011 #include "Acts/Definitions/TrackParametrization.hpp"
0012 #include "Acts/Plugins/Root/RootMeasurementIo.hpp"
0013 #include "ActsExamples/EventData/AverageSimHits.hpp"
0014 #include "ActsExamples/EventData/Index.hpp"
0015 #include "ActsExamples/EventData/Measurement.hpp"
0016 #include "ActsExamples/Framework/AlgorithmContext.hpp"
0017 #include "ActsExamples/Utilities/Range.hpp"
0018
0019 #include <ios>
0020 #include <limits>
0021 #include <memory>
0022 #include <stdexcept>
0023 #include <utility>
0024
0025 #include <TFile.h>
0026 #include <TTree.h>
0027
0028 namespace {
0029
0030 std::tuple<std::vector<double>, std::vector<double>, std::vector<unsigned int>>
0031 prepareBoundMeasurement(
0032 const ActsExamples::ConstVariableBoundMeasurementProxy& m) {
0033 std::vector<double> measurements = {};
0034 std::vector<double> variances = {};
0035 std::vector<unsigned int> subspaceIndex = {};
0036
0037 for (unsigned int i = 0; i < m.size(); ++i) {
0038 auto ib = m.subspaceIndexVector()[i];
0039
0040 measurements.push_back(m.parameters()[i]);
0041 variances.push_back(m.covariance()(i, i));
0042 subspaceIndex.push_back(static_cast<unsigned int>(ib));
0043 }
0044
0045 return {measurements, variances, subspaceIndex};
0046 }
0047
0048 std::vector<std::tuple<int, int, float>> prepareChannels(
0049 const ActsExamples::Cluster& c) {
0050 std::vector<std::tuple<int, int, float>> channels = {};
0051 for (auto ch : c.channels) {
0052 channels.emplace_back(static_cast<int>(ch.bin[0]),
0053 static_cast<int>(ch.bin[1]),
0054 static_cast<float>(ch.activation));
0055 }
0056 return channels;
0057 }
0058
0059 }
0060
0061 namespace ActsExamples {
0062
0063 RootMeasurementWriter::RootMeasurementWriter(
0064 const RootMeasurementWriter::Config& config, Acts::Logging::Level level)
0065 : WriterT(config.inputMeasurements, "RootMeasurementWriter", level),
0066 m_cfg(config) {
0067
0068 if (m_cfg.inputSimHits.empty()) {
0069 throw std::invalid_argument("Missing simulated hits input collection");
0070 }
0071 if (m_cfg.inputMeasurementSimHitsMap.empty()) {
0072 throw std::invalid_argument(
0073 "Missing hit-to-simulated-hits map input collection");
0074 }
0075
0076 m_inputClusters.maybeInitialize(m_cfg.inputClusters);
0077 m_inputSimHits.initialize(m_cfg.inputSimHits);
0078 m_inputMeasurementSimHitsMap.initialize(m_cfg.inputMeasurementSimHitsMap);
0079
0080 if (m_cfg.surfaceByIdentifier.empty()) {
0081 throw std::invalid_argument("Missing Surface-GeoID association map");
0082 }
0083
0084 m_outputFile = TFile::Open(m_cfg.filePath.c_str(), m_cfg.fileMode.c_str());
0085 if (m_outputFile == nullptr) {
0086 throw std::ios_base::failure("Could not open '" + m_cfg.filePath + "'");
0087 }
0088
0089 m_outputFile->cd();
0090 m_outputTree = new TTree(m_cfg.treeName.c_str(), "Measurements");
0091 m_outputTree->Branch("particles", &m_particles);
0092
0093 Acts::RootMeasurementIo::Config treeCfg{m_cfg.boundIndices,
0094 m_cfg.clusterIndices};
0095 m_measurementIo = std::make_unique<Acts::RootMeasurementIo>(treeCfg);
0096 m_measurementIo->connectForWrite(*m_outputTree);
0097 }
0098
0099 RootMeasurementWriter::~RootMeasurementWriter() {
0100 if (m_outputFile != nullptr) {
0101 m_outputFile->Close();
0102 }
0103 }
0104
0105 ProcessCode RootMeasurementWriter::finalize() {
0106
0107 m_outputFile->cd();
0108 m_outputTree->Write();
0109 m_outputFile->Close();
0110
0111 return ProcessCode::SUCCESS;
0112 }
0113
0114 ProcessCode RootMeasurementWriter::writeT(
0115 const AlgorithmContext& ctx, const MeasurementContainer& measurements) {
0116 const auto& simHits = m_inputSimHits(ctx);
0117 const auto& hitSimHitsMap = m_inputMeasurementSimHitsMap(ctx);
0118
0119 const ClusterContainer* clusters = nullptr;
0120 if (!m_cfg.inputClusters.empty()) {
0121 clusters = &m_inputClusters(ctx);
0122 }
0123
0124
0125 std::lock_guard<std::mutex> lock(m_writeMutex);
0126
0127 for (Index hitIdx = 0u; hitIdx < measurements.size(); ++hitIdx) {
0128 const ConstVariableBoundMeasurementProxy meas =
0129 measurements.getMeasurement(hitIdx);
0130
0131 Acts::GeometryIdentifier geoId = meas.geometryId();
0132
0133 auto surfaceItr = m_cfg.surfaceByIdentifier.find(geoId);
0134 if (surfaceItr == m_cfg.surfaceByIdentifier.end()) {
0135 continue;
0136 }
0137 const Acts::Surface& surface = *(surfaceItr->second);
0138
0139
0140 m_measurementIo->fillIdentification(static_cast<int>(ctx.eventNumber),
0141 geoId);
0142
0143
0144 auto indices = makeRange(hitSimHitsMap.equal_range(hitIdx));
0145
0146 auto [local, pos4, dir] =
0147 averageSimHits(ctx.geoContext, surface, simHits, indices, logger());
0148 Acts::RotationMatrix3 rot =
0149 surface
0150 .referenceFrame(ctx.geoContext, pos4.segment<3>(Acts::ePos0), dir)
0151 .inverse();
0152 std::pair<double, double> angles =
0153 Acts::VectorHelpers::incidentAngles(dir, rot);
0154 for (auto [_, i] : indices) {
0155 m_particles.push_back(simHits.nth(i)->particleId().value());
0156 }
0157 m_measurementIo->fillTruthParameters(local, pos4, dir, angles);
0158
0159
0160 auto [msm, vcs, ssi] = prepareBoundMeasurement(meas);
0161 m_measurementIo->fillBoundMeasurement(msm, vcs, ssi);
0162
0163
0164 if (clusters != nullptr) {
0165 const auto& cluster = (*clusters)[hitIdx];
0166 m_measurementIo->fillGlobalPosition(cluster.globalPosition);
0167 m_measurementIo->fillCluster(prepareChannels(cluster));
0168 }
0169
0170 m_outputTree->Fill();
0171 m_particles.clear();
0172 m_measurementIo->clear();
0173 }
0174
0175 return ProcessCode::SUCCESS;
0176 }
0177
0178 }