Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2026-07-16 08:07:46

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.localToGlobalTransform(gctx).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 =
0191         std::make_unique<TrackingVolume>(topVolume.localToGlobalTransform(gctx),
0192                                          std::move(newBounds), worldName);
0193 
0194     // Need one-sided portal shell that connects outwards to nullptr
0195     SingleCylinderPortalShell worldShell{*world};
0196     worldShell.applyToVolume();
0197 
0198   } else if (const auto *box =
0199                  dynamic_cast<const CuboidVolumeBounds *>(&bounds);
0200              box != nullptr) {
0201     ACTS_VERBOSE(prefix() << "Expanding cuboid bounds");
0202     // Make a copy that we'll modify
0203     auto newBounds = std::make_shared<CuboidVolumeBounds>(*box);
0204 
0205     // Get the current half lengths
0206     double halfX = newBounds->get(CuboidVolumeBounds::eHalfLengthX);
0207     double halfY = newBounds->get(CuboidVolumeBounds::eHalfLengthY);
0208     double halfZ = newBounds->get(CuboidVolumeBounds::eHalfLengthZ);
0209 
0210     // Apply envelope to each dimension
0211     const auto &xEnv = m_cfg.envelope[AxisX];
0212     const auto &yEnv = m_cfg.envelope[AxisY];
0213     const auto &zEnv = m_cfg.envelope[AxisZ];
0214 
0215     // Check if envelopes are symmetric for all dimensions
0216     if (xEnv[0] != xEnv[1]) {
0217       ACTS_ERROR(
0218           prefix() << "Root node cuboid envelope for X must be symmetric");
0219       throw std::logic_error(
0220           "Root node cuboid envelope for X must be symmetric");
0221     }
0222 
0223     if (yEnv[0] != yEnv[1]) {
0224       ACTS_ERROR(
0225           prefix() << "Root node cuboid envelope for Y must be symmetric");
0226       throw std::logic_error(
0227           "Root node cuboid envelope for Y must be symmetric");
0228     }
0229 
0230     if (zEnv[0] != zEnv[1]) {
0231       ACTS_ERROR(
0232           prefix() << "Root node cuboid envelope for Z must be symmetric");
0233       throw std::logic_error(
0234           "Root node cuboid envelope for Z must be symmetric");
0235     }
0236 
0237     newBounds->set({
0238         {CuboidVolumeBounds::eHalfLengthX, halfX + xEnv[0]},
0239         {CuboidVolumeBounds::eHalfLengthY, halfY + yEnv[0]},
0240         {CuboidVolumeBounds::eHalfLengthZ, halfZ + zEnv[0]},
0241     });
0242 
0243     ACTS_DEBUG(prefix() << "Applied envelope to cuboid: X=" << xEnv[0]
0244                         << ", Y=" << yEnv[0] << ", Z=" << zEnv[0]);
0245 
0246     world =
0247         std::make_unique<TrackingVolume>(topVolume.localToGlobalTransform(gctx),
0248                                          std::move(newBounds), worldName);
0249 
0250     // Need one-sided portal shell that connects outwards to nullptr
0251     SingleCuboidPortalShell worldShell{*world};
0252     worldShell.applyToVolume();
0253 
0254   } else {
0255     throw std::logic_error{"Unsupported volume bounds type"};
0256   }
0257 
0258   ACTS_DEBUG(prefix() << "New root volume bounds are: "
0259                       << world->volumeBounds());
0260 
0261   world->setNavigationPolicy(std::make_unique<Acts::TryAllNavigationPolicy>(
0262       gctx, *world, logger, Acts::TryAllNavigationPolicy::Config{}));
0263 
0264   auto &shell = child.connect(options, gctx, logger);
0265 
0266   // Composite of trivial will not be converted to grid like this
0267   // Performance impact should be negligible since it's a rare case, but might
0268   // want to change
0269   shell.fill(*world);
0270 
0271   if (m_cfg.boundDeduplication) {
0272     ACTS_DEBUG("Deduplicate equivalent bounds");
0273     detail::BoundDeduplicator deduplicator{};
0274     world->apply(deduplicator);
0275   }
0276   child.finalize(options, gctx, *world, logger);
0277 
0278   std::set<std::string, std::less<>> volumeNames;
0279   std::array<const TrackingVolume *, GeometryIdentifier::getMaxVolume()>
0280       volumesById{};
0281   volumesById.fill(nullptr);
0282 
0283   // @TODO: Take this from GeometryIdentifier instead of hard-coding
0284 
0285   world->apply([&, this](TrackingVolume &volume) {
0286     if (volumeNames.contains(volume.volumeName())) {
0287       ACTS_ERROR(prefix() << "Duplicate volume name: " << volume.volumeName());
0288       throw std::logic_error("Duplicate volume name");
0289     }
0290     volumeNames.insert(volume.volumeName());
0291 
0292     if (volume.geometryId() != GeometryIdentifier{}) {
0293       // We can have multiple volumes with the same volume ID component, but
0294       // they should differ in other components like "layer"
0295       if (volumesById.at(volume.geometryId().volume() - 1) == nullptr) {
0296         volumesById.at(volume.geometryId().volume() - 1) = &volume;
0297       }
0298     }
0299 
0300     // Clear boundary surfaces!
0301     volume.clearBoundarySurfaces();
0302   });
0303 
0304   std::size_t unusedVolumeIds = std::ranges::count(volumesById, nullptr);
0305   ACTS_DEBUG(prefix() << "Number of unused volume IDs: " << unusedVolumeIds);
0306 
0307   ACTS_DEBUG(prefix() << "Assigning volume IDs for remaining volumes");
0308 
0309   BlueprintVisitor visitor{logger, volumesById};
0310   world->apply(visitor);
0311 
0312   return std::make_unique<TrackingGeometry>(
0313       std::move(world), nullptr, GeometryIdentifierHook{}, logger, false);
0314 }
0315 
0316 }  // namespace Acts::Experimental