Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-11-13 09:37:42

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/PadBlueprintNode.hpp"
0018 #include "Acts/Geometry/PortalShell.hpp"
0019 #include "Acts/Geometry/VolumeBounds.hpp"
0020 #include "Acts/Geometry/detail/AlignablePortalVisitor.hpp"
0021 #include "Acts/Geometry/detail/BoundDeduplicator.hpp"
0022 #include "Acts/Navigation/INavigationPolicy.hpp"
0023 #include "Acts/Utilities/GraphViz.hpp"
0024 #include "Acts/Utilities/Logger.hpp"
0025 
0026 #include <algorithm>
0027 
0028 namespace {
0029 const std::string s_rootName = "Root";
0030 const std::string s_worldName = "World";
0031 }  // namespace
0032 
0033 namespace Acts {
0034 
0035 ///@class BlueprintVisitor
0036 /// A class for visiting blueprint hierarchy and apply the geometry identifiers
0037 class BlueprintVisitor : public TrackingGeometryMutableVisitor {
0038  public:
0039   explicit BlueprintVisitor(
0040       const Logger &logger,
0041       std::array<const TrackingVolume *, GeometryIdentifier::getMaxVolume()>
0042           &volumesById)
0043       : TrackingGeometryMutableVisitor(true),
0044         m_volumesById(volumesById),
0045         m_logger(logger) {}
0046 
0047   void visitVolume(TrackingVolume &volume) override {
0048     GeometryIdentifier::Value iportal = 0;
0049     GeometryIdentifier::Value isensitive = 0;
0050 
0051     auto id = volume.geometryId();
0052 
0053     if (id == GeometryIdentifier{}) {
0054       auto it = std::ranges::find(m_volumesById, nullptr);
0055       if (it == m_volumesById.end()) {
0056         ACTS_ERROR("No free volume IDs left, all " << m_volumesById.size()
0057                                                    << " are used");
0058         // @TODO: Maybe link to documentation about this
0059         throw std::logic_error("No free volume IDs left");
0060       }
0061 
0062       id = GeometryIdentifier().withVolume(
0063           std::distance(m_volumesById.begin(), it) + 1);
0064 
0065       ACTS_VERBOSE("Assigning volume ID " << id << " for "
0066                                           << volume.volumeName());
0067       volume.assignGeometryId(id);
0068       *it = &volume;
0069     }
0070 
0071     for (auto &portal : volume.portals()) {
0072       if (portal.surface().geometryId() != GeometryIdentifier{}) {
0073         continue;
0074       }
0075       iportal += 1;
0076       auto portalId = id.withBoundary(iportal);
0077       ACTS_VERBOSE("Assigning portal ID: " << portalId);
0078       portal.surface().assignGeometryId(portalId);
0079     }
0080     for (auto &surface : volume.surfaces()) {
0081       if (surface.geometryId() != GeometryIdentifier{}) {
0082         continue;
0083       }
0084       isensitive += 1;
0085       auto surfaceId = id.withSensitive(isensitive);
0086       ACTS_VERBOSE("Assigning surface ID: " << surfaceId);
0087       surface.assignGeometryId(surfaceId);
0088     }
0089   }
0090 
0091  private:
0092   std::array<const TrackingVolume *, GeometryIdentifier::getMaxVolume()>
0093       &m_volumesById;
0094   const Logger &m_logger;
0095   const Acts::Logger &logger() const { return m_logger; }
0096 };
0097 
0098 Blueprint &Blueprint::setVolumeLookupFactory(VolumeLookupFactory factory) {
0099   m_volumeLookupFactory = std::move(factory);
0100   return *this;
0101 }
0102 
0103 const VolumeLookupFactory &Blueprint::volumeLookupFactory() const {
0104   return m_volumeLookupFactory;
0105 }
0106 
0107 Blueprint::Blueprint(const Config &config) : m_cfg(config) {}
0108 
0109 const std::string &Blueprint::name() const {
0110   return s_rootName;
0111 }
0112 
0113 Volume &Blueprint::build(const BlueprintOptions & /*options*/,
0114                          const GeometryContext & /*gctx*/,
0115                          const Logger & /*logger*/) {
0116   throw std::logic_error("Root node cannot be built");
0117 }
0118 
0119 PortalShellBase &Blueprint::connect(const BlueprintOptions & /*options*/,
0120                                     const GeometryContext & /*gctx*/,
0121                                     const Logger & /*logger*/) {
0122   throw std::logic_error("Root node cannot be connected");
0123 }
0124 
0125 void Blueprint::finalize(const BlueprintOptions & /*options*/,
0126                          const GeometryContext & /*gctx*/,
0127                          TrackingVolume & /*parent*/,
0128                          const Logger & /*logger*/) {
0129   throw std::logic_error("Root node cannot be finalized");
0130 }
0131 
0132 void Blueprint::addToGraphviz(std::ostream &os) const {
0133   GraphViz::Node node{
0134       .id = name(), .label = "World", .shape = GraphViz::Shape::House};
0135 
0136   os << node;
0137   BlueprintNode::addToGraphviz(os);
0138 }
0139 
0140 std::unique_ptr<TrackingGeometry> Blueprint::construct(
0141     const BlueprintOptions &options, const GeometryContext &gctx,
0142     const Logger &logger) {
0143   using enum AxisDirection;
0144 
0145   ACTS_INFO(prefix() << "Building tracking geometry from blueprint tree");
0146 
0147   options.validate();
0148 
0149   if (m_cfg.envelope == ExtentEnvelope::Zero()) {
0150     ACTS_WARNING(prefix() << "Root node is configured with zero envelope. This "
0151                              "might lead to navigation issues");
0152   }
0153 
0154   if (children().size() != 1) {
0155     ACTS_ERROR(prefix() << "Root node must have exactly one child");
0156     throw std::logic_error("Root node must have exactly one child");
0157   }
0158 
0159   BlueprintNode &child = children().at(0);
0160 
0161   ACTS_DEBUG(prefix() << "Executing building on tree");
0162 
0163   // The world volume is not represented by a node in the tree: we size it here
0164   // from the built subtree, and own it directly until it is handed to the
0165   // TrackingGeometry at the end. The build/connect/finalize sequence below
0166   // mirrors what StaticBlueprintNode does for an ordinary enclosing volume,
0167   // minus registering itself with a parent (the world *is* the parent).
0168   const Volume &top = child.build(options, gctx, logger);
0169 
0170   std::unique_ptr<TrackingVolume> world = PadBlueprintNode::padded(
0171       gctx, top, m_cfg.envelope, s_worldName, std::nullopt,
0172       PadBlueprintNode::Centering::Centered, logger);
0173 
0174   ACTS_DEBUG(prefix() << "New root volume bounds are: "
0175                       << world->volumeBounds());
0176 
0177   // Register the world on the outside of the child's shell, then produce the
0178   // world's own (outermost) portals.
0179   child.connect(options, gctx, logger).fill(*world);
0180   std::unique_ptr<PortalShellBase> worldShell =
0181       PortalShellBase::makeSingle(gctx, *world);
0182 
0183   if (m_cfg.boundDeduplication) {
0184     ACTS_DEBUG("Deduplicate equivalent bounds");
0185     detail::BoundDeduplicator deduplicator{};
0186     world->apply(deduplicator);
0187   }
0188 
0189   child.finalize(options, gctx, *world, logger);
0190   worldShell->applyToVolume();
0191   world->setNavigationPolicy(
0192       options.defaultNavigationPolicyFactory->build(gctx, *world, logger));
0193 
0194   std::set<std::string, std::less<>> volumeNames;
0195   std::array<const TrackingVolume *, GeometryIdentifier::getMaxVolume()>
0196       volumesById{};
0197   volumesById.fill(nullptr);
0198 
0199   // @TODO: Take this from GeometryIdentifier instead of hard-coding
0200 
0201   world->apply([&, this](TrackingVolume &volume) {
0202     if (volumeNames.contains(volume.volumeName())) {
0203       ACTS_ERROR(prefix() << "Duplicate volume name: " << volume.volumeName());
0204       throw std::logic_error("Duplicate volume name");
0205     }
0206     volumeNames.insert(volume.volumeName());
0207 
0208     if (volume.geometryId() != GeometryIdentifier{}) {
0209       // We can have multiple volumes with the same volume ID component, but
0210       // they should differ in other components like "layer"
0211       if (volumesById.at(volume.geometryId().volume() - 1) == nullptr) {
0212         volumesById.at(volume.geometryId().volume() - 1) = &volume;
0213       }
0214     }
0215 
0216     // Clear boundary surfaces!
0217     volume.clearBoundarySurfaces();
0218   });
0219 
0220   std::size_t unusedVolumeIds = std::ranges::count(volumesById, nullptr);
0221   ACTS_DEBUG(prefix() << "Number of unused volume IDs: " << unusedVolumeIds);
0222 
0223   ACTS_DEBUG(prefix() << "Assigning volume IDs for remaining volumes");
0224 
0225   BlueprintVisitor visitor{logger, volumesById};
0226   world->apply(visitor);
0227 
0228   Acts::detail::AlignablePortalVisitor alignPortals{gctx, logger};
0229   world->apply(alignPortals);
0230 
0231   // All navigation policies are attached at this point: initialize each one's
0232   // statelessness cache (probing whether it pushes only default states) so the
0233   // navigator can skip the per-volume-entry state creation for volumes with
0234   // stateless policies.
0235   world->apply([&](TrackingVolume &volume) {
0236     if (INavigationPolicy *policy = volume.navigationPolicy();
0237         policy != nullptr) {
0238       policy->initializeStatelessCache(gctx, logger);
0239     }
0240   });
0241 
0242   if (m_volumeLookupFactory) {
0243     auto lookup = m_volumeLookupFactory(gctx, *world, logger);
0244     if (!lookup) {
0245       throw std::invalid_argument("Volume lookup factory returned nullptr");
0246     }
0247     world->setVolumeLookup(std::move(lookup));
0248   }
0249   return std::make_unique<TrackingGeometry>(
0250       std::shared_ptr<TrackingVolume>(std::move(world)), nullptr,
0251       GeometryIdentifierHook{}, logger, false);
0252 }
0253 
0254 }  // namespace Acts