File indexing completed on 2026-07-16 07:46:58
0001
0002
0003
0004
0005
0006
0007
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/Navigation/TryAllNavigationPolicy.hpp"
0024 #include "Acts/Utilities/GraphViz.hpp"
0025 #include "Acts/Utilities/Logger.hpp"
0026
0027 #include <algorithm>
0028
0029 namespace {
0030 const std::string s_rootName = "Root";
0031 }
0032
0033 namespace Acts::Experimental {
0034
0035
0036
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
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 & ,
0105 const GeometryContext & ,
0106 const Logger & ) {
0107 throw std::logic_error("Root node cannot be built");
0108 }
0109
0110 PortalShellBase &Blueprint::connect(const BlueprintOptions & ,
0111 const GeometryContext & ,
0112 const Logger & ) {
0113 throw std::logic_error("Root node cannot be connected");
0114 }
0115
0116 void Blueprint::finalize(const BlueprintOptions & ,
0117 const GeometryContext & ,
0118 TrackingVolume & ,
0119 const 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 auto child = childPtr()[0];
0151 clearChildren();
0152
0153 ACTS_DEBUG(prefix() << "Executing building on tree");
0154
0155 auto autoSizingNode = std::make_shared<Acts::Experimental::PadBlueprintNode>(
0156 "World", m_cfg.envelope);
0157
0158 autoSizingNode->addChild(std::move(child));
0159
0160 autoSizingNode->build(options, gctx, logger);
0161
0162 auto world = autoSizingNode->trackingVolume();
0163
0164 ACTS_DEBUG(prefix() << "New root volume bounds are: "
0165 << world->volumeBounds());
0166
0167 world->setNavigationPolicy(std::make_unique<Acts::TryAllNavigationPolicy>(
0168 gctx, *world, logger, Acts::TryAllNavigationPolicy::Config{}));
0169
0170 autoSizingNode->connect(options, gctx, logger);
0171
0172 if (m_cfg.boundDeduplication) {
0173 ACTS_DEBUG("Deduplicate equivalent bounds");
0174 detail::BoundDeduplicator deduplicator{};
0175 world->apply(deduplicator);
0176 }
0177 autoSizingNode->finalize(options, gctx, *world, logger);
0178
0179 std::set<std::string, std::less<>> volumeNames;
0180 std::array<const TrackingVolume *, GeometryIdentifier::getMaxVolume()>
0181 volumesById{};
0182 volumesById.fill(nullptr);
0183
0184
0185
0186 world->apply([&, this](TrackingVolume &volume) {
0187 if (volumeNames.contains(volume.volumeName())) {
0188 ACTS_ERROR(prefix() << "Duplicate volume name: " << volume.volumeName());
0189 throw std::logic_error("Duplicate volume name");
0190 }
0191 volumeNames.insert(volume.volumeName());
0192
0193 if (volume.geometryId() != GeometryIdentifier{}) {
0194
0195
0196 if (volumesById.at(volume.geometryId().volume() - 1) == nullptr) {
0197 volumesById.at(volume.geometryId().volume() - 1) = &volume;
0198 }
0199 }
0200
0201
0202 volume.clearBoundarySurfaces();
0203 });
0204
0205 std::size_t unusedVolumeIds = std::ranges::count(volumesById, nullptr);
0206 ACTS_DEBUG(prefix() << "Number of unused volume IDs: " << unusedVolumeIds);
0207
0208 ACTS_DEBUG(prefix() << "Assigning volume IDs for remaining volumes");
0209
0210 BlueprintVisitor visitor{logger, volumesById};
0211 world->apply(visitor);
0212
0213 Acts::detail::AlignablePortalVisitor alignPortals{gctx, logger};
0214 world->apply(alignPortals);
0215
0216 return std::make_unique<TrackingGeometry>(
0217 std::shared_ptr<TrackingVolume>(autoSizingNode->releaseVolume()), nullptr,
0218 GeometryIdentifierHook{}, logger, false);
0219 }
0220
0221 }