Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-11 09:24:31

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