Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-14 08:18:15

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(const Config &config) : m_cfg(config) {}
0099 
0100 const std::string &Blueprint::name() const {
0101   return s_rootName;
0102 }
0103 
0104 Volume &Blueprint::build(const BlueprintOptions & /*options*/,
0105                          const GeometryContext & /*gctx*/,
0106                          const Logger & /*logger*/) {
0107   throw std::logic_error("Root node cannot be built");
0108 }
0109 
0110 PortalShellBase &Blueprint::connect(const BlueprintOptions & /*options*/,
0111                                     const GeometryContext & /*gctx*/,
0112                                     const Logger & /*logger*/) {
0113   throw std::logic_error("Root node cannot be connected");
0114 }
0115 
0116 void Blueprint::finalize(const BlueprintOptions & /*options*/,
0117                          const GeometryContext & /*gctx*/,
0118                          TrackingVolume & /*parent*/,
0119                          const Logger & /*logger*/) {
0120   throw std::logic_error("Root node cannot be finalized");
0121 }
0122 
0123 void Blueprint::addToGraphviz(std::ostream &os) const {
0124   GraphViz::Node node{
0125       .id = name(), .label = "World", .shape = GraphViz::Shape::House};
0126 
0127   os << node;
0128   BlueprintNode::addToGraphviz(os);
0129 }
0130 
0131 std::unique_ptr<TrackingGeometry> Blueprint::construct(
0132     const BlueprintOptions &options, const GeometryContext &gctx,
0133     const Logger &logger) {
0134   using enum AxisDirection;
0135 
0136   ACTS_INFO(prefix() << "Building tracking geometry from blueprint tree");
0137 
0138   options.validate();
0139 
0140   if (m_cfg.envelope == ExtentEnvelope::Zero()) {
0141     ACTS_WARNING(prefix() << "Root node is configured with zero envelope. This "
0142                              "might lead to navigation issues");
0143   }
0144 
0145   if (children().size() != 1) {
0146     ACTS_ERROR(prefix() << "Root node must have exactly one child");
0147     throw std::logic_error("Root node must have exactly one child");
0148   }
0149 
0150   BlueprintNode &child = children().at(0);
0151 
0152   ACTS_DEBUG(prefix() << "Executing building on tree");
0153 
0154   // The world volume is not represented by a node in the tree: we size it here
0155   // from the built subtree, and own it directly until it is handed to the
0156   // TrackingGeometry at the end. The build/connect/finalize sequence below
0157   // mirrors what StaticBlueprintNode does for an ordinary enclosing volume,
0158   // minus registering itself with a parent (the world *is* the parent).
0159   const Volume &top = child.build(options, gctx, logger);
0160 
0161   std::unique_ptr<TrackingVolume> world = PadBlueprintNode::padded(
0162       gctx, top, m_cfg.envelope, s_worldName, std::nullopt,
0163       PadBlueprintNode::Centering::Centered, logger);
0164 
0165   ACTS_DEBUG(prefix() << "New root volume bounds are: "
0166                       << world->volumeBounds());
0167 
0168   // Register the world on the outside of the child's shell, then produce the
0169   // world's own (outermost) portals.
0170   child.connect(options, gctx, logger).fill(*world);
0171   std::unique_ptr<PortalShellBase> worldShell =
0172       PortalShellBase::makeSingle(gctx, *world);
0173 
0174   if (m_cfg.boundDeduplication) {
0175     ACTS_DEBUG("Deduplicate equivalent bounds");
0176     detail::BoundDeduplicator deduplicator{};
0177     world->apply(deduplicator);
0178   }
0179 
0180   child.finalize(options, gctx, *world, logger);
0181   worldShell->applyToVolume();
0182   world->setNavigationPolicy(
0183       options.defaultNavigationPolicyFactory->build(gctx, *world, logger));
0184 
0185   std::set<std::string, std::less<>> volumeNames;
0186   std::array<const TrackingVolume *, GeometryIdentifier::getMaxVolume()>
0187       volumesById{};
0188   volumesById.fill(nullptr);
0189 
0190   // @TODO: Take this from GeometryIdentifier instead of hard-coding
0191 
0192   world->apply([&, this](TrackingVolume &volume) {
0193     if (volumeNames.contains(volume.volumeName())) {
0194       ACTS_ERROR(prefix() << "Duplicate volume name: " << volume.volumeName());
0195       throw std::logic_error("Duplicate volume name");
0196     }
0197     volumeNames.insert(volume.volumeName());
0198 
0199     if (volume.geometryId() != GeometryIdentifier{}) {
0200       // We can have multiple volumes with the same volume ID component, but
0201       // they should differ in other components like "layer"
0202       if (volumesById.at(volume.geometryId().volume() - 1) == nullptr) {
0203         volumesById.at(volume.geometryId().volume() - 1) = &volume;
0204       }
0205     }
0206 
0207     // Clear boundary surfaces!
0208     volume.clearBoundarySurfaces();
0209   });
0210 
0211   std::size_t unusedVolumeIds = std::ranges::count(volumesById, nullptr);
0212   ACTS_DEBUG(prefix() << "Number of unused volume IDs: " << unusedVolumeIds);
0213 
0214   ACTS_DEBUG(prefix() << "Assigning volume IDs for remaining volumes");
0215 
0216   BlueprintVisitor visitor{logger, volumesById};
0217   world->apply(visitor);
0218 
0219   Acts::detail::AlignablePortalVisitor alignPortals{gctx, logger};
0220   world->apply(alignPortals);
0221 
0222   // All navigation policies are attached at this point: initialize each one's
0223   // statelessness cache (probing whether it pushes only default states) so the
0224   // navigator can skip the per-volume-entry state creation for volumes with
0225   // stateless policies.
0226   world->apply([&](TrackingVolume &volume) {
0227     if (INavigationPolicy *policy = volume.navigationPolicy();
0228         policy != nullptr) {
0229       policy->initializeStatelessCache(gctx, logger);
0230     }
0231   });
0232 
0233   return std::make_unique<TrackingGeometry>(
0234       std::shared_ptr<TrackingVolume>(std::move(world)), nullptr,
0235       GeometryIdentifierHook{}, logger, false);
0236 }
0237 
0238 }  // namespace Acts