Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-05 08:12:08

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