Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-07 07:59:54

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include "Acts/Utilities/Logger.hpp"
0010 #include "ActsExamples/EventData/Index.hpp"
0011 #include "ActsExamples/EventData/SimParticle.hpp"
0012 #include "ActsExamples/EventData/Track.hpp"
0013 #include "ActsExamples/EventData/TruthMatching.hpp"
0014 #include "ActsExamples/Framework/DataHandle.hpp"
0015 #include "ActsExamples/Framework/IWriter.hpp"
0016 #include "ActsExamples/Framework/ProcessCode.hpp"
0017 #include "ActsExamples/Framework/WriterT.hpp"
0018 #include "ActsExamples/Validation/TrackFinderPerformanceCollector.hpp"
0019 #include "ActsPython/Utilities/Macros.hpp"
0020 
0021 #include <mutex>
0022 #include <stdexcept>
0023 #include <string>
0024 
0025 #include <pybind11/pybind11.h>
0026 #include <pybind11/stl.h>
0027 
0028 namespace py = pybind11;
0029 using namespace pybind11::literals;
0030 
0031 using namespace Acts;
0032 using namespace ActsExamples;
0033 
0034 namespace {
0035 
0036 /// A ROOT-free writer that collects track-finder performance histograms and
0037 /// exposes them to Python via histograms() after s.run().
0038 class PythonTrackFinderPerformanceWriter final
0039     : public WriterT<ConstTrackContainer> {
0040  public:
0041   struct Config {
0042     /// Input (found) tracks collection.
0043     std::string inputTracks;
0044     /// Input particles collection.
0045     std::string inputParticles;
0046     /// Input track-particle matching.
0047     std::string inputTrackParticleMatching;
0048     /// Input track-particle matching.
0049     std::string inputParticleTrackMatching;
0050     /// Input particle measurements map.
0051     std::string inputParticleMeasurementsMap;
0052     /// Plot tool configurations (inlined from TrackFinderPerformanceCollector).
0053     EffPlotTool::Config effPlotToolConfig;
0054     FakePlotTool::Config fakePlotToolConfig;
0055     DuplicationPlotTool::Config duplicationPlotToolConfig;
0056     TrackSummaryPlotTool::Config trackSummaryPlotToolConfig;
0057     TrackQualityPlotTool::Config trackQualityPlotToolConfig;
0058     /// Optional per-subdetector track summary plots.
0059     std::map<std::string, std::set<int>> subDetectorTrackSummaryVolumes;
0060   };
0061 
0062   PythonTrackFinderPerformanceWriter(Config cfg, Acts::Logging::Level lvl)
0063       : WriterT(cfg.inputTracks, "PythonTrackFinderPerformanceWriter", lvl),
0064         m_cfg(std::move(cfg)),
0065         m_collector(
0066             TrackFinderPerformanceCollector::Config{
0067                 m_cfg.effPlotToolConfig, m_cfg.fakePlotToolConfig,
0068                 m_cfg.duplicationPlotToolConfig,
0069                 m_cfg.trackSummaryPlotToolConfig,
0070                 m_cfg.trackQualityPlotToolConfig,
0071                 m_cfg.subDetectorTrackSummaryVolumes},
0072             logger().clone()) {
0073     if (m_cfg.inputParticles.empty()) {
0074       throw std::invalid_argument("Missing particles input collection");
0075     }
0076     if (m_cfg.inputTrackParticleMatching.empty()) {
0077       throw std::invalid_argument("Missing input track particles matching");
0078     }
0079     if (m_cfg.inputParticleTrackMatching.empty()) {
0080       throw std::invalid_argument("Missing input particle track matching");
0081     }
0082     if (m_cfg.inputParticleMeasurementsMap.empty()) {
0083       throw std::invalid_argument("Missing input measurement particles map");
0084     }
0085 
0086     m_inputParticles.initialize(m_cfg.inputParticles);
0087     m_inputTrackParticleMatching.initialize(m_cfg.inputTrackParticleMatching);
0088     m_inputParticleTrackMatching.initialize(m_cfg.inputParticleTrackMatching);
0089     m_inputParticleMeasurementsMap.initialize(
0090         m_cfg.inputParticleMeasurementsMap);
0091   }
0092 
0093   ProcessCode finalize() override {
0094     m_collector.logSummary();
0095     return ProcessCode::SUCCESS;
0096   }
0097 
0098   const Config& config() const { return m_cfg; }
0099 
0100   /// Return all filled histograms as a Python dict keyed by histogram name.
0101   py::dict histograms() const {
0102     py::dict d;
0103     const auto& coll = m_collector;
0104 
0105     for (const auto& [name, eff] : coll.effPlotTool().efficiencies1D()) {
0106       d[py::str(name)] = py::cast(eff, py::return_value_policy::copy);
0107     }
0108     for (const auto& [name, eff] : coll.effPlotTool().efficiencies2D()) {
0109       d[py::str(name)] = py::cast(eff, py::return_value_policy::copy);
0110     }
0111     for (const auto& eff : coll.effPlotTool().trackEffVsEtaInPtRanges()) {
0112       d[py::str(eff.name())] = py::cast(eff, py::return_value_policy::copy);
0113     }
0114     for (const auto& eff : coll.effPlotTool().trackEffVsPtInAbsEtaRanges()) {
0115       d[py::str(eff.name())] = py::cast(eff, py::return_value_policy::copy);
0116     }
0117 
0118     for (const auto& [name, hist] : coll.fakePlotTool().histograms()) {
0119       d[py::str(name)] = py::cast(hist, py::return_value_policy::copy);
0120     }
0121     for (const auto& [name, eff] : coll.fakePlotTool().efficiencies()) {
0122       d[py::str(name)] = py::cast(eff, py::return_value_policy::copy);
0123     }
0124 
0125     for (const auto& [name, prof] : coll.duplicationPlotTool().profiles()) {
0126       d[py::str(name)] = py::cast(prof, py::return_value_policy::copy);
0127     }
0128     for (const auto& [name, eff] : coll.duplicationPlotTool().efficiencies()) {
0129       d[py::str(name)] = py::cast(eff, py::return_value_policy::copy);
0130     }
0131 
0132     for (const auto& [name, prof] : coll.trackSummaryPlotTool().profiles()) {
0133       d[py::str(name)] = py::cast(prof, py::return_value_policy::copy);
0134     }
0135     for (const auto& [key, tool] : coll.subDetectorSummaryTools()) {
0136       for (const auto& [name, prof] : tool.profiles()) {
0137         d[py::str(name)] = py::cast(prof, py::return_value_policy::copy);
0138       }
0139     }
0140 
0141     for (const auto& [name, prof] : coll.trackQualityPlotTool().profiles()) {
0142       d[py::str(name)] = py::cast(prof, py::return_value_policy::copy);
0143     }
0144 
0145     return d;
0146   }
0147 
0148  private:
0149   ProcessCode writeT(const AlgorithmContext& ctx,
0150                      const ConstTrackContainer& tracks) override {
0151     const auto& particles = m_inputParticles(ctx);
0152     const auto& trackParticleMatching = m_inputTrackParticleMatching(ctx);
0153     const auto& particleTrackMatching = m_inputParticleTrackMatching(ctx);
0154     const auto& particleMeasurementsMap = m_inputParticleMeasurementsMap(ctx);
0155 
0156     std::lock_guard<std::mutex> lock(m_writeMutex);
0157     return m_collector.fill(ctx.geoContext, tracks, particles,
0158                             trackParticleMatching, particleTrackMatching,
0159                             particleMeasurementsMap);
0160   }
0161 
0162   Config m_cfg;
0163   std::mutex m_writeMutex;
0164   TrackFinderPerformanceCollector m_collector;
0165 
0166   ReadDataHandle<SimParticleContainer> m_inputParticles{this, "InputParticles"};
0167   ReadDataHandle<TrackParticleMatching> m_inputTrackParticleMatching{
0168       this, "InputTrackParticleMatching"};
0169   ReadDataHandle<ParticleTrackMatching> m_inputParticleTrackMatching{
0170       this, "InputParticleTrackMatching"};
0171   ReadDataHandle<InverseMultimap<SimBarcode>> m_inputParticleMeasurementsMap{
0172       this, "InputParticleMeasurementsMap"};
0173 };
0174 
0175 }  // namespace
0176 
0177 namespace ActsPython {
0178 
0179 void addPythonSpecific(py::module_& mex) {
0180   using Writer = PythonTrackFinderPerformanceWriter;
0181   using Config = Writer::Config;
0182 
0183   auto w = py::class_<Writer, IWriter, std::shared_ptr<Writer>>(
0184                mex, "PythonTrackFinderPerformanceWriter")
0185                .def(py::init<const Config&, Acts::Logging::Level>(),
0186                     py::arg("config"), py::arg("level"))
0187                .def_property_readonly("config", &Writer::config)
0188                .def("histograms", &Writer::histograms);
0189 
0190   auto c = py::class_<Config>(w, "Config").def(py::init<>());
0191   ACTS_PYTHON_STRUCT(c, inputTracks, inputParticles, inputTrackParticleMatching,
0192                      inputParticleTrackMatching, inputParticleMeasurementsMap,
0193                      effPlotToolConfig, fakePlotToolConfig,
0194                      duplicationPlotToolConfig, trackSummaryPlotToolConfig,
0195                      trackQualityPlotToolConfig,
0196                      subDetectorTrackSummaryVolumes);
0197 }
0198 
0199 }  // namespace ActsPython