Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-09 07:50:33

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/Definitions/Algebra.hpp"
0010 #include "Acts/Detector/Detector.hpp"
0011 #include "Acts/Geometry/TrackingGeometry.hpp"
0012 #include "Acts/MagneticField/MagneticFieldProvider.hpp"
0013 #include "Acts/Plugins/Geant4/Geant4DetectorElement.hpp"
0014 #include "Acts/Plugins/Geant4/Geant4DetectorSurfaceFactory.hpp"
0015 #include "Acts/Plugins/Geant4/Geant4PhysicalVolumeSelectors.hpp"
0016 #include "Acts/Plugins/Python/Utilities.hpp"
0017 #include "Acts/Surfaces/SurfaceVisitorConcept.hpp"
0018 #include "Acts/Utilities/Logger.hpp"
0019 #include "ActsExamples/Geant4/Geant4ConstructionOptions.hpp"
0020 #include "ActsExamples/Geant4/Geant4Manager.hpp"
0021 #include "ActsExamples/Geant4/Geant4Simulation.hpp"
0022 #include "ActsExamples/Geant4/RegionCreator.hpp"
0023 #include "ActsExamples/Geant4/SensitiveSurfaceMapper.hpp"
0024 #include "ActsExamples/Geant4Detector/GdmlDetector.hpp"
0025 #include "ActsExamples/Geant4Detector/GdmlDetectorConstruction.hpp"
0026 #include "ActsExamples/Geant4Detector/Geant4Detector.hpp"
0027 #include "ActsExamples/MuonSpectrometerMockupDetector/MockupSectorBuilder.hpp"
0028 
0029 #include <algorithm>
0030 #include <memory>
0031 #include <ranges>
0032 #include <string>
0033 #include <tuple>
0034 #include <unordered_map>
0035 #include <utility>
0036 #include <vector>
0037 
0038 #include <G4RunManager.hh>
0039 #include <G4Transform3D.hh>
0040 #include <G4UserEventAction.hh>
0041 #include <G4UserRunAction.hh>
0042 #include <pybind11/pybind11.h>
0043 #include <pybind11/stl.h>
0044 
0045 namespace py = pybind11;
0046 using namespace pybind11::literals;
0047 
0048 using namespace ActsExamples;
0049 using namespace Acts;
0050 using namespace Acts::Python;
0051 
0052 struct ExperimentalSensitiveCandidates
0053     : public Geant4::SensitiveCandidatesBase {
0054   std::shared_ptr<const Experimental::Detector> detector;
0055 
0056   /// Find the sensitive surfaces for a given position
0057   std::vector<const Acts::Surface*> queryPosition(
0058       const Acts::GeometryContext& gctx,
0059       const Acts::Vector3& position) const override {
0060     std::vector<const Acts::Surface*> surfaces;
0061     // Here's the detector volume
0062     auto volume = detector->findDetectorVolume(gctx, position);
0063     if (volume != nullptr) {
0064       for (const auto& surface : volume->surfaces()) {
0065         if (surface->associatedDetectorElement() != nullptr) {
0066           surfaces.push_back(surface);
0067         }
0068       }
0069     }
0070     return surfaces;
0071   }
0072 
0073   std::vector<const Acts::Surface*> queryAll() const override {
0074     std::vector<const Acts::Surface*> surfaces;
0075     detector->visitSurfaces([&](const Acts::Surface* surface) {
0076       if (surface->associatedDetectorElement() != nullptr) {
0077         surfaces.push_back(surface);
0078       }
0079     });
0080     return surfaces;
0081   }
0082 };
0083 
0084 PYBIND11_MODULE(ActsPythonBindingsGeant4, mod) {
0085   py::class_<Geant4Manager, std::unique_ptr<Geant4Manager, py::nodelete>>(
0086       mod, "Geant4Manager")
0087       .def_static("instance", &Geant4Manager::instance,
0088                   py::return_value_policy::reference)
0089       .def("currentHandle", &Geant4Manager::currentHandle);
0090 
0091   py::class_<Geant4Handle, std::shared_ptr<Geant4Handle>>(mod, "Geant4Handle")
0092       .def("tweakLogging", &Geant4Handle::tweakLogging);
0093 
0094   {
0095     py::class_<Geant4ConstructionOptions,
0096                std::shared_ptr<Geant4ConstructionOptions>>(
0097         mod, "Geant4ConstructionOptions")
0098         .def(py::init<>())
0099         .def_readwrite("regionCreators",
0100                        &Geant4ConstructionOptions::regionCreators);
0101   }
0102 
0103   {
0104     using Algorithm = Geant4SimulationBase;
0105     using Config = Algorithm::Config;
0106     auto alg =
0107         py::class_<Algorithm, IAlgorithm, std::shared_ptr<Algorithm>>(
0108             mod, "Geant4SimulationBase")
0109             .def_property_readonly("geant4Handle", &Algorithm::geant4Handle);
0110 
0111     auto c1 = py::class_<Config, std::shared_ptr<Config>>(alg, "Config")
0112                   .def(py::init<>());
0113     ACTS_PYTHON_STRUCT(c1, inputParticles, randomNumbers, detector,
0114                        geant4Handle);
0115   }
0116 
0117   {
0118     using Config = Geant4::SensitiveSurfaceMapper::Config;
0119     using State = Geant4::SensitiveSurfaceMapper::State;
0120     auto sm =
0121         py::class_<Geant4::SensitiveSurfaceMapper,
0122                    std::shared_ptr<Geant4::SensitiveSurfaceMapper>>(
0123             mod, "SensitiveSurfaceMapper")
0124             .def(py::init([](const Config& cfg, Acts::Logging::Level level) {
0125               return std::make_shared<Geant4::SensitiveSurfaceMapper>(
0126                   cfg, getDefaultLogger("SensitiveSurfaceMapper", level));
0127             }));
0128 
0129     py::class_<State>(sm, "State").def(py::init<>());
0130 
0131     auto c = py::class_<Config>(sm, "Config").def(py::init<>());
0132     ACTS_PYTHON_STRUCT(c, materialMappings, volumeMappings, candidateSurfaces);
0133 
0134     sm.def("create",
0135            [](const Config& cfg, Acts::Logging::Level level,
0136               const std::shared_ptr<const TrackingGeometry>& tGeometry) {
0137              // Set a new surface finder
0138              Config ccfg = cfg;
0139              auto candidateSurfaces =
0140                  std::make_shared<Geant4::SensitiveCandidates>();
0141              candidateSurfaces->trackingGeometry = tGeometry;
0142              ccfg.candidateSurfaces = candidateSurfaces;
0143              return std::make_shared<Geant4::SensitiveSurfaceMapper>(
0144                  ccfg, getDefaultLogger("SensitiveSurfaceMapper", level));
0145            });
0146 
0147     sm.def("create",
0148            [](const Config& cfg, Acts::Logging::Level level,
0149               const std::shared_ptr<const Experimental::Detector>& detector) {
0150              // Helper struct to find the sensitive surface candidates
0151 
0152              // Set a new surface finder
0153              Config ccfg = cfg;
0154              auto candidateSurfaces =
0155                  std::make_shared<ExperimentalSensitiveCandidates>();
0156              candidateSurfaces->detector = detector;
0157              ccfg.candidateSurfaces = candidateSurfaces;
0158              return std::make_shared<Geant4::SensitiveSurfaceMapper>(
0159                  ccfg, getDefaultLogger("SensitiveSurfaceMapper", level));
0160            });
0161 
0162     sm.def(
0163         "remapSensitiveNames",
0164         [](Geant4::SensitiveSurfaceMapper& self, State& state,
0165            GeometryContext& gctx, Detector& detector, Transform3& transform) {
0166           return self.remapSensitiveNames(
0167               state, gctx,
0168               detector.buildGeant4DetectorConstruction({})->Construct(),
0169               transform);
0170         },
0171         "state"_a, "gctx"_a, "g4physicalVolume"_a, "motherTransform"_a);
0172     sm.def("checkMapping", &Geant4::SensitiveSurfaceMapper::checkMapping,
0173            "state"_a, "gctx"_a, "writeMappedAsObj"_a, "writeMissingAsObj"_a);
0174   }
0175 
0176   {
0177     using Algorithm = Geant4Simulation;
0178     using Config = Algorithm::Config;
0179     auto alg =
0180         py::class_<Algorithm, Geant4SimulationBase, std::shared_ptr<Algorithm>>(
0181             mod, "Geant4Simulation")
0182             .def(py::init<const Config&, Acts::Logging::Level>(),
0183                  py::arg("config"), py::arg("level"))
0184             .def_property_readonly("config", &Algorithm::config);
0185 
0186     auto c1 = py::class_<Config, Geant4SimulationBase::Config,
0187                          std::shared_ptr<Config>>(alg, "Config")
0188                   .def(py::init<>());
0189     ACTS_PYTHON_STRUCT(
0190         c1, outputSimHits, outputParticles, outputPropagationSummaries,
0191         sensitiveSurfaceMapper, magneticField, physicsList, killVolume,
0192         killAfterTime, killSecondaries, recordHitsOfCharged,
0193         recordHitsOfNeutrals, recordHitsOfPrimaries, recordHitsOfSecondaries,
0194         keepParticlesWithoutHits, recordPropagationSummaries);
0195   }
0196 
0197   {
0198     using Algorithm = Geant4MaterialRecording;
0199     using Config = Algorithm::Config;
0200     auto alg =
0201         py::class_<Algorithm, Geant4SimulationBase, std::shared_ptr<Algorithm>>(
0202             mod, "Geant4MaterialRecording")
0203             .def(py::init<const Config&, Acts::Logging::Level>(),
0204                  py::arg("config"), py::arg("level"))
0205             .def_property_readonly("config", &Algorithm::config);
0206 
0207     auto c = py::class_<Config, Geant4SimulationBase::Config,
0208                         std::shared_ptr<Config>>(alg, "Config")
0209                  .def(py::init<>());
0210     ACTS_PYTHON_STRUCT(c, outputMaterialTracks, excludeMaterials);
0211   }
0212 
0213   {
0214     using ISelector = Acts::IGeant4PhysicalVolumeSelector;
0215     auto is = py::class_<ISelector, std::shared_ptr<ISelector>>(
0216         mod, "IVolumeSelector");
0217 
0218     using NameSelector = Acts::Geant4PhysicalVolumeSelectors::NameSelector;
0219     auto ns = py::class_<NameSelector, std::shared_ptr<NameSelector>>(
0220                   mod, "VolumeNameSelector", is)
0221                   .def(py::init<const std::vector<std::string>&, bool>());
0222 
0223     using Factory = Acts::Geant4DetectorSurfaceFactory;
0224     auto o = py::class_<Factory::Options>(mod, "SurfaceFactoryOptions")
0225                  .def(py::init<>());
0226     ACTS_PYTHON_STRUCT(o, scaleConversion, convertMaterial,
0227                        convertedMaterialThickness, sensitiveSurfaceSelector,
0228                        passiveSurfaceSelector);
0229   }
0230 
0231   {
0232     auto f =
0233         py::class_<Geant4Detector, Detector, std::shared_ptr<Geant4Detector>>(
0234             mod, "Geant4Detector")
0235             .def(py::init<const Geant4Detector::Config&>());
0236 
0237     auto c = py::class_<Geant4Detector::Config>(f, "Config").def(py::init<>());
0238     ACTS_PYTHON_STRUCT(c, name, g4World, g4SurfaceOptions, protoDetector,
0239                        geometryIdentifierHook, logLevel);
0240   }
0241 
0242   {
0243     auto f = py::class_<GdmlDetector, Detector, std::shared_ptr<GdmlDetector>>(
0244                  mod, "GdmlDetector")
0245                  .def(py::init<const GdmlDetector::Config&>());
0246 
0247     auto c = py::class_<GdmlDetector::Config>(f, "Config").def(py::init<>());
0248     ACTS_PYTHON_STRUCT(c, path, logLevel);
0249   }
0250 
0251   {
0252     /// Helper function to test if the automatic geometry conversion works
0253     ///
0254     /// @param gdmlFileName is the name of the GDML file
0255     /// @param sensitiveMatches is a list of strings to match sensitive volumes
0256     /// @param passiveMatches is a list of strings to match passive volumes
0257     mod.def("convertSurfaces", [](const std::string& gdmlFileName,
0258                                   const std::vector<std::string>&
0259                                       sensitiveMatches,
0260                                   const std::vector<std::string>&
0261                                       passiveMatches,
0262                                   bool convertMaterial) {
0263       // Initiate the detector construction & retrieve world
0264       ActsExamples::GdmlDetectorConstruction gdmlContruction(gdmlFileName, {});
0265       const auto* world = gdmlContruction.Construct();
0266 
0267       // Create the selectors
0268       auto sensitiveSelectors =
0269           std::make_shared<Acts::Geant4PhysicalVolumeSelectors::NameSelector>(
0270               sensitiveMatches, false);
0271       auto passiveSelectors =
0272           std::make_shared<Acts::Geant4PhysicalVolumeSelectors::NameSelector>(
0273               passiveMatches, false);
0274 
0275       Acts::Geant4DetectorSurfaceFactory::Config config;
0276       Acts::Geant4DetectorSurfaceFactory::Cache cache;
0277       Acts::Geant4DetectorSurfaceFactory::Options options;
0278       options.sensitiveSurfaceSelector = sensitiveSelectors;
0279       options.passiveSurfaceSelector = passiveSelectors;
0280       options.convertMaterial = convertMaterial;
0281 
0282       G4Transform3D nominal;
0283       Acts::Geant4DetectorSurfaceFactory factory(config);
0284       factory.construct(cache, nominal, *world, options);
0285 
0286       // Capture the sensitive elements and the surfaces
0287       using Elements =
0288           std::vector<std::shared_ptr<Acts::Geant4DetectorElement>>;
0289       Elements detectorElements;
0290       detectorElements.reserve(cache.sensitiveSurfaces.size());
0291       using Surfaces = std::vector<std::shared_ptr<Acts::Surface>>;
0292       Surfaces surfaces;
0293       surfaces.reserve(cache.sensitiveSurfaces.size());
0294       std::ranges::for_each(
0295           cache.sensitiveSurfaces, [&](const auto& sensitive) {
0296             detectorElements.push_back(std::get<0>(sensitive));
0297             surfaces.push_back(std::get<1>(sensitive));
0298           });
0299 
0300       // Capture the passive surfaces
0301       Surfaces passiveSurfaces;
0302       passiveSurfaces.reserve(cache.passiveSurfaces.size());
0303       for (const auto& passive : cache.passiveSurfaces) {
0304         passiveSurfaces.push_back(passive);
0305       }
0306 
0307       // Return a convenient tuple for drawing
0308       return std::tuple<Elements, Surfaces, Surfaces>(
0309           std::move(detectorElements), std::move(surfaces),
0310           std::move(passiveSurfaces));
0311     });
0312   }
0313 
0314   {
0315     using MockupSectorBuilder = MockupSectorBuilder;
0316     using Config = MockupSectorBuilder::Config;
0317     using ChamberConfig = MockupSectorBuilder::ChamberConfig;
0318 
0319     auto ms =
0320         py::class_<MockupSectorBuilder, std::shared_ptr<MockupSectorBuilder>>(
0321             mod, "MockupSectorBuilder")
0322             .def(py::init<const Config&>())
0323             .def("buildChamber", &MockupSectorBuilder::buildChamber)
0324             .def("buildSector", &MockupSectorBuilder::buildSector)
0325             .def("drawSector", &MockupSectorBuilder::drawSector);
0326 
0327     auto c = py::class_<Config>(ms, "Config").def(py::init<>());
0328     ACTS_PYTHON_STRUCT(c, gdmlPath, NumberOfSectors, toleranceOverlap);
0329 
0330     auto cch = py::class_<ChamberConfig>(ms, "ChamberConfig").def(py::init<>());
0331     ACTS_PYTHON_STRUCT(cch, name, SensitiveNames, PassiveNames);
0332   }
0333 
0334   {
0335     using Tool = Geant4::RegionCreator;
0336     using Config = Tool::Config;
0337     auto tool = py::class_<Tool>(mod, "RegionCreator")
0338                     .def(py::init<const Config&>(), py::arg("config"))
0339                     .def_property_readonly("config", &Tool::config);
0340 
0341     auto c = py::class_<Config>(tool, "Config").def(py::init<>());
0342     ACTS_PYTHON_STRUCT(c, gammaCut, electronCut, positronCut, protonCut,
0343                        volumes);
0344   }
0345 
0346   Acts::Python::Context ctx;
0347   ctx.modules["geant4"] = mod;
0348 }