File indexing completed on 2025-01-18 09:12:01
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Geometry/Blueprint.hpp"
0010
0011 #include "Acts/Definitions/Units.hpp"
0012 #include "Acts/Geometry/BlueprintNode.hpp"
0013 #include "Acts/Geometry/CylinderContainerBlueprintNode.hpp"
0014 #include "Acts/Geometry/CylinderVolumeStack.hpp"
0015 #include "Acts/Geometry/LayerBlueprintNode.hpp"
0016 #include "Acts/Geometry/MaterialDesignatorBlueprintNode.hpp"
0017 #include "Acts/Geometry/StaticBlueprintNode.hpp"
0018 #include "Acts/Navigation/NavigationStream.hpp"
0019 #include "Acts/Plugins/Python/Utilities.hpp"
0020 #include "Acts/Utilities/AxisDefinitions.hpp"
0021 #include "Acts/Utilities/Logger.hpp"
0022
0023 #include <fstream>
0024 #include <random>
0025 #include <utility>
0026
0027 #include <pybind11/pybind11.h>
0028 #include <pybind11/pytypes.h>
0029 #include <pybind11/stl.h>
0030 #include <pybind11/stl/filesystem.h>
0031
0032 namespace py = pybind11;
0033 using namespace pybind11::literals;
0034
0035 namespace Acts::Python {
0036 namespace {
0037 using std::uniform_real_distribution;
0038
0039
0040 void pseudoNavigation(const TrackingGeometry& trackingGeometry,
0041 const GeometryContext& gctx, std::filesystem::path& path,
0042 std::size_t runs, std::size_t substepsPerCm,
0043 std::pair<double, double> etaRange,
0044 Logging::Level logLevel) {
0045 using namespace Acts::UnitLiterals;
0046
0047 ACTS_LOCAL_LOGGER(getDefaultLogger("pseudoNavigation", logLevel));
0048
0049 std::ofstream csv{path};
0050 csv << "x,y,z,volume,boundary,sensitive,material" << std::endl;
0051
0052 std::mt19937 rnd{42};
0053
0054 std::uniform_real_distribution<> dist{-1, 1};
0055 std::uniform_real_distribution<> subStepDist{0.01, 0.99};
0056
0057 double thetaMin = 2 * std::atan(std::exp(-etaRange.first));
0058 double thetaMax = 2 * std::atan(std::exp(-etaRange.second));
0059 std::uniform_real_distribution<> thetaDist{thetaMin, thetaMax};
0060
0061 using namespace Acts::UnitLiterals;
0062
0063 for (std::size_t run = 0; run < runs; run++) {
0064 Vector3 position = Vector3::Zero();
0065
0066 double theta = thetaDist(rnd);
0067 double phi = 2 * std::numbers::pi * dist(rnd);
0068
0069 Vector3 direction;
0070 direction[0] = std::sin(theta) * std::cos(phi);
0071 direction[1] = std::sin(theta) * std::sin(phi);
0072 direction[2] = std::cos(theta);
0073
0074 ACTS_VERBOSE("start navigation " << run);
0075 ACTS_VERBOSE("pos: " << position.transpose());
0076 ACTS_VERBOSE("dir: " << direction.transpose());
0077 ACTS_VERBOSE(direction.norm());
0078
0079 std::mt19937 rng{static_cast<unsigned int>(run)};
0080
0081 const auto* volume = trackingGeometry.lowestTrackingVolume(gctx, position);
0082 assert(volume != nullptr);
0083 ACTS_VERBOSE(volume->volumeName());
0084
0085 NavigationStream main;
0086 const TrackingVolume* currentVolume = volume;
0087
0088 csv << run << "," << position[0] << "," << position[1] << ","
0089 << position[2];
0090 csv << "," << volume->geometryId().volume();
0091 csv << "," << volume->geometryId().boundary();
0092 csv << "," << volume->geometryId().sensitive();
0093 csv << "," << 0;
0094 csv << std::endl;
0095
0096 ACTS_VERBOSE("start pseudo navigation");
0097
0098 auto writeIntersection = [&](const Vector3& pos, const Surface& surface) {
0099 csv << run << "," << pos[0] << "," << pos[1] << "," << pos[2];
0100 csv << "," << surface.geometryId().volume();
0101 csv << "," << surface.geometryId().boundary();
0102 csv << "," << surface.geometryId().sensitive();
0103 csv << "," << (surface.surfaceMaterial() != nullptr ? 1 : 0);
0104 csv << std::endl;
0105 };
0106
0107 for (std::size_t i = 0; i < 100; i++) {
0108 assert(currentVolume != nullptr);
0109 main = NavigationStream{};
0110
0111 AppendOnlyNavigationStream navStream{main};
0112 currentVolume->initializeNavigationCandidates(
0113 {.position = position, .direction = direction}, navStream, logger());
0114
0115 ACTS_VERBOSE(main.candidates().size() << " candidates");
0116
0117 for (const auto& candidate : main.candidates()) {
0118 ACTS_VERBOSE(" -> " << candidate.surface().geometryId());
0119 ACTS_VERBOSE(" " << candidate.surface().toStream(gctx));
0120 }
0121
0122 ACTS_VERBOSE("initializing candidates");
0123 main.initialize(gctx, {position, direction}, BoundaryTolerance::None());
0124
0125 ACTS_VERBOSE(main.candidates().size() << " candidates remaining");
0126
0127 for (const auto& candidate : main.candidates()) {
0128 ACTS_VERBOSE(" -> " << candidate.surface().geometryId());
0129 ACTS_VERBOSE(" " << candidate.surface().toStream(gctx));
0130 }
0131
0132 if (main.currentCandidate().surface().isOnSurface(gctx, position,
0133 direction)) {
0134 ACTS_VERBOSE(
0135 "Already on surface at initialization, skipping candidate");
0136
0137 writeIntersection(position, main.currentCandidate().surface());
0138
0139 if (!main.switchToNextCandidate()) {
0140 ACTS_WARNING("candidates exhausted unexpectedly");
0141 break;
0142 }
0143 }
0144
0145 bool terminated = false;
0146 while (main.remainingCandidates() > 0) {
0147 const auto& candidate = main.currentCandidate();
0148
0149 ACTS_VERBOSE(candidate.portal);
0150 ACTS_VERBOSE(candidate.intersection.position().transpose());
0151
0152 ACTS_VERBOSE("moving to position: " << position.transpose() << " (r="
0153 << VectorHelpers::perp(position)
0154 << ")");
0155
0156 Vector3 delta = candidate.intersection.position() - position;
0157
0158 std::size_t substeps =
0159 std::max(1l, std::lround(delta.norm() / 10_cm * substepsPerCm));
0160
0161 for (std::size_t j = 0; j < substeps; j++) {
0162 Vector3 subpos = position + subStepDist(rng) * delta;
0163 csv << run << "," << subpos[0] << "," << subpos[1] << ","
0164 << subpos[2];
0165 csv << "," << currentVolume->geometryId().volume();
0166 csv << ",0,0,0";
0167 csv << std::endl;
0168 }
0169
0170 position = candidate.intersection.position();
0171 ACTS_VERBOSE(" -> "
0172 << position.transpose()
0173 << " (r=" << VectorHelpers::perp(position) << ")");
0174
0175 writeIntersection(position, candidate.surface());
0176
0177 if (candidate.portal != nullptr) {
0178 ACTS_VERBOSE(
0179 "On portal: " << candidate.portal->surface().toStream(gctx));
0180 currentVolume =
0181 candidate.portal->resolveVolume(gctx, position, direction)
0182 .value();
0183
0184 if (currentVolume == nullptr) {
0185 ACTS_VERBOSE("switched to nullptr -> we're done");
0186 terminated = true;
0187 }
0188 break;
0189
0190 } else {
0191 ACTS_VERBOSE("Not on portal");
0192 }
0193
0194 main.switchToNextCandidate();
0195 }
0196
0197 if (terminated) {
0198 ACTS_VERBOSE("Terminate pseudo navigation");
0199 break;
0200 }
0201
0202 ACTS_VERBOSE("switched to " << currentVolume->volumeName());
0203
0204 ACTS_VERBOSE("-----");
0205 }
0206 }
0207 }
0208
0209 }
0210
0211 void addBlueprint(Context& ctx) {
0212 auto m = ctx.get("main");
0213
0214 auto blueprintNode =
0215 py::class_<BlueprintNode, std::shared_ptr<BlueprintNode>>(
0216 m, "BlueprintNode");
0217
0218 auto rootNode =
0219 py::class_<Blueprint, BlueprintNode, std::shared_ptr<Blueprint>>(
0220 m, "Blueprint");
0221
0222 rootNode
0223 .def(py::init<const Blueprint::Config&>())
0224
0225
0226 .def(
0227 "construct",
0228 [](Blueprint& self, const BlueprintOptions& options,
0229 const GeometryContext& gctx,
0230 Logging::Level level) -> std::shared_ptr<TrackingGeometry> {
0231 return self.construct(options, gctx,
0232 *getDefaultLogger("Blueprint", level));
0233 },
0234 py::arg("options"), py::arg("gctx"),
0235 py::arg("level") = Logging::INFO);
0236
0237 {
0238 auto c = py::class_<Blueprint::Config>(rootNode, "Config").def(py::init());
0239 ACTS_PYTHON_STRUCT_BEGIN(c, Blueprint::Config);
0240 ACTS_PYTHON_MEMBER(envelope);
0241 ACTS_PYTHON_MEMBER(geometryIdentifierHook);
0242 ACTS_PYTHON_STRUCT_END();
0243 }
0244
0245 auto addContextManagerProtocol = []<typename class_>(class_& cls) {
0246 using type = typename class_::type;
0247 cls.def("__enter__", [](type& self) -> type& { return self; })
0248 .def("__exit__", [](type& , const py::object& ,
0249 const py::object& ,
0250 const py::object& ) {
0251
0252 });
0253 };
0254
0255 auto addNodeMethods = [&blueprintNode](const std::string& name,
0256 auto&& callable, auto&&... args) {
0257 blueprintNode.def(name.c_str(), callable, args...)
0258 .def(("add" + name).c_str(), callable, args...);
0259 };
0260
0261 blueprintNode
0262 .def("__str__",
0263 [](const BlueprintNode& self) {
0264 std::stringstream ss;
0265 ss << self;
0266 return ss.str();
0267 })
0268 .def("addChild", &BlueprintNode::addChild)
0269 .def_property_readonly("children",
0270 py::overload_cast<>(&BlueprintNode::children))
0271 .def("clearChildren", &BlueprintNode::clearChildren)
0272 .def_property_readonly("name", &BlueprintNode::name)
0273 .def_property_readonly("depth", &BlueprintNode::depth)
0274 .def("graphviz", [](BlueprintNode& self, const py::object& fh) {
0275 std::stringstream ss;
0276 self.graphviz(ss);
0277 fh.attr("write")(ss.str());
0278 });
0279
0280 py::class_<BlueprintOptions>(m, "BlueprintOptions")
0281 .def(py::init<>())
0282 .def_readwrite("defaultNavigationPolicyFactory",
0283 &BlueprintOptions::defaultNavigationPolicyFactory);
0284
0285 py::class_<BlueprintNode::MutableChildRange>(blueprintNode,
0286 "MutableChildRange")
0287 .def(
0288 "__iter__",
0289 [](BlueprintNode::MutableChildRange& self) {
0290 return py::make_iterator(self.begin(), self.end());
0291 },
0292 py::keep_alive<0, 1>())
0293 .def(
0294 "__getitem__",
0295 [](BlueprintNode::MutableChildRange& self,
0296 int i) -> Acts::BlueprintNode& {
0297 if (i < 0) {
0298 i += self.size();
0299 }
0300 return self.at(i);
0301 },
0302 py::return_value_policy::reference_internal)
0303 .def("__len__", [](const BlueprintNode::MutableChildRange& self) {
0304 return self.size();
0305 });
0306
0307 auto staticNode =
0308 py::class_<Acts::StaticBlueprintNode, Acts::BlueprintNode,
0309 std::shared_ptr<Acts::StaticBlueprintNode>>(
0310 m, "StaticBlueprintNode")
0311 .def(py::init([](const Transform3& transform,
0312 const std::shared_ptr<VolumeBounds>& bounds,
0313 const std::string& name) {
0314 return std::make_shared<Acts::StaticBlueprintNode>(
0315 std::make_unique<Acts::TrackingVolume>(transform, bounds,
0316 name));
0317 }),
0318 py::arg("transform"), py::arg("bounds"),
0319 py::arg("name") = "undefined")
0320 .def_property("navigationPolicyFactory",
0321 &Acts::StaticBlueprintNode::navigationPolicyFactory,
0322 &Acts::StaticBlueprintNode::setNavigationPolicyFactory);
0323
0324 addContextManagerProtocol(staticNode);
0325
0326 addNodeMethods(
0327 "StaticVolume",
0328 [](BlueprintNode& self, const Transform3& transform,
0329 const std::shared_ptr<VolumeBounds>& bounds, const std::string& name) {
0330 auto node = std::make_shared<Acts::StaticBlueprintNode>(
0331 std::make_unique<Acts::TrackingVolume>(transform, bounds, name));
0332 self.addChild(node);
0333 return node;
0334 },
0335 py::arg("transform"), py::arg("bounds"), py::arg("name") = "undefined");
0336
0337 auto cylNode =
0338 py::class_<Acts::CylinderContainerBlueprintNode, Acts::BlueprintNode,
0339 std::shared_ptr<Acts::CylinderContainerBlueprintNode>>(
0340 m, "CylinderContainerBlueprintNode")
0341 .def(py::init<const std::string&, AxisDirection,
0342 CylinderVolumeStack::AttachmentStrategy,
0343 CylinderVolumeStack::ResizeStrategy>(),
0344 py::arg("name"), py::arg("direction"),
0345 py::arg("attachmentStrategy") =
0346 CylinderVolumeStack::AttachmentStrategy::Gap,
0347 py::arg("resizeStrategy") =
0348 CylinderVolumeStack::ResizeStrategy::Gap)
0349 .def_property(
0350 "attachmentStrategy",
0351 &Acts::CylinderContainerBlueprintNode::attachmentStrategy,
0352 &Acts::CylinderContainerBlueprintNode::setAttachmentStrategy)
0353 .def_property(
0354 "resizeStrategy",
0355 &Acts::CylinderContainerBlueprintNode::resizeStrategy,
0356 &Acts::CylinderContainerBlueprintNode::setResizeStrategy)
0357 .def_property("direction",
0358 &Acts::CylinderContainerBlueprintNode::direction,
0359 &Acts::CylinderContainerBlueprintNode::setDirection);
0360
0361 addContextManagerProtocol(cylNode);
0362
0363 addNodeMethods(
0364 "CylinderContainer",
0365 [](BlueprintNode& self, const std::string& name,
0366 AxisDirection direction) {
0367 auto cylinder =
0368 std::make_shared<CylinderContainerBlueprintNode>(name, direction);
0369 self.addChild(cylinder);
0370 return cylinder;
0371 },
0372 py::arg("name"), py::arg("direction"));
0373
0374 auto matNode =
0375 py::class_<MaterialDesignatorBlueprintNode, BlueprintNode,
0376 std::shared_ptr<MaterialDesignatorBlueprintNode>>(
0377 m, "MaterialDesignatorBlueprintNode")
0378 .def(py::init<const std::string&>(), "name"_a)
0379 .def_property("binning", &MaterialDesignatorBlueprintNode::binning,
0380 &MaterialDesignatorBlueprintNode::setBinning);
0381
0382 addContextManagerProtocol(matNode);
0383
0384 addNodeMethods(
0385 "Material",
0386 [](BlueprintNode& self, const std::string& name) {
0387 auto child = std::make_shared<MaterialDesignatorBlueprintNode>(name);
0388 self.addChild(child);
0389 return child;
0390 },
0391 "name"_a);
0392
0393 auto layerNode =
0394 py::class_<Acts::LayerBlueprintNode, Acts::StaticBlueprintNode,
0395 std::shared_ptr<Acts::LayerBlueprintNode>>(
0396 m, "LayerBlueprintNode")
0397 .def(py::init<const std::string&>(), py::arg("name"))
0398 .def_property_readonly("name", &Acts::LayerBlueprintNode::name)
0399 .def_property("surfaces", &Acts::LayerBlueprintNode::surfaces,
0400 &Acts::LayerBlueprintNode::setSurfaces)
0401 .def_property("transform", &Acts::LayerBlueprintNode::transform,
0402 &Acts::LayerBlueprintNode::setTransform)
0403 .def_property("envelope", &Acts::LayerBlueprintNode::envelope,
0404 &Acts::LayerBlueprintNode::setEnvelope)
0405 .def_property("layerType", &Acts::LayerBlueprintNode::layerType,
0406 &Acts::LayerBlueprintNode::setLayerType)
0407 .def_property("navigationPolicyFactory",
0408 &Acts::LayerBlueprintNode::navigationPolicyFactory,
0409 &Acts::LayerBlueprintNode::setNavigationPolicyFactory);
0410
0411 py::enum_<Acts::LayerBlueprintNode::LayerType>(layerNode, "LayerType")
0412 .value("Cylinder", Acts::LayerBlueprintNode::LayerType::Cylinder)
0413 .value("Disc", Acts::LayerBlueprintNode::LayerType::Disc)
0414 .value("Plane", Acts::LayerBlueprintNode::LayerType::Plane);
0415
0416 addContextManagerProtocol(layerNode);
0417
0418 addNodeMethods(
0419 "Layer",
0420 [](BlueprintNode& self, const std::string& name) {
0421 auto child = std::make_shared<LayerBlueprintNode>(name);
0422 self.addChild(child);
0423 return child;
0424 },
0425 py::arg("name"));
0426
0427
0428 m.def("pseudoNavigation", &pseudoNavigation, "trackingGeometry"_a, "gctx"_a,
0429 "path"_a, "runs"_a, "substepsPerCm"_a = 2,
0430 "etaRange"_a = std::pair{-4.5, 4.5}, "logLevel"_a = Logging::INFO);
0431 }
0432
0433 }