Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-18 08:13:14

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/Detector/Detector.hpp"
0010 #include "Acts/Detector/DetectorVolume.hpp"
0011 #include "Acts/Detector/Portal.hpp"
0012 #include "Acts/Geometry/CylinderVolumeBounds.hpp"
0013 #include "Acts/Geometry/GeometryContext.hpp"
0014 #include "Acts/Geometry/GridPortalLink.hpp"
0015 #include "Acts/Geometry/Layer.hpp"
0016 #include "Acts/Geometry/PortalLinkBase.hpp"
0017 #include "Acts/Geometry/TrackingGeometry.hpp"
0018 #include "Acts/Geometry/TrackingVolume.hpp"
0019 #include "Acts/Navigation/SurfaceArrayNavigationPolicy.hpp"
0020 #include "Acts/Plugins/ActSVG/DetectorSvgConverter.hpp"
0021 #include "Acts/Plugins/ActSVG/DetectorVolumeSvgConverter.hpp"
0022 #include "Acts/Plugins/ActSVG/IndexedSurfacesSvgConverter.hpp"
0023 #include "Acts/Plugins/ActSVG/LayerSvgConverter.hpp"
0024 #include "Acts/Plugins/ActSVG/PortalSvgConverter.hpp"
0025 #include "Acts/Plugins/ActSVG/SurfaceArraySvgConverter.hpp"
0026 #include "Acts/Plugins/ActSVG/SurfaceSvgConverter.hpp"
0027 #include "Acts/Plugins/ActSVG/SvgUtils.hpp"
0028 #include "Acts/Plugins/ActSVG/TrackingGeometrySvgConverter.hpp"
0029 #include "Acts/Surfaces/CylinderBounds.hpp"
0030 #include "Acts/Surfaces/DiscBounds.hpp"
0031 #include "Acts/Utilities/Enumerate.hpp"
0032 #include "Acts/Utilities/Helpers.hpp"
0033 #include "ActsExamples/EventData/GeometryContainers.hpp"
0034 #include "ActsExamples/EventData/SimHit.hpp"
0035 #include "ActsExamples/EventData/SimSpacePoint.hpp"
0036 #include "ActsExamples/Io/Svg/SvgPointWriter.hpp"
0037 #include "ActsExamples/Io/Svg/SvgTrackingGeometryWriter.hpp"
0038 #include "ActsPython/Utilities/Helpers.hpp"
0039 #include "ActsPython/Utilities/Macros.hpp"
0040 #include <actsvg/core/draw.hpp>
0041 
0042 #include <algorithm>
0043 #include <memory>
0044 #include <ranges>
0045 #include <sstream>
0046 #include <string>
0047 #include <tuple>
0048 #include <vector>
0049 
0050 #include <pybind11/pybind11.h>
0051 #include <pybind11/stl.h>
0052 
0053 namespace py = pybind11;
0054 using namespace pybind11::literals;
0055 
0056 using namespace Acts;
0057 using namespace Acts::Experimental;
0058 using namespace ActsExamples;
0059 
0060 namespace {
0061 
0062 // A cache object
0063 using PortalCache = std::list<std::string>;
0064 
0065 // Helper lambda for view range selection
0066 bool viewRangeSel(const Svg::ProtoSurface& s, const Extent& vRange) {
0067   for (const auto& v : s._vertices) {
0068     if (vRange.contains(v)) {
0069       return true;
0070     }
0071   }
0072 
0073   return false;
0074 };
0075 
0076 /// @brief helper tuple to define which views and which range to be used
0077 ///
0078 /// @param view is the view type, e.g. 'xy', 'zr', ...
0079 /// @param selection is the selection of the volume, e.g. 'all', 'sensitives', 'portals', 'materials'
0080 using ViewAndRange = std::tuple<std::string, std::vector<std::string>, Extent>;
0081 
0082 /// Helper function to be picked in different access patterns
0083 ///
0084 /// @param pVolume is the proto volume to be drawn
0085 /// @param identification is the identification of the volume
0086 /// @param viewAndRange is the view, selection and range to be drawn
0087 /// @param portalCache is a portal cache to avoid multiple drawings of the same portal
0088 ///
0089 /// Returns an svg object in the right view
0090 actsvg::svg::object drawDetectorVolume(const Svg::ProtoVolume& pVolume,
0091                                        const std::string& identification,
0092                                        const ViewAndRange& viewAndRange,
0093                                        PortalCache& portalCache) {
0094   actsvg::svg::object svgDet;
0095   svgDet._id = identification;
0096   svgDet._tag = "g";
0097 
0098   const auto& [view, selection, viewRange] = viewAndRange;
0099 
0100   // Translate selection into booleans
0101   const bool all = rangeContainsValue(selection, "all");
0102   const bool sensitives = rangeContainsValue(selection, "sensitives");
0103   const bool portals = rangeContainsValue(selection, "portals");
0104   const bool materials = rangeContainsValue(selection, "materials");
0105 
0106   // Helper lambda for material selection
0107   auto materialSel = [&](const Svg::ProtoSurface& s) -> bool {
0108     return (materials && s._decorations.contains("material"));
0109   };
0110 
0111   // -------------------- surface section
0112   // The surfaces to be drawn
0113   std::vector<Svg::ProtoVolume::surface_type> sSurfaces;
0114   sSurfaces.reserve(pVolume._v_surfaces.size());
0115   for (const auto& s : pVolume._v_surfaces) {
0116     if ((all || sensitives || materialSel(s)) && viewRangeSel(s, viewRange)) {
0117       sSurfaces.push_back(s);
0118     }
0119   }
0120 
0121   // Now draw all the surfaces
0122   for (const auto& vs : sSurfaces) {
0123     if (view == "xy") {
0124       svgDet.add_object(Svg::View::xy(vs, identification));
0125     } else if (view == "zr") {
0126       svgDet.add_object(Svg::View::zr(vs, identification));
0127     } else {
0128       throw std::invalid_argument("Unknown view type");
0129     }
0130   }
0131 
0132   // -------------------- portal section
0133   std::vector<Svg::ProtoPortal> sPortals;
0134   sPortals.reserve(pVolume._portals.size());
0135   for (const auto& vp : pVolume._portals) {
0136     if ((all || portals || materialSel(vp._surface)) &&
0137         viewRangeSel(vp._surface, viewRange)) {
0138       sPortals.push_back(vp);
0139     }
0140   }
0141 
0142   // Now draw all the portals - if not already in the cache
0143   for (const auto& vp : sPortals) {
0144     auto pgID = vp._surface._decorations.find("geo_id");
0145     std::string gpIDs = "";
0146     if (pgID != vp._surface._decorations.end()) {
0147       gpIDs = pgID->second._id;
0148     }
0149 
0150     if (rangeContainsValue(portalCache, gpIDs)) {
0151       continue;
0152     }
0153 
0154     // Register this portal to the cache
0155     portalCache.insert(portalCache.begin(), gpIDs);
0156 
0157     if (view == "xy") {
0158       svgDet.add_object(Svg::View::xy(vp, identification));
0159     } else if (view == "zr") {
0160       svgDet.add_object(Svg::View::zr(vp, identification));
0161     } else {
0162       throw std::invalid_argument("Unknown view type");
0163     }
0164   }
0165   return svgDet;
0166 }
0167 
0168 // Helper function to be picked in different access patterns
0169 std::vector<actsvg::svg::object> drawDetector(
0170     const GeometryContext& gctx, const Detector& detector,
0171     const std::string& identification,
0172     const std::vector<std::tuple<int, Svg::DetectorVolumeConverter::Options>>&
0173         volumeIdxOpts,
0174     const std::vector<ViewAndRange>& viewAndRanges) {
0175   PortalCache portalCache;
0176 
0177   // The svg object to be returned
0178   std::vector<actsvg::svg::object> svgDetViews;
0179   svgDetViews.reserve(viewAndRanges.size());
0180   for (unsigned int i = 0; i < viewAndRanges.size(); ++i) {
0181     actsvg::svg::object svgDet;
0182     svgDet._id = identification;
0183     svgDet._tag = "g";
0184     svgDetViews.push_back(svgDet);
0185   }
0186 
0187   for (const auto& [vidx, vopts] : volumeIdxOpts) {
0188     // Get the volume and convert it
0189     const auto& v = detector.volumes()[vidx];
0190     auto [pVolume, pGrid] =
0191         Svg::DetectorVolumeConverter::convert(gctx, *v, vopts);
0192 
0193     for (auto [iv, var] : enumerate(viewAndRanges)) {
0194       auto [view, selection, range] = var;
0195       // Get the view and the range
0196       auto svgVolView = drawDetectorVolume(
0197           pVolume, identification + "_vol" + std::to_string(vidx) + "_" + view,
0198           var, portalCache);
0199       svgDetViews[iv].add_object(svgVolView);
0200     }
0201   }
0202   return svgDetViews;
0203 }
0204 
0205 }  // namespace
0206 
0207 namespace ActsPython {
0208 void addSvg(Context& ctx) {
0209   auto [m, mex] = ctx.get("main", "examples");
0210 
0211   auto svg = m.def_submodule("svg");
0212 
0213   { svg.def("toFile", &Svg::toFile, py::arg("objects"), py::arg("filename")); }
0214 
0215   py::class_<actsvg::svg::object>(svg, "object")
0216       .def_readwrite("id", &actsvg::svg::object::_id);
0217 
0218   py::class_<actsvg::svg::file>(svg, "file")
0219       .def(py::init<>())
0220       .def("add_object", &actsvg::svg::file::add_object)
0221       .def("add_objects", &actsvg::svg::file::add_objects)
0222       .def("clip",
0223            [](actsvg::svg::file& self, std::array<actsvg::scalar, 4> box) {
0224              self.set_view_box(box);
0225            })
0226       .def("write",
0227            [](const actsvg::svg::file& self, const std::string& filename) {
0228              std::ofstream file(filename);
0229              file << self;
0230              file.close();
0231            });
0232 
0233   { svg.def("toFile", &Svg::toFile, py::arg("objects"), py::arg("filename")); }
0234 
0235   // Core components, added as an acts.svg submodule
0236   {
0237     auto c = py::class_<Svg::Style>(svg, "Style").def(py::init<>());
0238     ACTS_PYTHON_STRUCT(c, fillColor, fillOpacity, highlightColor, highlights,
0239                        strokeWidth, strokeColor, highlightStrokeWidth,
0240                        highlightStrokeColor, fontSize, fontColor,
0241                        quarterSegments);
0242   }
0243 
0244   // How surfaces should be drawn: Svg Surface options & drawning
0245   {
0246     auto c = py::class_<Svg::SurfaceConverter::Options>(svg, "SurfaceOptions")
0247                  .def(py::init<>());
0248     ACTS_PYTHON_STRUCT(c, style, templateSurface);
0249 
0250     // Define the proto surface
0251     py::class_<Svg::ProtoSurface>(svg, "ProtoSurface");
0252     // Convert an Surface object into an svg::proto::surface
0253 
0254     svg.def("convertSurface", &Svg::SurfaceConverter::convert);
0255 
0256     // Define the view functions
0257     svg.def("viewSurface", [](const Svg::ProtoSurface& pSurface,
0258                               const std::string& identification,
0259                               const std::string& view = "xy") {
0260       if (view == "xy") {
0261         return Svg::View::xy(pSurface, identification);
0262       } else if (view == "zr") {
0263         return Svg::View::zr(pSurface, identification);
0264       } else if (view == "zphi") {
0265         return Svg::View::zphi(pSurface, identification);
0266       } else if (view == "zrphi") {
0267         return Svg::View::zrphi(pSurface, identification);
0268       } else {
0269         throw std::invalid_argument("Unknown view type");
0270       }
0271     });
0272   }
0273 
0274   // How portals should be drawn: Svg Portal options & drawning
0275   {
0276     auto c = py::class_<Svg::PortalConverter::Options>(svg, "PortalOptions")
0277                  .def(py::init<>());
0278 
0279     ACTS_PYTHON_STRUCT(c, surfaceOptions, linkLength, volumeIndices);
0280 
0281     // Define the proto portal
0282     py::class_<Svg::ProtoPortal>(svg, "ProtoPortal");
0283     // Convert an Portal object into an
0284     // svg::proto::portal
0285     svg.def("convertPortal", &Svg::PortalConverter::convert);
0286 
0287     // Define the view functions
0288     svg.def("viewPortal", [](const Svg::ProtoPortal& pPortal,
0289                              const std::string& identification,
0290                              const std::string& view = "xy") {
0291       if (view == "xy") {
0292         return Svg::View::xy(pPortal, identification);
0293       } else if (view == "zr") {
0294         return Svg::View::zr(pPortal, identification);
0295       } else {
0296         throw std::invalid_argument("Unknown view type");
0297       }
0298     });
0299   }
0300 
0301   // Draw primitives
0302   {
0303     svg.def("drawArrow", &actsvg::draw::arrow);
0304 
0305     svg.def("drawText", &actsvg::draw::text);
0306 
0307     svg.def("drawInfoBox", &Svg::infoBox);
0308   }
0309 
0310   // Draw Eta Lines
0311   {
0312     svg.def(
0313         "drawEtaLines",
0314         [](const std::string& id, actsvg ::scalar z, actsvg::scalar r,
0315            const std::vector<actsvg::scalar>& etaMain,
0316            actsvg::scalar strokeWidthMain, unsigned int sizeMain,
0317            bool labelMain, const std::vector<actsvg::scalar>& etaSub,
0318            actsvg::scalar strokeWidthSub, const std::vector<int> strokeDashSub,
0319            unsigned int sizeSub, bool labelSub) {
0320           // The main eta lines
0321           actsvg::style::stroke strokeMain;
0322           strokeMain._width = strokeWidthMain;
0323           actsvg::style::font fontMain;
0324           fontMain._size = sizeMain;
0325 
0326           actsvg::style::stroke strokeSub;
0327           strokeSub._width = strokeWidthSub;
0328           strokeSub._dasharray = strokeDashSub;
0329           actsvg::style::font fontSub;
0330           fontSub._size = sizeSub;
0331 
0332           return actsvg::display::eta_lines(
0333               id, z, r,
0334               {std::tie(etaMain, strokeMain, labelMain, fontMain),
0335                std::tie(etaSub, strokeSub, labelSub, fontSub)});
0336         });
0337   }
0338 
0339   {
0340     auto gco = py::class_<Svg::GridConverter::Options>(svg, "GridOptions")
0341                    .def(py::init<>());
0342     ACTS_PYTHON_STRUCT(gco, style);
0343 
0344     auto isco = py::class_<Svg::IndexedSurfacesConverter::Options>(
0345                     svg, "IndexedSurfacesOptions")
0346                     .def(py::init<>());
0347     ACTS_PYTHON_STRUCT(isco, gridOptions);
0348   }
0349 
0350   // How detector volumes are drawn: Svg DetectorVolume options & drawning
0351   {
0352     auto c = py::class_<Svg::DetectorVolumeConverter::Options>(
0353                  svg, "DetectorVolumeOptions")
0354                  .def(py::init<>());
0355 
0356     ACTS_PYTHON_STRUCT(c, portalIndices, portalOptions, surfaceOptions,
0357                        indexedSurfacesOptions);
0358 
0359     // Convert an DetectorVolume object into an
0360     // svg::proto::volume
0361     svg.def("convertDetectorVolume", &Svg::DetectorVolumeConverter::convert);
0362 
0363     // Define the view functions
0364     svg.def("drawDetectorVolume", &drawDetectorVolume);
0365   }
0366 
0367   // Draw the ProtoIndexedSurfaceGrid
0368   {
0369     svg.def("drawIndexedSurfaces",
0370             [](const Svg::ProtoIndexedSurfaceGrid& pIndexedSurfaceGrid,
0371                const std::string& identification) {
0372               return Svg::View::xy(pIndexedSurfaceGrid, identification);
0373             });
0374   }
0375 
0376   // How a detector is drawn: Svg Detector options & drawning
0377   { svg.def("drawDetector", &drawDetector); }
0378 
0379   { svg.def("drawSurfaceArrays", &Svg::drawSurfaceArrays); }
0380 
0381   // Legacy geometry drawing
0382   {
0383     using DefinedStyle = std::pair<GeometryIdentifier, Svg::Style>;
0384     using DefinedStyleSet = std::vector<DefinedStyle>;
0385 
0386     auto sm = py::class_<GeometryHierarchyMap<Svg::Style>>(svg, "StyleMap")
0387                   .def(py::init<DefinedStyleSet>(), py::arg("elements"));
0388 
0389     auto c = py::class_<Svg::LayerConverter::Options>(svg, "LayerOptions")
0390                  .def(py::init<>());
0391     ACTS_PYTHON_STRUCT(c, name, surfaceStyles, zRange, phiRange, gridInfo,
0392                        moduleInfo, projectionInfo, labelProjection, labelGauge);
0393   }
0394 
0395   {
0396     using DefinedLayerOptions =
0397         std::pair<GeometryIdentifier, Svg::LayerConverter::Options>;
0398     using DefinedLayerOptionsSet = std::vector<DefinedLayerOptions>;
0399 
0400     auto lom =
0401         py::class_<GeometryHierarchyMap<Svg::LayerConverter::Options>>(
0402             svg, "LayerOptionMap")
0403             .def(py::init<DefinedLayerOptionsSet>(), py::arg("elements"));
0404 
0405     auto c = py::class_<Svg::TrackingGeometryConverter::Options>(
0406                  svg, "TrackingGeometryOptions")
0407                  .def(py::init<>());
0408     ACTS_PYTHON_STRUCT(c, prefix, layerOptions);
0409   }
0410 
0411   // Components from the ActsExamples - part of acts.examples
0412 
0413   {
0414     using Writer = SvgTrackingGeometryWriter;
0415     auto w =
0416         py::class_<Writer, std::shared_ptr<Writer>>(mex,
0417                                                     "SvgTrackingGeometryWriter")
0418             .def(py::init<const Writer::Config&, Logging::Level>(),
0419                  py::arg("config"), py::arg("level"))
0420             .def("write",
0421                  py::overload_cast<const AlgorithmContext&,
0422                                    const TrackingGeometry&>(&Writer::write));
0423 
0424     auto c = py::class_<Writer::Config>(w, "Config").def(py::init<>());
0425     ACTS_PYTHON_STRUCT(c, outputDir, converterOptions);
0426   }
0427   {
0428     using Writer = SvgPointWriter<SimSpacePoint>;
0429     auto w =
0430         py::class_<Writer, IWriter, std::shared_ptr<Writer>>(
0431             mex, "SvgSimSpacePointWriter")
0432             .def(py::init<const Writer::Config&, Logging::Level>(),
0433                  py::arg("config"), py::arg("level"))
0434             .def("write",
0435                  py::overload_cast<const AlgorithmContext&>(&Writer::write));
0436 
0437     auto c = py::class_<Writer::Config>(w, "Config").def(py::init<>());
0438     ACTS_PYTHON_STRUCT(c, writerName, trackingGeometry, inputCollection,
0439                        infoBoxTitle, outputDir);
0440   }
0441 
0442   {
0443     using Writer = SvgPointWriter<SimHit, AccessorPositionXYZ>;
0444     auto w =
0445         py::class_<Writer, IWriter, std::shared_ptr<Writer>>(mex,
0446                                                              "SvgSimHitWriter")
0447             .def(py::init<const Writer::Config&, Logging::Level>(),
0448                  py::arg("config"), py::arg("level"))
0449             .def("write",
0450                  py::overload_cast<const AlgorithmContext&>(&Writer::write));
0451 
0452     auto c = py::class_<Writer::Config>(w, "Config").def(py::init<>());
0453     ACTS_PYTHON_STRUCT(c, writerName, trackingGeometry, inputCollection,
0454                        infoBoxTitle, outputDir);
0455   }
0456 
0457   svg.def(
0458       "drawTrackingGeometry",
0459       [](const GeometryContext& gctx, const TrackingGeometry& tGeometry,
0460          const std::string& view, bool drawSurfaces, bool highlightMaterial) {
0461         std::variant<actsvg::views::x_y, actsvg::views::z_r> v;
0462         if (view == "xy") {
0463           v = actsvg::views::x_y();
0464         } else if (view == "zr") {
0465           v = actsvg::views::z_r();
0466         } else {
0467           throw std::invalid_argument("Unknown view type");
0468         }
0469 
0470         return Svg::drawTrackingGeometry(gctx, tGeometry, v, drawSurfaces,
0471                                          highlightMaterial);
0472       },
0473       py::arg("gctx"), py::arg("tGeometry"), py::arg("view"),
0474       py::arg("drawSurfaces") = true, py::arg("highlightMaterial") = false);
0475 }
0476 }  // namespace ActsPython