Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-12 07:51:53

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