File indexing completed on 2025-07-09 07:50:33
0001
0002
0003
0004
0005
0006
0007
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
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
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
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
0151
0152
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
0253
0254
0255
0256
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
0264 ActsExamples::GdmlDetectorConstruction gdmlContruction(gdmlFileName, {});
0265 const auto* world = gdmlContruction.Construct();
0266
0267
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
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
0301 Surfaces passiveSurfaces;
0302 passiveSurfaces.reserve(cache.passiveSurfaces.size());
0303 for (const auto& passive : cache.passiveSurfaces) {
0304 passiveSurfaces.push_back(passive);
0305 }
0306
0307
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 }