File indexing completed on 2026-11-13 09:37:42
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/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 }
0032
0033 namespace Acts {
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::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 & ,
0114 const GeometryContext & ,
0115 const Logger & ) {
0116 throw std::logic_error("Root node cannot be built");
0117 }
0118
0119 PortalShellBase &Blueprint::connect(const BlueprintOptions & ,
0120 const GeometryContext & ,
0121 const Logger & ) {
0122 throw std::logic_error("Root node cannot be connected");
0123 }
0124
0125 void Blueprint::finalize(const BlueprintOptions & ,
0126 const GeometryContext & ,
0127 TrackingVolume & ,
0128 const 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
0164
0165
0166
0167
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
0178
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
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
0210
0211 if (volumesById.at(volume.geometryId().volume() - 1) == nullptr) {
0212 volumesById.at(volume.geometryId().volume() - 1) = &volume;
0213 }
0214 }
0215
0216
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
0232
0233
0234
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 }