Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-10-29 07:54:47

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