Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-01-09 09:26:46

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/Geometry/TrackingGeometry.hpp"
0011 #include "Acts/MagneticField/MagneticFieldProvider.hpp"
0012 #include "Acts/Surfaces/SurfaceVisitorConcept.hpp"
0013 #include "Acts/Utilities/Logger.hpp"
0014 #include "ActsExamples/Geant4/Geant4ConstructionOptions.hpp"
0015 #include "ActsExamples/Geant4/Geant4Manager.hpp"
0016 #include "ActsExamples/Geant4/Geant4Simulation.hpp"
0017 #include "ActsExamples/Geant4/RegionCreator.hpp"
0018 #include "ActsExamples/Geant4/SensitiveSurfaceMapper.hpp"
0019 #include "ActsExamples/Geant4Detector/GdmlDetector.hpp"
0020 #include "ActsExamples/Geant4Detector/GdmlDetectorConstruction.hpp"
0021 #include "ActsExamples/Geant4Detector/Geant4Detector.hpp"
0022 #include "ActsPython/Utilities/Helpers.hpp"
0023 #include "ActsPython/Utilities/Macros.hpp"
0024 
0025 #include <algorithm>
0026 #include <memory>
0027 #include <ranges>
0028 #include <string>
0029 #include <tuple>
0030 #include <unordered_map>
0031 #include <utility>
0032 #include <vector>
0033 
0034 #include <G4RunManager.hh>
0035 #include <G4Transform3D.hh>
0036 #include <G4UserEventAction.hh>
0037 #include <G4UserRunAction.hh>
0038 #include <pybind11/pybind11.h>
0039 #include <pybind11/stl.h>
0040 
0041 namespace py = pybind11;
0042 using namespace pybind11::literals;
0043 
0044 using namespace ActsExamples;
0045 using namespace Acts;
0046 using namespace ActsPlugins;
0047 using namespace ActsPython;
0048 
0049 PYBIND11_MODULE(ActsExamplesPythonBindingsGeant4, mod) {
0050   py::class_<Geant4Manager, std::unique_ptr<Geant4Manager, py::nodelete>>(
0051       mod, "Geant4Manager")
0052       .def_static("instance", &Geant4Manager::instance,
0053                   py::return_value_policy::reference)
0054       .def("currentHandle", &Geant4Manager::currentHandle);
0055 
0056   py::class_<Geant4Handle, std::shared_ptr<Geant4Handle>>(mod, "Geant4Handle")
0057       .def("tweakLogging", &Geant4Handle::tweakLogging);
0058 
0059   {
0060     py::class_<Geant4ConstructionOptions,
0061                std::shared_ptr<Geant4ConstructionOptions>>(
0062         mod, "Geant4ConstructionOptions")
0063         .def(py::init<>())
0064         .def_readwrite("regionCreators",
0065                        &Geant4ConstructionOptions::regionCreators);
0066   }
0067 
0068   {
0069     using Algorithm = Geant4SimulationBase;
0070     using Config = Algorithm::Config;
0071     auto alg =
0072         py::class_<Algorithm, IAlgorithm, std::shared_ptr<Algorithm>>(
0073             mod, "Geant4SimulationBase")
0074             .def_property_readonly("geant4Handle", &Algorithm::geant4Handle);
0075 
0076     auto c1 = py::class_<Config, std::shared_ptr<Config>>(alg, "Config")
0077                   .def(py::init<>());
0078     ACTS_PYTHON_STRUCT(c1, inputParticles, randomNumbers, detector,
0079                        geant4Handle);
0080   }
0081 
0082   {
0083     using Config = Geant4::SensitiveSurfaceMapper::Config;
0084     using State = Geant4::SensitiveSurfaceMapper::State;
0085     auto sm = py::class_<Geant4::SensitiveSurfaceMapper,
0086                          std::shared_ptr<Geant4::SensitiveSurfaceMapper>>(
0087                   mod, "SensitiveSurfaceMapper")
0088                   .def(py::init([](const Config& cfg, Logging::Level level) {
0089                     return std::make_shared<Geant4::SensitiveSurfaceMapper>(
0090                         cfg, getDefaultLogger("SensitiveSurfaceMapper", level));
0091                   }));
0092 
0093     py::class_<State>(sm, "State").def(py::init<>());
0094 
0095     auto c = py::class_<Config>(sm, "Config").def(py::init<>());
0096     ACTS_PYTHON_STRUCT(c, materialMappings, volumeMappings, candidateSurfaces);
0097 
0098     sm.def("create",
0099            [](const Config& cfg, Logging::Level level,
0100               const std::shared_ptr<const TrackingGeometry>& tGeometry) {
0101              // Set a new surface finder
0102              Config ccfg = cfg;
0103              ccfg.candidateSurfaces =
0104                  std::make_shared<Geant4::SensitiveCandidates>(
0105                      tGeometry, getDefaultLogger("SensitiveCandidates", level));
0106              return std::make_shared<Geant4::SensitiveSurfaceMapper>(
0107                  ccfg, getDefaultLogger("SensitiveSurfaceMapper", level));
0108            });
0109 
0110     sm.def(
0111         "remapSensitiveNames",
0112         [](Geant4::SensitiveSurfaceMapper& self, State& state,
0113            GeometryContext& gctx, Detector& detector, Transform3& transform) {
0114           return self.remapSensitiveNames(
0115               state, gctx,
0116               detector.buildGeant4DetectorConstruction({})->Construct(),
0117               transform);
0118         },
0119         "state"_a, "gctx"_a, "g4physicalVolume"_a, "motherTransform"_a);
0120     sm.def("checkMapping", &Geant4::SensitiveSurfaceMapper::checkMapping,
0121            "state"_a, "gctx"_a, "writeMappedAsObj"_a, "writeMissingAsObj"_a);
0122   }
0123 
0124   {
0125     using Algorithm = Geant4Simulation;
0126     using Config = Algorithm::Config;
0127     auto alg =
0128         py::class_<Algorithm, Geant4SimulationBase, std::shared_ptr<Algorithm>>(
0129             mod, "Geant4Simulation")
0130             .def(py::init<const Config&, Logging::Level>(), py::arg("config"),
0131                  py::arg("level"))
0132             .def_property_readonly("config", &Algorithm::config);
0133 
0134     auto c1 = py::class_<Config, Geant4SimulationBase::Config,
0135                          std::shared_ptr<Config>>(alg, "Config")
0136                   .def(py::init<>());
0137     ACTS_PYTHON_STRUCT(
0138         c1, outputSimHits, outputParticles, outputPropagationSummaries,
0139         sensitiveSurfaceMapper, magneticField, physicsList, killVolume,
0140         killAfterTime, killSecondaries, recordHitsOfCharged,
0141         recordHitsOfNeutrals, recordHitsOfPrimaries, recordHitsOfSecondaries,
0142         keepParticlesWithoutHits, recordPropagationSummaries);
0143   }
0144 
0145   {
0146     using Algorithm = Geant4MaterialRecording;
0147     using Config = Algorithm::Config;
0148     auto alg =
0149         py::class_<Algorithm, Geant4SimulationBase, std::shared_ptr<Algorithm>>(
0150             mod, "Geant4MaterialRecording")
0151             .def(py::init<const Config&, Logging::Level>(), py::arg("config"),
0152                  py::arg("level"))
0153             .def_property_readonly("config", &Algorithm::config);
0154 
0155     auto c = py::class_<Config, Geant4SimulationBase::Config,
0156                         std::shared_ptr<Config>>(alg, "Config")
0157                  .def(py::init<>());
0158     ACTS_PYTHON_STRUCT(c, outputMaterialTracks, excludeMaterials);
0159   }
0160 
0161   {
0162     auto f =
0163         py::class_<Geant4Detector, Detector, std::shared_ptr<Geant4Detector>>(
0164             mod, "Geant4Detector")
0165             .def(py::init<const Geant4Detector::Config&>());
0166 
0167     auto c = py::class_<Geant4Detector::Config>(f, "Config").def(py::init<>());
0168     ACTS_PYTHON_STRUCT(c, name, g4World, g4SurfaceOptions, logLevel);
0169   }
0170 
0171   {
0172     auto f = py::class_<GdmlDetector, Detector, std::shared_ptr<GdmlDetector>>(
0173                  mod, "GdmlDetector")
0174                  .def(py::init<const GdmlDetector::Config&>());
0175 
0176     auto c = py::class_<GdmlDetector::Config>(f, "Config").def(py::init<>());
0177     ACTS_PYTHON_STRUCT(c, path, logLevel);
0178   }
0179 
0180   {
0181     /// Helper function to test if the automatic geometry conversion works
0182     ///
0183     /// @param gdmlFileName is the name of the GDML file
0184     /// @param sensitiveMatches is a list of strings to match sensitive volumes
0185     /// @param passiveMatches is a list of strings to match passive volumes
0186     mod.def(
0187         "convertSurfaces", [](const std::string& gdmlFileName,
0188                               const std::vector<std::string>& sensitiveMatches,
0189                               const std::vector<std::string>& passiveMatches,
0190                               bool convertMaterial) {
0191           // Initiate the detector construction & retrieve world
0192           ActsExamples::GdmlDetectorConstruction gdmlContruction(gdmlFileName,
0193                                                                  {});
0194           const auto* world = gdmlContruction.Construct();
0195 
0196           // Create the selectors
0197           auto sensitiveSelectors =
0198               std::make_shared<Geant4PhysicalVolumeSelectors::NameSelector>(
0199                   sensitiveMatches, false);
0200           auto passiveSelectors =
0201               std::make_shared<Geant4PhysicalVolumeSelectors::NameSelector>(
0202                   passiveMatches, false);
0203 
0204           Geant4DetectorSurfaceFactory::Config config;
0205           Geant4DetectorSurfaceFactory::Cache cache;
0206           Geant4DetectorSurfaceFactory::Options options;
0207           options.sensitiveSurfaceSelector = sensitiveSelectors;
0208           options.passiveSurfaceSelector = passiveSelectors;
0209           options.convertMaterial = convertMaterial;
0210 
0211           G4Transform3D nominal;
0212           Geant4DetectorSurfaceFactory factory(config);
0213           factory.construct(cache, nominal, *world, options);
0214 
0215           // Capture the sensitive elements and the surfaces
0216           using Elements = std::vector<std::shared_ptr<Geant4DetectorElement>>;
0217           Elements detectorElements;
0218           detectorElements.reserve(cache.sensitiveSurfaces.size());
0219           using Surfaces = std::vector<std::shared_ptr<Surface>>;
0220           Surfaces surfaces;
0221           surfaces.reserve(cache.sensitiveSurfaces.size());
0222           std::ranges::for_each(
0223               cache.sensitiveSurfaces, [&](const auto& sensitive) {
0224                 detectorElements.push_back(std::get<0>(sensitive));
0225                 surfaces.push_back(std::get<1>(sensitive));
0226               });
0227 
0228           // Capture the passive surfaces
0229           Surfaces passiveSurfaces;
0230           passiveSurfaces.reserve(cache.passiveSurfaces.size());
0231           for (const auto& passive : cache.passiveSurfaces) {
0232             passiveSurfaces.push_back(passive);
0233           }
0234 
0235           // Return a convenient tuple for drawing
0236           return std::tuple<Elements, Surfaces, Surfaces>(
0237               std::move(detectorElements), std::move(surfaces),
0238               std::move(passiveSurfaces));
0239         });
0240   }
0241 
0242   {
0243     using Tool = Geant4::RegionCreator;
0244     using Config = Tool::Config;
0245     auto tool = py::class_<Tool>(mod, "RegionCreator")
0246                     .def(py::init<const Config&>(), py::arg("config"))
0247                     .def_property_readonly("config", &Tool::config);
0248 
0249     auto c = py::class_<Config>(tool, "Config").def(py::init<>());
0250     ACTS_PYTHON_STRUCT(c, gammaCut, electronCut, positronCut, protonCut,
0251                        volumes);
0252   }
0253 }