File indexing completed on 2026-06-20 07:36:39
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "ActsExamples/Io/Root/RootSpacePointPerformanceWriter.hpp"
0010
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "ActsExamples/EventData/AverageSimHits.hpp"
0013 #include "ActsExamples/EventData/IndexSourceLink.hpp"
0014 #include "ActsExamples/EventData/SimParticle.hpp"
0015 #include "ActsExamples/EventData/SpacePoint.hpp"
0016 #include "ActsExamples/Framework/AlgorithmContext.hpp"
0017 #include "ActsExamples/Utilities/StripModulePairing.hpp"
0018 #include "ActsPlugins/Root/HistogramConverter.hpp"
0019
0020 #include <ios>
0021 #include <ranges>
0022 #include <stdexcept>
0023
0024 #include <TEfficiency.h>
0025 #include <TFile.h>
0026 #include <TH1.h>
0027 #include <TTree.h>
0028
0029 namespace ActsExamples {
0030
0031 namespace {
0032
0033 template <typename Begin, typename End>
0034 auto toRange(const std::pair<Begin, End>& pair) {
0035 return std::ranges::subrange(pair.first, pair.second);
0036 }
0037
0038 template <typename Range1, typename Range2>
0039 bool hasCommon(Range1&& a, Range2&& b) {
0040 for (const auto& elementA : a) {
0041 for (const auto& elementB : b) {
0042 if (elementA == elementB) {
0043 return true;
0044 }
0045 }
0046 }
0047 return false;
0048 }
0049
0050 }
0051
0052 RootSpacePointPerformanceWriter::RootSpacePointPerformanceWriter(
0053 const Config& config, Acts::Logging::Level level)
0054 : WriterT(config.inputSpacePoints, "RootSpacePointPerformanceWriter",
0055 level),
0056 m_cfg(config) {
0057
0058 if (m_cfg.inputParticles.empty()) {
0059 throw std::invalid_argument("Missing particles input collection");
0060 }
0061 if (m_cfg.inputMeasurements.empty()) {
0062 throw std::invalid_argument("Missing measurements input collection");
0063 }
0064 if (m_cfg.inputSimHits.empty()) {
0065 throw std::invalid_argument("Missing sim hits input collection");
0066 }
0067 if (m_cfg.inputMeasurementSimHitsMap.empty()) {
0068 throw std::invalid_argument(
0069 "Missing measurement-to-hits map input collection");
0070 }
0071 if (m_cfg.inputMeasurementParticlesMap.empty()) {
0072 throw std::invalid_argument(
0073 "Missing measurement-to-particles map input collection");
0074 }
0075 if (m_cfg.trackingGeometry == nullptr) {
0076 throw std::invalid_argument("Missing tracking geometry");
0077 }
0078
0079 m_inputParticles.initialize(m_cfg.inputParticles);
0080 m_inputMeasurements.initialize(m_cfg.inputMeasurements);
0081 m_inputSimHits.initialize(m_cfg.inputSimHits);
0082 m_inputMeasurementSimHitsMap.initialize(m_cfg.inputMeasurementSimHitsMap);
0083 m_inputMeasurementParticlesMap.initialize(m_cfg.inputMeasurementParticlesMap);
0084
0085 m_stripModulePairMap = pairStripModules(
0086 *m_cfg.trackingGeometry, m_cfg.stripGeometrySelection, logger());
0087
0088
0089 m_outputFile = TFile::Open(m_cfg.filePath.c_str(), m_cfg.fileMode.c_str());
0090 if (m_outputFile == nullptr) {
0091 throw std::ios_base::failure("Could not open '" + m_cfg.filePath + "'");
0092 }
0093
0094 m_outputFile->cd();
0095
0096 m_fakeVsZ.emplace("fake_vs_z", "Space point fake ratio vs z",
0097 std::array{m_cfg.zAxis});
0098 m_fakeVsR.emplace("fake_vs_r", "Space point fake ratio vs r",
0099 std::array{m_cfg.rAxis});
0100 m_fakeVsEta.emplace("fake_vs_eta", "Space point fake ratio vs eta",
0101 std::array{m_cfg.etaAxis});
0102 m_fakeVsPhi.emplace("fake_vs_phi", "Space point fake ratio vs phi",
0103 std::array{m_cfg.phiAxis});
0104
0105 m_effVsZ.emplace("efficiency_vs_z", "Space point efficiency vs z",
0106 std::array{m_cfg.zAxis});
0107 m_effVsR.emplace("efficiency_vs_r", "Space point efficiency vs r",
0108 std::array{m_cfg.rAxis});
0109 m_effVsEta.emplace("efficiency_vs_eta", "Space point efficiency vs eta",
0110 std::array{m_cfg.etaAxis});
0111 m_effVsPhi.emplace("efficiency_vs_phi", "Space point efficiency vs phi",
0112 std::array{m_cfg.phiAxis});
0113 }
0114
0115 RootSpacePointPerformanceWriter::~RootSpacePointPerformanceWriter() {
0116 if (m_outputFile != nullptr) {
0117 m_outputFile->Close();
0118 }
0119 }
0120
0121 ProcessCode RootSpacePointPerformanceWriter::finalize() {
0122
0123 m_outputFile->cd();
0124
0125 ActsPlugins::toRoot(*m_fakeVsZ)->Write();
0126 ActsPlugins::toRoot(*m_fakeVsR)->Write();
0127 ActsPlugins::toRoot(*m_fakeVsEta)->Write();
0128 ActsPlugins::toRoot(*m_fakeVsPhi)->Write();
0129
0130 ActsPlugins::toRoot(*m_effVsZ)->Write();
0131 ActsPlugins::toRoot(*m_effVsR)->Write();
0132 ActsPlugins::toRoot(*m_effVsEta)->Write();
0133 ActsPlugins::toRoot(*m_effVsPhi)->Write();
0134
0135 m_outputFile->Close();
0136
0137 return ProcessCode::SUCCESS;
0138 }
0139
0140 ProcessCode RootSpacePointPerformanceWriter::writeT(
0141 const AlgorithmContext& ctx, const SpacePointContainer& spacePoints) {
0142 const auto& particles = m_inputParticles(ctx);
0143 const auto& measurements = m_inputMeasurements(ctx);
0144 const auto& simHits = m_inputSimHits(ctx);
0145 const auto& measurementSimHitsMap = m_inputMeasurementSimHitsMap(ctx);
0146 const auto& measurementParticlesMap = m_inputMeasurementParticlesMap(ctx);
0147
0148 const auto transformSecond =
0149 std::views::transform([](auto& p) -> auto& { return p.second; });
0150 const auto filterParticles = std::views::filter(
0151 [&](const SimBarcode& particle) { return particles.contains(particle); });
0152
0153 std::lock_guard<std::mutex> lock(m_writeMutex);
0154
0155 std::map<std::pair<Index, Index>, SpacePointIndex> trueSpacePoints;
0156
0157 for (const auto& spacePoint : spacePoints) {
0158 const Acts::Vector3 position(spacePoint.x(), spacePoint.y(),
0159 spacePoint.z());
0160 const double z = position.z();
0161 const double r = Acts::VectorHelpers::perp(position);
0162 const double phi = Acts::VectorHelpers::phi(position);
0163 const double eta = Acts::VectorHelpers::eta(position);
0164
0165 const auto& sourceLinks = spacePoint.sourceLinks();
0166
0167 bool isFake = false;
0168
0169 if (sourceLinks.size() == 2) {
0170 const auto measurementIndex1 =
0171 sourceLinks[0].get<IndexSourceLink>().index();
0172 const auto measurementIndex2 =
0173 sourceLinks[1].get<IndexSourceLink>().index();
0174
0175 auto contributingParticles1 =
0176 toRange(measurementParticlesMap.equal_range(measurementIndex1)) |
0177 transformSecond | filterParticles;
0178 auto contributingParticles2 =
0179 toRange(measurementParticlesMap.equal_range(measurementIndex2)) |
0180 transformSecond | filterParticles;
0181
0182 isFake = !hasCommon(contributingParticles1, contributingParticles2);
0183
0184 if (!isFake) {
0185 trueSpacePoints.emplace(
0186 std::pair<Index, Index>{measurementIndex1, measurementIndex2},
0187 spacePoint.index());
0188 }
0189 }
0190
0191 m_fakeVsZ->fill({z}, isFake);
0192 m_fakeVsR->fill({r}, isFake);
0193 m_fakeVsEta->fill({eta}, isFake);
0194 m_fakeVsPhi->fill({phi}, isFake);
0195 }
0196
0197 for (const auto [module1, module2] : m_stripModulePairMap) {
0198 const Acts::Surface* surface1 =
0199 m_cfg.trackingGeometry->findSurface(module1);
0200 const Acts::Surface* surface2 =
0201 m_cfg.trackingGeometry->findSurface(module2);
0202
0203 if (surface1 == nullptr || surface2 == nullptr) {
0204 ACTS_WARNING("Could not find surfaces for modules " << module1 << " and "
0205 << module2);
0206 continue;
0207 }
0208
0209 const auto sourceLinks1 =
0210 measurements.orderedIndices().equal_range(module1);
0211 const auto sourceLinks2 =
0212 measurements.orderedIndices().equal_range(module2);
0213
0214 for (const auto& sourceLink1 : toRange(sourceLinks1)) {
0215 const auto measurementIndex1 = sourceLink1.index();
0216 auto contributingParticles1 =
0217 toRange(measurementParticlesMap.equal_range(measurementIndex1)) |
0218 transformSecond | filterParticles;
0219
0220 for (const auto& sourceLink2 : toRange(sourceLinks2)) {
0221 const auto measurementIndex2 = sourceLink2.index();
0222 auto contributingParticles2 =
0223 toRange(measurementParticlesMap.equal_range(measurementIndex2)) |
0224 transformSecond | filterParticles;
0225
0226 if (!hasCommon(contributingParticles1, contributingParticles2)) {
0227 continue;
0228 }
0229
0230
0231 const auto hitRange1 =
0232 makeRange(measurementSimHitsMap.equal_range(measurementIndex1));
0233
0234 const auto [local1, position1, dir1] = averageSimHits(
0235 ctx.geoContext, *surface1, simHits, hitRange1, logger());
0236
0237 const double z = position1.z();
0238 const double r = Acts::VectorHelpers::perp(position1);
0239 const double phi = Acts::VectorHelpers::phi(position1);
0240 const double eta = Acts::VectorHelpers::eta(position1.head<3>());
0241
0242 const bool isReconstructed =
0243 trueSpacePoints.contains(std::pair<Index, Index>{
0244 measurementIndex1, measurementIndex2}) ||
0245 trueSpacePoints.contains(
0246 std::pair<Index, Index>{measurementIndex2, measurementIndex1});
0247
0248 m_effVsZ->fill({z}, isReconstructed);
0249 m_effVsR->fill({r}, isReconstructed);
0250 m_effVsEta->fill({eta}, isReconstructed);
0251 m_effVsPhi->fill({phi}, isReconstructed);
0252 }
0253 }
0254 }
0255
0256 return ProcessCode::SUCCESS;
0257 }
0258
0259 }