File indexing completed on 2026-05-30 07:59:25
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "ActsExamples/Io/Root/RootAthenaDumpWriter.hpp"
0010
0011 #include "Acts/Definitions/Units.hpp"
0012 #include "Acts/Utilities/VectorHelpers.hpp"
0013 #include "ActsExamples/EventData/IndexSourceLink.hpp"
0014 #include "ActsExamples/Framework/AlgorithmContext.hpp"
0015
0016 #include <ios>
0017 #include <stdexcept>
0018 #include <unordered_map>
0019
0020 #include <TFile.h>
0021 #include <TTree.h>
0022
0023 namespace ActsExamples {
0024
0025 RootAthenaDumpWriter::RootAthenaDumpWriter(const Config& config,
0026 Acts::Logging::Level level)
0027 : IWriter(),
0028 m_cfg(config),
0029 m_logger(Acts::getDefaultLogger(name(), level)) {
0030 if (m_cfg.filePath.empty()) {
0031 throw std::invalid_argument("Missing file path");
0032 }
0033 if (m_cfg.treeName.empty()) {
0034 throw std::invalid_argument("Missing tree name");
0035 }
0036
0037 m_inputParticles.initialize(m_cfg.inputParticles);
0038 m_inputClusters.initialize(m_cfg.inputClusters);
0039 m_inputMeasurements.initialize(m_cfg.inputMeasurements);
0040 m_inputMeasParticleMap.initialize(m_cfg.inputMeasParticleMap);
0041 m_inputSpacePoints.initialize(m_cfg.inputSpacePoints);
0042
0043 m_outputFile = TFile::Open(m_cfg.filePath.c_str(), "RECREATE");
0044 if (m_outputFile == nullptr) {
0045 throw std::ios_base::failure("Could not open '" + m_cfg.filePath + "'");
0046 }
0047 m_outputFile->cd();
0048 m_outputTree = new TTree(m_cfg.treeName.c_str(), m_cfg.treeName.c_str());
0049 if (m_outputTree == nullptr) {
0050 throw std::bad_alloc();
0051 }
0052
0053
0054
0055
0056 m_partEventNumber.reserve(s_maxParticles);
0057 m_partBarcode.reserve(s_maxParticles);
0058 m_partPt.reserve(s_maxParticles);
0059 m_partEta.reserve(s_maxParticles);
0060 m_partPdgId.reserve(s_maxParticles);
0061 m_partVx.reserve(s_maxParticles);
0062 m_partVy.reserve(s_maxParticles);
0063 m_partVz.reserve(s_maxParticles);
0064
0065 m_clIndex.reserve(s_maxClusters);
0066 m_clBarrelEndcap.reserve(s_maxClusters);
0067 m_clEtaModule.reserve(s_maxClusters);
0068 m_clPhiModule.reserve(s_maxClusters);
0069 m_clModuleId.reserve(s_maxClusters);
0070 m_clX.reserve(s_maxClusters);
0071 m_clY.reserve(s_maxClusters);
0072 m_clZ.reserve(s_maxClusters);
0073
0074 m_spIndex.reserve(s_maxSpacePoints);
0075 m_spX.reserve(s_maxSpacePoints);
0076 m_spY.reserve(s_maxSpacePoints);
0077 m_spZ.reserve(s_maxSpacePoints);
0078 m_spCL1Index.reserve(s_maxSpacePoints);
0079 m_spCL2Index.reserve(s_maxSpacePoints);
0080 m_spIsOverlap.reserve(s_maxSpacePoints);
0081
0082
0083 m_outputTree->Branch("event_number", &m_eventNumber);
0084
0085
0086 m_outputTree->Branch("nPartEVT", &m_nPartEVT, "nPartEVT/I");
0087 m_outputTree->Branch("Part_event_number", m_partEventNumber.data(),
0088 "Part_event_number[nPartEVT]/I");
0089 m_outputTree->Branch("Part_barcode", m_partBarcode.data(),
0090 "Part_barcode[nPartEVT]/I");
0091 m_outputTree->Branch("Part_pt", m_partPt.data(), "Part_pt[nPartEVT]/F");
0092 m_outputTree->Branch("Part_eta", m_partEta.data(), "Part_eta[nPartEVT]/F");
0093 m_outputTree->Branch("Part_pdg_id", m_partPdgId.data(),
0094 "Part_pdg_id[nPartEVT]/I");
0095 m_outputTree->Branch("Part_vx", m_partVx.data(), "Part_vx[nPartEVT]/F");
0096 m_outputTree->Branch("Part_vy", m_partVy.data(), "Part_vy[nPartEVT]/F");
0097 m_outputTree->Branch("Part_vz", m_partVz.data(), "Part_vz[nPartEVT]/F");
0098
0099
0100 m_outputTree->Branch("nCL", &m_nCL, "nCL/I");
0101 m_outputTree->Branch("CLindex", m_clIndex.data(), "CLindex[nCL]/I");
0102 m_outputTree->Branch("CLhardware", &m_clHardware);
0103 m_outputTree->Branch("CLbarrel_endcap", m_clBarrelEndcap.data(),
0104 "CLbarrel_endcap[nCL]/I");
0105 m_outputTree->Branch("CLeta_module", m_clEtaModule.data(),
0106 "CLeta_module[nCL]/I");
0107 m_outputTree->Branch("CLphi_module", m_clPhiModule.data(),
0108 "CLphi_module[nCL]/I");
0109 m_outputTree->Branch("CLmoduleID", m_clModuleId.data(), "CLmoduleID[nCL]/l");
0110 m_outputTree->Branch("CLx", m_clX.data(), "CLx[nCL]/D");
0111 m_outputTree->Branch("CLy", m_clY.data(), "CLy[nCL]/D");
0112 m_outputTree->Branch("CLz", m_clZ.data(), "CLz[nCL]/D");
0113 m_outputTree->Branch("CLlocal_cov", &m_clLocalCov);
0114 m_outputTree->Branch("CLparticleLink_eventIndex",
0115 &m_clParticleLinkEventIndex);
0116 m_outputTree->Branch("CLparticleLink_barcode", &m_clParticleLinkBarcode);
0117
0118
0119 m_outputTree->Branch("nSP", &m_nSP, "nSP/I");
0120 m_outputTree->Branch("SPindex", m_spIndex.data(), "SPindex[nSP]/I");
0121 m_outputTree->Branch("SPx", m_spX.data(), "SPx[nSP]/D");
0122 m_outputTree->Branch("SPy", m_spY.data(), "SPy[nSP]/D");
0123 m_outputTree->Branch("SPz", m_spZ.data(), "SPz[nSP]/D");
0124 m_outputTree->Branch("SPCL1_index", m_spCL1Index.data(),
0125 "SPCL1_index[nSP]/I");
0126 m_outputTree->Branch("SPCL2_index", m_spCL2Index.data(),
0127 "SPCL2_index[nSP]/I");
0128 m_outputTree->Branch("SPisOverlap", m_spIsOverlap.data(),
0129 "SPisOverlap[nSP]/I");
0130 }
0131
0132 RootAthenaDumpWriter::~RootAthenaDumpWriter() {
0133 if (m_outputFile != nullptr) {
0134 m_outputFile->Close();
0135 }
0136 }
0137
0138 ProcessCode RootAthenaDumpWriter::finalize() {
0139 m_outputFile->cd();
0140 m_outputTree->Write();
0141 m_outputFile->Close();
0142 ACTS_VERBOSE("Wrote Athena dump to '" << m_cfg.filePath << "'");
0143 return ProcessCode::SUCCESS;
0144 }
0145
0146 ProcessCode RootAthenaDumpWriter::write(const AlgorithmContext& ctx) {
0147 const auto& particles = m_inputParticles(ctx);
0148 const auto& clusters = m_inputClusters(ctx);
0149 const auto& measurements = m_inputMeasurements(ctx);
0150 const auto& measPartMap = m_inputMeasParticleMap(ctx);
0151 const auto& spacePoints = m_inputSpacePoints(ctx);
0152
0153 std::lock_guard<std::mutex> lock(m_writeMutex);
0154
0155 if (particles.size() > s_maxParticles) {
0156 throw std::runtime_error(
0157 "RootAthenaDumpWriter: particle count exceeds maxParticles (" +
0158 std::to_string(particles.size()) + " > " +
0159 std::to_string(s_maxParticles) + ")");
0160 }
0161 if (measurements.size() > s_maxClusters) {
0162 throw std::runtime_error(
0163 "RootAthenaDumpWriter: measurement count exceeds maxClusters (" +
0164 std::to_string(measurements.size()) + " > " +
0165 std::to_string(s_maxClusters) + ")");
0166 }
0167 if (spacePoints.size() > s_maxSpacePoints) {
0168 throw std::runtime_error(
0169 "RootAthenaDumpWriter: space point count exceeds maxSpacePoints (" +
0170 std::to_string(spacePoints.size()) + " > " +
0171 std::to_string(s_maxSpacePoints) + ")");
0172 }
0173
0174 m_eventNumber = ctx.eventNumber;
0175
0176
0177
0178
0179
0180
0181 std::unordered_map<ActsFatras::Barcode, int> barcodeMap;
0182 barcodeMap.reserve(particles.size());
0183 int primaryCount = 1;
0184 int secondaryCount = s_maxBarcodeForPrimary + 1;
0185 for (const auto& particle : particles) {
0186 const bool isPrimary = particle.particleId().vertexSecondary() == 0;
0187 barcodeMap[particle.particleId()] =
0188 isPrimary ? primaryCount++ : secondaryCount++;
0189 }
0190
0191
0192 m_partEventNumber.clear();
0193 m_partBarcode.clear();
0194 m_partPt.clear();
0195 m_partEta.clear();
0196 m_partPdgId.clear();
0197 m_partVx.clear();
0198 m_partVy.clear();
0199 m_partVz.clear();
0200
0201 for (const auto& particle : particles) {
0202 const float pT = static_cast<float>(particle.transverseMomentum() /
0203 Acts::UnitConstants::MeV);
0204 const float eta = static_cast<float>(
0205 Acts::VectorHelpers::eta(particle.momentum().normalized()));
0206
0207 m_partEventNumber.push_back(
0208 static_cast<int>(particle.particleId().vertexPrimary()));
0209 m_partBarcode.push_back(barcodeMap.at(particle.particleId()));
0210 m_partPt.push_back(pT);
0211 m_partEta.push_back(eta);
0212 m_partPdgId.push_back(static_cast<int>(particle.pdg()));
0213 m_partVx.push_back(
0214 static_cast<float>(particle.position().x() / Acts::UnitConstants::mm));
0215 m_partVy.push_back(
0216 static_cast<float>(particle.position().y() / Acts::UnitConstants::mm));
0217 m_partVz.push_back(
0218 static_cast<float>(particle.position().z() / Acts::UnitConstants::mm));
0219 }
0220 m_nPartEVT = static_cast<int>(m_partBarcode.size());
0221
0222
0223 m_clIndex.clear();
0224 m_clHardware.clear();
0225 m_clBarrelEndcap.clear();
0226 m_clEtaModule.clear();
0227 m_clPhiModule.clear();
0228 m_clModuleId.clear();
0229 m_clX.clear();
0230 m_clY.clear();
0231 m_clZ.clear();
0232 m_clLocalCov.clear();
0233 m_clParticleLinkEventIndex.clear();
0234 m_clParticleLinkBarcode.clear();
0235
0236 for (std::size_t im = 0; im < measurements.size(); ++im) {
0237 const auto meas = measurements.at(im);
0238
0239 const bool isPixel = (meas.size() == 2);
0240
0241 m_clIndex.push_back(static_cast<int>(im));
0242 m_clHardware.push_back(isPixel ? "PIXEL" : "STRIP");
0243 m_clModuleId.push_back(meas.geometryId().value());
0244
0245 m_clBarrelEndcap.push_back(s_sentinel);
0246 m_clEtaModule.push_back(s_sentinel);
0247 m_clPhiModule.push_back(s_sentinel);
0248
0249 const Acts::Vector3& pos = clusters.at(im).globalPosition;
0250 m_clX.push_back(pos.x());
0251 m_clY.push_back(pos.y());
0252 m_clZ.push_back(pos.z());
0253
0254 if (isPixel) {
0255 m_clLocalCov.push_back({meas.covariance()(0, 0), meas.covariance()(0, 1),
0256 meas.covariance()(1, 0),
0257 meas.covariance()(1, 1)});
0258 } else {
0259 const double var = meas.covariance()(0, 0);
0260 m_clLocalCov.push_back({var, var});
0261 }
0262
0263 std::vector<int> eventIndices;
0264 std::vector<int> barcodes;
0265 const auto [begin, end] = measPartMap.equal_range(im);
0266 for (auto it = begin; it != end; ++it) {
0267 const auto& bc = it->second;
0268 eventIndices.push_back(s_sentinel);
0269 const auto bcIt = barcodeMap.find(bc);
0270 barcodes.push_back(bcIt != barcodeMap.end() ? bcIt->second : 0);
0271 }
0272 m_clParticleLinkEventIndex.push_back(std::move(eventIndices));
0273 m_clParticleLinkBarcode.push_back(std::move(barcodes));
0274 }
0275 m_nCL = static_cast<int>(m_clIndex.size());
0276
0277
0278 m_spIndex.clear();
0279 m_spX.clear();
0280 m_spY.clear();
0281 m_spZ.clear();
0282 m_spCL1Index.clear();
0283 m_spCL2Index.clear();
0284 m_spIsOverlap.clear();
0285
0286 for (const auto& sp : spacePoints) {
0287 const auto sLinks = sp.sourceLinks();
0288 if (sLinks.empty()) {
0289 ACTS_WARNING("Space point with no source links, skipping");
0290 continue;
0291 }
0292
0293 m_spIndex.push_back(static_cast<int>(m_spIndex.size()));
0294 m_spX.push_back(static_cast<double>(sp.x()));
0295 m_spY.push_back(static_cast<double>(sp.y()));
0296 m_spZ.push_back(static_cast<double>(sp.z()));
0297
0298 m_spCL1Index.push_back(
0299 static_cast<int>(sLinks[0].get<IndexSourceLink>().index()));
0300 m_spCL2Index.push_back(
0301 sLinks.size() > 1
0302 ? static_cast<int>(sLinks[1].get<IndexSourceLink>().index())
0303 : -1);
0304
0305
0306 m_spIsOverlap.push_back(0);
0307 }
0308 m_nSP = static_cast<int>(m_spIndex.size());
0309
0310 m_outputTree->Fill();
0311
0312 ACTS_DEBUG("Wrote event " << m_eventNumber << " with " << m_nPartEVT
0313 << " particles, " << m_nCL << " clusters, and "
0314 << m_nSP << " space points");
0315
0316 return ProcessCode::SUCCESS;
0317 }
0318
0319 }