Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-17 08:03:26

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/Plugins/ActSVG/TrackingGeometrySvgConverter.hpp"
0010 
0011 #include "Acts/Geometry/CompositePortalLink.hpp"
0012 #include "Acts/Geometry/CylinderVolumeBounds.hpp"
0013 #include "Acts/Geometry/GeometryContext.hpp"
0014 #include "Acts/Geometry/GridPortalLink.hpp"
0015 #include "Acts/Geometry/TrackingGeometry.hpp"
0016 #include "Acts/Geometry/TrackingVolume.hpp"
0017 #include "Acts/Navigation/SurfaceArrayNavigationPolicy.hpp"
0018 #include "Acts/Plugins/ActSVG/DetectorVolumeSvgConverter.hpp"
0019 #include "Acts/Plugins/ActSVG/PortalSvgConverter.hpp"
0020 #include "Acts/Plugins/ActSVG/SurfaceArraySvgConverter.hpp"
0021 #include "Acts/Plugins/ActSVG/SurfaceSvgConverter.hpp"
0022 #include "Acts/Utilities/Zip.hpp"
0023 
0024 #include <sstream>
0025 #include <stdexcept>
0026 
0027 namespace Acts::Svg {
0028 
0029 std::vector<actsvg::svg::object> TrackingGeometryConverter::convert(
0030     const GeometryContext& gctx, const TrackingGeometry& tGeometry,
0031     const TrackingGeometryConverter::Options& cOptions) {
0032   // Get the world volume
0033   const TrackingVolume* world = tGeometry.highestTrackingVolume();
0034 
0035   // Initiate the cache
0036   TrackingGeometryConverter::State cState;
0037 
0038   // Run the conversion recursively
0039   convert(gctx, *world, cOptions, cState);
0040 
0041   // Digest the views and globals
0042   std::vector<actsvg::svg::object> finalViews = cState.finalViews;
0043   if (!cState.xyCrossSection.empty()) {
0044     finalViews.push_back(
0045         Acts::Svg::group(cState.xyCrossSection, cOptions.prefix + "layers_xy"));
0046   }
0047   if (!cState.zrCrossSection.empty()) {
0048     finalViews.push_back(
0049         Acts::Svg::group(cState.zrCrossSection, cOptions.prefix + "layers_zr"));
0050   }
0051   // return all final Views
0052   return finalViews;
0053 }
0054 
0055 void TrackingGeometryConverter::convert(
0056     const GeometryContext& gctx, const TrackingVolume& tVolume,
0057     const TrackingGeometryConverter::Options& cOptions,
0058     TrackingGeometryConverter::State& cState) {
0059   // Process confined layers first
0060   if (tVolume.confinedLayers() != nullptr) {
0061     for (const auto& layer : tVolume.confinedLayers()->arrayObjects()) {
0062       if (layer->surfaceArray() != nullptr) {
0063         GeometryIdentifier geoID = layer->geometryId();
0064         std::string layerName = cOptions.prefix + "vol_" +
0065                                 std::to_string(geoID.volume()) + "_layer_" +
0066                                 std::to_string(geoID.layer());
0067 
0068         LayerConverter::Options lOptions;
0069         // Search for predefined layer options
0070         auto deflOptions = cOptions.layerOptions.find(geoID);
0071         if (deflOptions != cOptions.layerOptions.end()) {
0072           lOptions = (*deflOptions);
0073           lOptions.name = layerName;
0074         }
0075         // Retrieve the layer sheets
0076         auto layerSheets = LayerConverter::convert(gctx, *layer, lOptions);
0077 
0078         // Record the sheets
0079         for (const auto& lSheet : layerSheets) {
0080           if (lSheet.is_defined()) {
0081             cState.finalViews.push_back(lSheet);
0082           }
0083         }
0084         // Collect the xy views
0085         if (layerSheets[LayerConverter::eCrossSectionXY].is_defined() &&
0086             layer->surfaceRepresentation().type() == Acts::Surface::Cylinder) {
0087           cState.xyCrossSection.push_back(
0088               layerSheets[LayerConverter::eCrossSectionXY]);
0089         }
0090         // Collect the zr views
0091         if (layerSheets[LayerConverter::eCrossSectionZR].is_defined()) {
0092           cState.zrCrossSection.push_back(
0093               layerSheets[LayerConverter::eCrossSectionZR]);
0094         }
0095       }
0096     }
0097   }
0098 
0099   // Run recursively over confined volumes
0100   if (tVolume.confinedVolumes() != nullptr) {
0101     for (const auto& volume : tVolume.confinedVolumes()->arrayObjects()) {
0102       convert(gctx, *volume, cOptions, cState);
0103     }
0104   }
0105 }
0106 
0107 std::array<actsvg::svg::object, 2> TrackingGeometryProjections::convert(
0108     const GeometryContext& gctx, const Acts::TrackingGeometry& tGeometry,
0109     const TrackingGeometryProjections::Options& cOptions) {
0110   // The projections
0111   actsvg::svg::object xyView;
0112   actsvg::svg::object zrView;
0113 
0114   // Get the world volume
0115   const Acts::TrackingVolume* world = tGeometry.highestTrackingVolume();
0116   if (world != nullptr) {
0117     // Initiate the cache
0118     Acts::Svg::TrackingGeometryConverter::State cState;
0119 
0120     // Run the conversion recursively
0121     Acts::Svg::TrackingGeometryConverter::convert(
0122         gctx, *world, cOptions.trackingGeometryOptions, cState);
0123 
0124     xyView = Acts::Svg::group(cState.xyCrossSection,
0125                               cOptions.prefix + "projection_xy");
0126     zrView = Acts::Svg::group(cState.zrCrossSection,
0127                               cOptions.prefix + "projection_zr");
0128   }
0129   return {xyView, zrView};
0130 }
0131 
0132 // Gen3
0133 
0134 namespace {
0135 void convertPortalLink(const GeometryContext& gctx,
0136                        const PortalLinkBase& portalLink, Direction direction,
0137                        std::vector<Svg::ProtoPortal::link>& links) {
0138   auto getCenters = [&](const GridPortalLink& grid, AxisDirection axis0) {
0139     assert((grid.dim() == 2 || grid.dim() == 1) &&
0140            "Grid has unexpected number of dimension");
0141 
0142     std::vector<Vector2> centers;
0143 
0144     const auto localBins = grid.grid().numLocalBinsAny();
0145     if (grid.dim() == 2) {
0146       for (std::size_t i = 1; i <= localBins[0]; ++i) {
0147         for (std::size_t j = 1; j <= localBins[1]; ++j) {
0148           auto center = grid.grid().binCenterAny({i, j});
0149           assert(center.size() == 2);
0150           centers.push_back(Vector2(center[0], center[1]));
0151         }
0152       }
0153     } else {
0154       for (std::size_t i = 1; i <= localBins[0]; ++i) {
0155         auto center = grid.grid().binCenterAny({i});
0156         assert(center.size() == 1);
0157         if (axis0 == grid.direction()) {
0158           centers.push_back(Vector2(center[0], 0.));
0159         } else {
0160           centers.push_back(Vector2(0., center[0]));
0161         }
0162       }
0163     }
0164 
0165     return centers;
0166   };
0167 
0168   if (const auto* discBounds =
0169           dynamic_cast<const DiscBounds*>(&portalLink.surface().bounds());
0170       discBounds != nullptr) {
0171     if (const auto* grid = dynamic_cast<const GridPortalLink*>(&portalLink);
0172         grid != nullptr) {
0173       std::vector<Vector2> centers = getCenters(*grid, AxisDirection::AxisR);
0174       for (const auto& center : centers) {
0175         Svg::ProtoPortal::link link;
0176         link._start = portalLink.surface().localToGlobal(gctx, center);
0177         Vector3 normal = portalLink.surface().normal(gctx, link._start);
0178         link._end = link._start + normal * 10. * direction.sign();
0179         links.push_back(link);
0180       }
0181     } else if (const auto* composite =
0182                    dynamic_cast<const CompositePortalLink*>(&portalLink);
0183                composite != nullptr) {
0184       for (const auto& link : composite->links()) {
0185         convertPortalLink(gctx, link, direction, links);
0186       }
0187     } else {
0188       double rMin = discBounds->rMin();
0189       double rMax = discBounds->rMax();
0190       double rMid = 0.5 * (rMin + rMax);
0191 
0192       Svg::ProtoPortal::link link;
0193       link._start = portalLink.surface().center(gctx) + Vector3(rMid, 0., 0.);
0194       Vector3 normal = portalLink.surface().normal(gctx, link._start);
0195       link._end = link._start + normal * 10. * direction.sign();
0196       links.push_back(link);
0197     }
0198 
0199   } else if (const auto* cylBounds = dynamic_cast<const CylinderBounds*>(
0200                  &portalLink.surface().bounds());
0201              cylBounds != nullptr) {
0202     if (const auto* grid = dynamic_cast<const GridPortalLink*>(&portalLink);
0203         grid != nullptr) {
0204       std::vector<Vector2> centers = getCenters(*grid, AxisDirection::AxisRPhi);
0205       for (const auto& center : centers) {
0206         Svg::ProtoPortal::link link;
0207         link._start = portalLink.surface().localToGlobal(gctx, center);
0208         Vector3 normal = portalLink.surface().normal(gctx, link._start);
0209         link._end = link._start + normal * 10. * direction.sign();
0210         links.push_back(link);
0211       }
0212     } else if (const auto* composite =
0213                    dynamic_cast<const CompositePortalLink*>(&portalLink);
0214                composite != nullptr) {
0215       for (const auto& link : composite->links()) {
0216         convertPortalLink(gctx, link, direction, links);
0217       }
0218     } else {
0219       double r = cylBounds->get(CylinderBounds::eR);
0220       Svg::ProtoPortal::link link;
0221       link._start = portalLink.surface().center(gctx) + Vector3(r, 0., 0.);
0222       Vector3 normal = portalLink.surface().normal(gctx, link._start);
0223       link._end = link._start + normal * 10. * direction.sign();
0224       links.push_back(link);
0225     }
0226   } else {
0227     throw std::invalid_argument("Unknown bounds type");
0228   }
0229 }
0230 
0231 struct Visitor : TrackingGeometryVisitor {
0232   explicit Visitor(const GeometryContext& gctxIn) : gctx(gctxIn) {}
0233 
0234   void visitSurface(const Surface& surface) override {
0235     auto proto = Svg::SurfaceConverter::convert(gctx, surface, {});
0236     surfaces.push_back(std::pair{&surface, proto});
0237   }
0238 
0239   void visitPortal(const Portal& portal) override {
0240     // Include portal surface itself
0241     visitSurface(portal.surface());
0242 
0243     auto pIt = std::ranges::find_if(
0244         portals, [&](const auto& p) { return p.first == &portal; });
0245 
0246     Svg::ProtoPortal pPortal;
0247     if (pIt == portals.end()) {
0248       pPortal._name = "portal_" + std::to_string(portals.size());
0249       auto surface = Svg::SurfaceConverter::convert(gctx, portal.surface(), {});
0250       pPortal._surface = surface;
0251 
0252       if (auto link = portal.getLink(Direction::AlongNormal());
0253           link != nullptr) {
0254         convertPortalLink(gctx, *link, Direction::AlongNormal(),
0255                           pPortal._volume_links);
0256       }
0257 
0258       if (auto link = portal.getLink(Direction::OppositeNormal());
0259           link != nullptr) {
0260         convertPortalLink(gctx, *link, Direction::OppositeNormal(),
0261                           pPortal._volume_links);
0262       }
0263       portals.push_back(std::pair{&portal, pPortal});
0264     } else {
0265       pPortal = pIt->second;
0266     }
0267 
0268     auto& [volume, pVolume] = volumes.back();
0269     pVolume._portals.push_back(pPortal);
0270   }
0271 
0272   void visitVolume(const TrackingVolume& volume) override {
0273     Svg::ProtoVolume pVolume;
0274     pVolume._name = volume.volumeName();
0275 
0276     auto volumeTransform = volume.transform();
0277 
0278     // https://github.com/acts-project/actsvg/blob/2f1aaa58365a1dd1af62dc27aea5039459a65a38/meta/include/actsvg/display/geometry.hpp#L687-L692
0279     enum svgBv : unsigned int {
0280       rInner = 0u,
0281       rOuter = 1u,
0282       zPos = 2u,
0283       zHalf = 3u,
0284       phiSec = 4u,
0285       avgPhi = 5u,
0286     };
0287 
0288     using enum CylinderVolumeBounds::BoundValues;
0289     const auto& boundValues = volume.volumeBounds().values();
0290     if (volume.volumeBounds().type() == Acts::VolumeBounds::eCylinder) {
0291       pVolume._bound_values.resize(6);
0292       pVolume._bound_values.at(svgBv::rInner) =
0293           static_cast<actsvg::scalar>(boundValues[eMinR]);
0294       pVolume._bound_values.at(svgBv::rOuter) =
0295           static_cast<actsvg::scalar>(boundValues[eMaxR]);
0296       pVolume._bound_values.at(svgBv::zPos) =
0297           static_cast<actsvg::scalar>(volumeTransform.translation().z());
0298       pVolume._bound_values.at(svgBv::zHalf) =
0299           static_cast<actsvg::scalar>(boundValues[eHalfLengthZ]);
0300       pVolume._bound_values.at(svgBv::phiSec) =
0301           static_cast<actsvg::scalar>(boundValues[eHalfPhiSector]);
0302       pVolume._bound_values.at(svgBv::avgPhi) =
0303           static_cast<actsvg::scalar>(boundValues[eAveragePhi]);
0304     } else {
0305       throw std::invalid_argument("Unknown bounds type");
0306     }
0307 
0308     volumes.push_back(std::pair{&volume, pVolume});
0309   }
0310 
0311   const GeometryContext& gctx;
0312 
0313   std::vector<std::pair<const Portal*, Svg::ProtoPortal>> portals;
0314 
0315   std::vector<std::pair<const TrackingVolume*, Svg::ProtoVolume>> volumes;
0316 
0317   std::vector<std::pair<const Surface*, Svg::ProtoSurface>> surfaces;
0318 };
0319 }  // namespace
0320 
0321 std::vector<actsvg::svg::object> drawTrackingGeometry(
0322     const GeometryContext& gctx, const TrackingGeometry& tGeometry,
0323     std::variant<actsvg::views::x_y, actsvg::views::z_r> view,
0324     bool drawSurfaces, bool highlightMaterial) {
0325   if (tGeometry.geometryVersion() != TrackingGeometry::GeometryVersion::Gen3) {
0326     throw std::invalid_argument{
0327         "Input tracking geometry needs to have been built in Gen3 mode"};
0328   }
0329 
0330   Visitor visitor(gctx);
0331   tGeometry.apply(visitor);
0332 
0333   std::vector<actsvg::svg::object> objects;
0334 
0335   std::visit(
0336       [&](auto& _view) {
0337         for (const auto& [tv, volume] : visitor.volumes) {
0338           std::string id = tv->volumeName();
0339           auto object = actsvg::display::volume(id, volume, _view);
0340           objects.push_back(object);
0341 
0342           std::vector<std::string> lines;
0343 
0344           std::stringstream ss;
0345           ss << "ID: " << tv->geometryId();
0346           lines.push_back(ss.str());
0347 
0348           ss.str("");
0349           ss << tv->volumeBounds();
0350           std::string bounds = ss.str();
0351           // split at first space after 40 characters
0352           ss.str("");
0353           for (std::size_t i = 0; i < bounds.size(); ++i) {
0354             ss << bounds[i];
0355             if (ss.str().size() > 40 && bounds[i] == ' ') {
0356               lines.push_back(ss.str());
0357               ss.str("");
0358             }
0359           }
0360 
0361           auto text = actsvg::draw::connected_info_box(
0362               "info_volume_" + volume._name, {0, 0}, volume._name,
0363               {{._rgb{200, 200, 200}}}, {._fc{._rgb{0, 0, 0}}, ._size = 24},
0364               lines, {{._rgb{220, 220, 220}}},
0365               {._fc{._rgb{0, 0, 0}}, ._size = 24}, {}, object);
0366 
0367           objects.push_back(text);
0368         }
0369 
0370         if (drawSurfaces) {
0371           for (const auto& [surface, proto] : visitor.surfaces) {
0372             std::stringstream ss;
0373             ss << surface->geometryId();
0374             auto object = actsvg::display::surface(ss.str(), proto, _view);
0375 
0376             if (highlightMaterial && surface->surfaceMaterial() != nullptr) {
0377               object._stroke._sc._rgb = {255, 0, 0};
0378               object._stroke._width = 1.5;
0379             }
0380 
0381             objects.push_back(object);
0382           }
0383         }
0384       },
0385       view);
0386 
0387   return objects;
0388 }
0389 
0390 namespace {
0391 
0392 struct SurfaceArrayCollector {
0393   std::vector<std::tuple<Acts::GeometryIdentifier, const Acts::SurfaceArray*>>
0394       surfaceArrays;
0395   // Visitor pattern to collect the surface arrays
0396   void operator()(const Acts::TrackingVolume* tVolume) {
0397     // This is trying Gen1 first
0398     const auto& cLayers = tVolume->confinedLayers();
0399     if (cLayers != nullptr) {
0400       for (const auto& layer : cLayers->arrayObjects()) {
0401         if (layer != nullptr) {
0402           const auto& surfaceArray = layer->surfaceArray();
0403           if (surfaceArray != nullptr) {
0404             surfaceArrays.emplace_back(layer->geometryId(), surfaceArray);
0405           }
0406         }
0407       }
0408     }
0409     // Now try Gen3
0410     if (const auto* policyPtr = tVolume->navigationPolicy();
0411         policyPtr != nullptr) {
0412       policyPtr->visit([&](const auto& policy) {
0413         if (auto sArrayPolicy =
0414                 dynamic_cast<const Acts::SurfaceArrayNavigationPolicy*>(
0415                     &policy);
0416             sArrayPolicy != nullptr) {
0417           surfaceArrays.emplace_back(tVolume->geometryId(),
0418                                      &sArrayPolicy->surfaceArray());
0419         }
0420       });
0421     }
0422   }
0423 };
0424 }  // namespace
0425 
0426 // Helper function to be picked to draw surface arrays
0427 std::vector<actsvg::svg::object> drawSurfaceArrays(
0428     const Acts::GeometryContext& gctx, const TrackingGeometry& tGeometry) {
0429   // The svg objects to be returned
0430   std::vector<actsvg::svg::object> svgSurfaceArrays;
0431 
0432   std::vector<const Acts::SurfaceArray*> surfaceArrays;
0433   // Retrieve the surface arrays from the geometry
0434   // - try Gen1 first
0435   SurfaceArrayCollector saCollector;
0436   tGeometry.visitVolumes(saCollector);
0437   // Now draw the surface arrays
0438   for (const auto& [geoId, surfaceArray] : saCollector.surfaceArrays) {
0439     // Get the surface array and convert it
0440     auto pIndexedSurfaceGrid =
0441         Svg::SurfaceArrayConverter::convert(gctx, *surfaceArray);
0442 
0443     // Decide the xy / rz view on the grid
0444     const auto& [pSurfaces, pGrid, pAssociations] = pIndexedSurfaceGrid;
0445     std::string sArrayName =
0446         "SurfaceArray_vol" + std::to_string(geoId.volume());
0447     sArrayName += "_lay" + std::to_string(geoId.layer());
0448     if (pGrid._type == actsvg::proto::grid::e_r_phi) {
0449       svgSurfaceArrays.emplace_back(
0450           Svg::View::xy(pIndexedSurfaceGrid, "xy_" + sArrayName));
0451     } else if (pGrid._type == actsvg::proto::grid::e_z_phi) {
0452       svgSurfaceArrays.emplace_back(
0453           Svg::View::xy(pIndexedSurfaceGrid, "zphi_" + sArrayName));
0454     }
0455   }
0456   return svgSurfaceArrays;
0457 }
0458 
0459 }  // namespace Acts::Svg