Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 09:23:04

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/Geometry/Blueprint.hpp"
0010 
0011 #include "Acts/Geometry/CuboidPortalShell.hpp"
0012 #include "Acts/Geometry/CuboidVolumeBounds.hpp"
0013 #include "Acts/Geometry/CylinderPortalShell.hpp"
0014 #include "Acts/Geometry/CylinderVolumeBounds.hpp"
0015 #include "Acts/Geometry/Extent.hpp"
0016 #include "Acts/Geometry/GeometryIdentifier.hpp"
0017 #include "Acts/Geometry/PortalShell.hpp"
0018 #include "Acts/Geometry/VolumeBounds.hpp"
0019 #include "Acts/Geometry/detail/BoundDeduplicator.hpp"
0020 #include "Acts/Navigation/INavigationPolicy.hpp"
0021 #include "Acts/Navigation/TryAllNavigationPolicy.hpp"
0022 #include "Acts/Utilities/GraphViz.hpp"
0023 #include "Acts/Utilities/Logger.hpp"
0024 
0025 #include <algorithm>
0026 
0027 namespace {
0028 const std::string s_rootName = "Root";
0029 }
0030 
0031 namespace Acts::Experimental {
0032 
0033 ///@class BlueprintVisitor
0034 /// A class for visiting blueprint hierarchy and apply the geometry identifiers
0035 class BlueprintVisitor : public TrackingGeometryMutableVisitor {
0036  public:
0037   explicit BlueprintVisitor(
0038       const Logger &logger,
0039       std::array<const TrackingVolume *, GeometryIdentifier::getMaxVolume()>
0040           &volumesById)
0041       : TrackingGeometryMutableVisitor(true),
0042         m_volumesById(volumesById),
0043         m_logger(logger) {}
0044 
0045   void visitVolume(TrackingVolume &volume) override {
0046     GeometryIdentifier::Value iportal = 0;
0047     GeometryIdentifier::Value isensitive = 0;
0048 
0049     auto id = volume.geometryId();
0050 
0051     if (id == GeometryIdentifier{}) {
0052       auto it = std::ranges::find(m_volumesById, nullptr);
0053       if (it == m_volumesById.end()) {
0054         ACTS_ERROR("No free volume IDs left, all " << m_volumesById.size()
0055                                                    << " are used");
0056         // @TODO: Maybe link to documentation about this
0057         throw std::logic_error("No free volume IDs left");
0058       }
0059 
0060       id = GeometryIdentifier().withVolume(
0061           std::distance(m_volumesById.begin(), it) + 1);
0062 
0063       ACTS_DEBUG("Assigning volume ID " << id << " for "
0064                                         << volume.volumeName());
0065       volume.assignGeometryId(id);
0066       *it = &volume;
0067     }
0068 
0069     for (auto &portal : volume.portals()) {
0070       if (portal.surface().geometryId() != GeometryIdentifier{}) {
0071         continue;
0072       }
0073       iportal += 1;
0074       auto portalId = id.withBoundary(iportal);
0075       ACTS_DEBUG("Assigning portal ID: " << portalId);
0076       portal.surface().assignGeometryId(portalId);
0077     }
0078     for (auto &surface : volume.surfaces()) {
0079       if (surface.geometryId() != GeometryIdentifier{}) {
0080         continue;
0081       }
0082       isensitive += 1;
0083       auto surfaceId = id.withSensitive(isensitive);
0084       ACTS_DEBUG("Assigning surface ID: " << surfaceId);
0085       surface.assignGeometryId(surfaceId);
0086     }
0087   }
0088 
0089  private:
0090   std::array<const TrackingVolume *, GeometryIdentifier::getMaxVolume()>
0091       &m_volumesById;
0092   const Logger &m_logger;
0093   const Acts::Logger &logger() const { return m_logger; }
0094 };
0095 
0096 Blueprint::Blueprint(const Config &config) : m_cfg(config) {}
0097 
0098 const std::string &Blueprint::name() const {
0099   return s_rootName;
0100 }
0101 
0102 Volume &Blueprint::build(const BlueprintOptions & /*options*/,
0103                          const GeometryContext & /*gctx*/,
0104                          const Logger & /*logger*/) {
0105   throw std::logic_error("Root node cannot be built");
0106 }
0107 
0108 PortalShellBase &Blueprint::connect(const BlueprintOptions & /*options*/,
0109                                     const GeometryContext & /*gctx*/,
0110                                     const Logger & /*logger*/) {
0111   throw std::logic_error("Root node cannot be connected");
0112 }
0113 
0114 void Blueprint::finalize(const BlueprintOptions & /*options*/,
0115                          const GeometryContext & /*gctx*/,
0116                          TrackingVolume & /*parent*/,
0117                          const Logger & /*logger*/) {
0118   throw std::logic_error("Root node cannot be finalized");
0119 }
0120 
0121 void Blueprint::addToGraphviz(std::ostream &os) const {
0122   GraphViz::Node node{
0123       .id = name(), .label = "World", .shape = GraphViz::Shape::House};
0124 
0125   os << node;
0126   BlueprintNode::addToGraphviz(os);
0127 }
0128 
0129 std::unique_ptr<TrackingGeometry> Blueprint::construct(
0130     const BlueprintOptions &options, const GeometryContext &gctx,
0131     const Logger &logger) {
0132   using enum AxisDirection;
0133 
0134   ACTS_INFO(prefix() << "Building tracking geometry from blueprint tree");
0135 
0136   options.validate();
0137 
0138   if (m_cfg.envelope == ExtentEnvelope::Zero()) {
0139     ACTS_WARNING(prefix() << "Root node is configured with zero envelope. This "
0140                              "might lead to navigation issues");
0141   }
0142 
0143   if (children().size() != 1) {
0144     ACTS_ERROR(prefix() << "Root node must have exactly one child");
0145     throw std::logic_error("Root node must have exactly one child");
0146   }
0147 
0148   auto &child = children().at(0);
0149 
0150   ACTS_DEBUG(prefix() << "Executing building on tree");
0151   Volume &topVolume = child.build(options, gctx, logger);
0152   const auto &bounds = topVolume.volumeBounds();
0153 
0154   std::stringstream ss;
0155   bounds.toStream(ss);
0156   ACTS_DEBUG(prefix() << "have top volume: " << ss.str() << "\n"
0157                       << topVolume.transform().matrix());
0158 
0159   std::unique_ptr<TrackingVolume> world;
0160   static const std::string worldName = "World";
0161 
0162   if (const auto *cyl = dynamic_cast<const CylinderVolumeBounds *>(&bounds);
0163       cyl != nullptr) {
0164     ACTS_VERBOSE(prefix() << "Expanding cylinder bounds");
0165     using enum CylinderVolumeBounds::BoundValues;
0166 
0167     // Make a copy that we'll modify
0168     auto newBounds = std::make_shared<CylinderVolumeBounds>(*cyl);
0169 
0170     const auto &zEnv = m_cfg.envelope[AxisZ];
0171     if (zEnv[0] != zEnv[1]) {
0172       ACTS_ERROR(
0173           prefix() << "Root node cylinder envelope for z must be symmetric");
0174       throw std::logic_error(
0175           "Root node cylinder envelope for z must be "
0176           "symmetric");
0177     }
0178 
0179     const auto &rEnv = m_cfg.envelope[AxisR];
0180 
0181     newBounds->set({
0182         {eHalfLengthZ, newBounds->get(eHalfLengthZ) + zEnv[0]},
0183         {eMinR, std::max(0.0, newBounds->get(eMinR) - rEnv[0])},
0184         {eMaxR, newBounds->get(eMaxR) + rEnv[1]},
0185     });
0186 
0187     ACTS_DEBUG(prefix() << "Applied envelope to cylinder: Z=" << zEnv[0]
0188                         << ", Rmin=" << rEnv[0] << ", Rmax=" << rEnv[1]);
0189 
0190     world = std::make_unique<TrackingVolume>(topVolume.transform(),
0191                                              std::move(newBounds), worldName);
0192 
0193     // Need one-sided portal shell that connects outwards to nullptr
0194     SingleCylinderPortalShell worldShell{*world};
0195     worldShell.applyToVolume();
0196 
0197   } else if (const auto *box =
0198                  dynamic_cast<const CuboidVolumeBounds *>(&bounds);
0199              box != nullptr) {
0200     ACTS_VERBOSE(prefix() << "Expanding cuboid bounds");
0201     // Make a copy that we'll modify
0202     auto newBounds = std::make_shared<CuboidVolumeBounds>(*box);
0203 
0204     // Get the current half lengths
0205     double halfX = newBounds->get(CuboidVolumeBounds::eHalfLengthX);
0206     double halfY = newBounds->get(CuboidVolumeBounds::eHalfLengthY);
0207     double halfZ = newBounds->get(CuboidVolumeBounds::eHalfLengthZ);
0208 
0209     // Apply envelope to each dimension
0210     const auto &xEnv = m_cfg.envelope[AxisX];
0211     const auto &yEnv = m_cfg.envelope[AxisY];
0212     const auto &zEnv = m_cfg.envelope[AxisZ];
0213 
0214     // Check if envelopes are symmetric for all dimensions
0215     if (xEnv[0] != xEnv[1]) {
0216       ACTS_ERROR(
0217           prefix() << "Root node cuboid envelope for X must be symmetric");
0218       throw std::logic_error(
0219           "Root node cuboid envelope for X must be symmetric");
0220     }
0221 
0222     if (yEnv[0] != yEnv[1]) {
0223       ACTS_ERROR(
0224           prefix() << "Root node cuboid envelope for Y must be symmetric");
0225       throw std::logic_error(
0226           "Root node cuboid envelope for Y must be symmetric");
0227     }
0228 
0229     if (zEnv[0] != zEnv[1]) {
0230       ACTS_ERROR(
0231           prefix() << "Root node cuboid envelope for Z must be symmetric");
0232       throw std::logic_error(
0233           "Root node cuboid envelope for Z must be symmetric");
0234     }
0235 
0236     newBounds->set({
0237         {CuboidVolumeBounds::eHalfLengthX, halfX + xEnv[0]},
0238         {CuboidVolumeBounds::eHalfLengthY, halfY + yEnv[0]},
0239         {CuboidVolumeBounds::eHalfLengthZ, halfZ + zEnv[0]},
0240     });
0241 
0242     ACTS_DEBUG(prefix() << "Applied envelope to cuboid: X=" << xEnv[0]
0243                         << ", Y=" << yEnv[0] << ", Z=" << zEnv[0]);
0244 
0245     world = std::make_unique<TrackingVolume>(topVolume.transform(),
0246                                              std::move(newBounds), worldName);
0247 
0248     // Need one-sided portal shell that connects outwards to nullptr
0249     SingleCuboidPortalShell worldShell{*world};
0250     worldShell.applyToVolume();
0251 
0252   } else {
0253     throw std::logic_error{"Unsupported volume bounds type"};
0254   }
0255 
0256   ACTS_DEBUG(prefix() << "New root volume bounds are: "
0257                       << world->volumeBounds());
0258 
0259   world->setNavigationPolicy(std::make_unique<Acts::TryAllNavigationPolicy>(
0260       gctx, *world, logger, Acts::TryAllNavigationPolicy::Config{}));
0261 
0262   auto &shell = child.connect(options, gctx, logger);
0263 
0264   // Composite of trivial will not be converted to grid like this
0265   // Performance impact should be negligible since it's a rare case, but might
0266   // want to change
0267   shell.fill(*world);
0268 
0269   if (m_cfg.boundDeduplication) {
0270     ACTS_DEBUG("Deduplicate equivalent bounds");
0271     detail::BoundDeduplicator deduplicator{};
0272     world->apply(deduplicator);
0273   }
0274   child.finalize(options, gctx, *world, logger);
0275 
0276   std::set<std::string, std::less<>> volumeNames;
0277   std::array<const TrackingVolume *, GeometryIdentifier::getMaxVolume()>
0278       volumesById{};
0279   volumesById.fill(nullptr);
0280 
0281   // @TODO: Take this from GeometryIdentifier instead of hard-coding
0282 
0283   world->apply([&, this](TrackingVolume &volume) {
0284     if (volumeNames.contains(volume.volumeName())) {
0285       ACTS_ERROR(prefix() << "Duplicate volume name: " << volume.volumeName());
0286       throw std::logic_error("Duplicate volume name");
0287     }
0288     volumeNames.insert(volume.volumeName());
0289 
0290     if (volume.geometryId() != GeometryIdentifier{}) {
0291       // We can have multiple volumes with the same volume ID component, but
0292       // they should differ in other components like "layer"
0293       if (volumesById.at(volume.geometryId().volume() - 1) == nullptr) {
0294         volumesById.at(volume.geometryId().volume() - 1) = &volume;
0295       }
0296     }
0297 
0298     // Clear boundary surfaces!
0299     volume.clearBoundarySurfaces();
0300   });
0301 
0302   std::size_t unusedVolumeIds = std::ranges::count(volumesById, nullptr);
0303   ACTS_DEBUG(prefix() << "Number of unused volume IDs: " << unusedVolumeIds);
0304 
0305   ACTS_DEBUG(prefix() << "Assigning volume IDs for remaining volumes");
0306 
0307   BlueprintVisitor visitor{logger, volumesById};
0308   world->apply(visitor);
0309 
0310   return std::make_unique<TrackingGeometry>(
0311       std::move(world), nullptr, GeometryIdentifierHook{}, logger, false);
0312 }
0313 
0314 }  // namespace Acts::Experimental