File indexing completed on 2026-08-27 08:31:04
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Geometry/TrackingGeometry.hpp"
0010
0011 #include "Acts/Geometry/GeometryContext.hpp"
0012 #include "Acts/Geometry/GeometryIdentifier.hpp"
0013 #include "Acts/Geometry/GeometryObject.hpp"
0014 #include "Acts/Geometry/Portal.hpp"
0015 #include "Acts/Geometry/TrackingGeometryVisitor.hpp"
0016 #include "Acts/Geometry/TrackingVolume.hpp"
0017 #include "Acts/Material/ProtoVolumeMaterial.hpp"
0018 #include "Acts/Surfaces/BoundaryTolerance.hpp"
0019 #include "Acts/Surfaces/Surface.hpp"
0020 #include "Acts/Surfaces/SurfaceError.hpp"
0021
0022 #include <cassert>
0023 #include <cstddef>
0024
0025 namespace Acts {
0026
0027 class Gen1GeometryClosureVisitor : public TrackingGeometryMutableVisitor {
0028 public:
0029 Gen1GeometryClosureVisitor(const Logger& logger,
0030 const IMaterialDecorator* materialDecorator,
0031 const GeometryIdentifierHook& hook)
0032 : m_logger(&logger),
0033 m_materialDecorator(materialDecorator),
0034 m_hook(&hook) {
0035 ACTS_VERBOSE("Creating Gen1GeometryClosureVisitor");
0036 }
0037
0038 const Logger& logger() const { return *m_logger; }
0039
0040 void visitVolume(TrackingVolume& volume) override {
0041 ACTS_DEBUG("Volume: " << volume.volumeName());
0042
0043
0044 m_volumeID = GeometryIdentifier().withVolume(m_volumeID.volume() + 1);
0045
0046 m_iboundary = 0;
0047
0048 m_ilayer = 0;
0049
0050
0051 ACTS_VERBOSE("~> volumeID: " << m_volumeID);
0052 volume.assignGeometryId(m_volumeID);
0053
0054
0055 if (m_materialDecorator != nullptr) {
0056 ACTS_VERBOSE("Decorating volume " << volume.volumeName()
0057 << " with material");
0058 m_materialDecorator->decorate(volume);
0059 }
0060 if (!volume.hasMaterial() && volume.motherVolume() != nullptr &&
0061 volume.motherVolume()->hasMaterial()) {
0062 auto protoMaterial = dynamic_cast<const ProtoVolumeMaterial*>(
0063 volume.motherVolume()->volumeMaterial());
0064 if (protoMaterial == nullptr) {
0065 volume.assignVolumeMaterial(volume.motherVolume()->volumeMaterialPtr());
0066 }
0067 }
0068 }
0069
0070 void visitBoundarySurface(
0071 BoundarySurfaceT<TrackingVolume>& boundary) override {
0072 ACTS_DEBUG("BoundarySurface: " << boundary.surfaceRepresentation().name());
0073
0074 auto& bSurface = boundary.surfaceRepresentation();
0075
0076 m_iboundary += 1;
0077 auto boundaryID = GeometryIdentifier(m_volumeID).withBoundary(m_iboundary);
0078 ACTS_VERBOSE("~> boundaryID: " << boundaryID);
0079
0080 auto& mutableBSurface = *(const_cast<RegularSurface*>(&bSurface));
0081
0082
0083 ACTS_VERBOSE("~> assigning boundaryID: " << boundaryID);
0084 mutableBSurface.assignGeometryId(boundaryID);
0085
0086
0087 if (m_materialDecorator != nullptr) {
0088 ACTS_VERBOSE("Decorating boundary surface " << bSurface.name()
0089 << " with material");
0090 m_materialDecorator->decorate(mutableBSurface);
0091 }
0092 }
0093
0094 void visitLayer(Layer& layer) override {
0095 ACTS_DEBUG("Close Layer");
0096
0097 m_ilayer += 1;
0098 auto layerID = GeometryIdentifier(m_volumeID).withLayer(m_ilayer);
0099 ACTS_VERBOSE("~> layerID: " << layerID);
0100
0101
0102 layer.closeGeometry(m_materialDecorator, layerID, *m_hook, *m_logger);
0103 }
0104
0105 const Logger* m_logger;
0106 GeometryIdentifier m_volumeID;
0107 GeometryIdentifier::Value m_iboundary = 0;
0108 GeometryIdentifier::Value m_ilayer = 0;
0109 const IMaterialDecorator* m_materialDecorator = nullptr;
0110 const GeometryIdentifierHook* m_hook = nullptr;
0111
0112 std::unordered_map<GeometryIdentifier, const TrackingVolume*> m_volumesById{};
0113 std::unordered_map<GeometryIdentifier, const Surface*> m_surfacesById{};
0114 };
0115
0116 namespace {
0117 class GeometryIdMapVisitor : public TrackingGeometryVisitor {
0118 private:
0119 void checkIdentifier(const GeometryObject& obj, std::string_view type) {
0120 if (obj.geometryId() == GeometryIdentifier{}) {
0121 std::stringstream ss;
0122 ss << "Encountered " << type << " with no geometry ID";
0123 throw std::invalid_argument(ss.str());
0124 }
0125
0126 ACTS_VERBOSE("Checking identifier for " << type << ": "
0127 << obj.geometryId());
0128
0129 auto [it, inserted] = m_objectsById.emplace(obj.geometryId(), &obj);
0130
0131 if (!inserted && it->second != &obj) {
0132 std::stringstream ss;
0133 ss << "Duplicate " << type << " ID: " << obj.geometryId() << ": & "
0134 << it->second << " != " << &obj;
0135 if (const auto* other = dynamic_cast<const TrackingVolume*>(it->second);
0136 other != nullptr) {
0137 ss << " (" << other->volumeName() << ")";
0138 }
0139 ACTS_ERROR(ss.str());
0140 throw std::invalid_argument(ss.str());
0141 } else {
0142 ACTS_VERBOSE("Inserted " << type << " ID: " << obj.geometryId()
0143 << " pointing at " << &obj);
0144 }
0145 }
0146
0147 const Logger& logger() const { return m_logger; }
0148 const Logger& m_logger;
0149
0150 public:
0151 explicit GeometryIdMapVisitor(const Logger& logger) : m_logger(logger) {}
0152
0153 void visitVolume(const TrackingVolume& volume) override {
0154 std::string label = "volume(" + volume.volumeName() + ")";
0155 checkIdentifier(volume, label);
0156
0157 m_volumesById.emplace(volume.geometryId(), &volume);
0158 }
0159
0160 void visitSurface(const Surface& surface) override {
0161 if (surface.geometryId() == GeometryIdentifier{}) {
0162 std::cout << "Surface has no geometry ID: "
0163 << surface.toStream(
0164 GeometryContext::dangerouslyDefaultConstruct())
0165 << std::endl;
0166 throw std::invalid_argument("Surface has no geometry ID");
0167 }
0168
0169 checkIdentifier(surface, "surface");
0170
0171 m_surfacesById.emplace(surface.geometryId(), &surface);
0172 }
0173
0174 void visitLayer(const Layer& layer) override {
0175
0176
0177
0178 if (layer.geometryId() != layer.surfaceRepresentation().geometryId()) {
0179 ACTS_ERROR("Layer ID mismatch: "
0180 << layer.geometryId()
0181 << " != " << layer.surfaceRepresentation().geometryId());
0182 throw std::invalid_argument("Layer ID mismatch");
0183 }
0184 }
0185
0186 void visitBoundarySurface(
0187 const BoundarySurfaceT<TrackingVolume>& boundary) override {
0188 const auto& surface = boundary.surfaceRepresentation();
0189 checkIdentifier(surface, "boundary surface");
0190 m_surfacesById.emplace(surface.geometryId(), &surface);
0191
0192
0193
0194 m_boundariesBySurface.emplace(&surface, &boundary);
0195 }
0196
0197 void visitPortal(const Portal& portal) override {
0198 const auto& surface = portal.surface();
0199 checkIdentifier(surface, "portal");
0200 m_surfacesById.emplace(surface.geometryId(), &surface);
0201
0202
0203
0204 m_portalsBySurface.emplace(&surface, &portal);
0205
0206 for (const auto& tag : portal.tags()) {
0207 auto [it, inserted] = m_portalsByTag.try_emplace(tag, &portal);
0208
0209
0210
0211 if (!inserted && it->second != &portal) {
0212 std::stringstream ss;
0213 ss << "Duplicate portal tag: " << tag;
0214 ACTS_ERROR(ss.str());
0215 throw std::invalid_argument(ss.str());
0216 }
0217 }
0218 }
0219
0220 std::unordered_map<GeometryIdentifier, const TrackingVolume*> m_volumesById{};
0221 std::unordered_map<GeometryIdentifier, const Surface*> m_surfacesById{};
0222 detail::PortalTagMap m_portalsByTag{};
0223 detail::PortalSurfaceMap m_portalsBySurface{};
0224 detail::BoundarySurfaceMap m_boundariesBySurface{};
0225
0226 std::unordered_map<GeometryIdentifier, const GeometryObject*> m_objectsById{};
0227 };
0228
0229 }
0230 TrackingGeometry::TrackingGeometry(
0231 const MutableTrackingVolumePtr& highestVolume,
0232 const IMaterialDecorator* materialDecorator,
0233 const GeometryIdentifierHook& hook, const Logger& logger, bool close)
0234 : m_world(highestVolume) {
0235 if (close) {
0236 ACTS_DEBUG("Closing tracking geometry with Gen1 assignment");
0237 Gen1GeometryClosureVisitor visitor{logger, materialDecorator, hook};
0238 apply(visitor);
0239 }
0240
0241 GeometryIdMapVisitor mapVisitor{logger};
0242 apply(mapVisitor);
0243 m_volumesById = std::move(mapVisitor.m_volumesById);
0244 m_surfacesById = std::move(mapVisitor.m_surfacesById);
0245 m_portalsByTag = std::move(mapVisitor.m_portalsByTag);
0246 m_portalsBySurface = std::move(mapVisitor.m_portalsBySurface);
0247 m_boundariesBySurface = std::move(mapVisitor.m_boundariesBySurface);
0248
0249 ACTS_DEBUG("TrackingGeometry created with "
0250 << m_volumesById.size() << " volumes and " << m_surfacesById.size()
0251 << " surfaces");
0252
0253 m_volumesById.rehash(0);
0254 m_surfacesById.rehash(0);
0255 m_portalsByTag.rehash(0);
0256 m_portalsBySurface.rehash(0);
0257 m_boundariesBySurface.rehash(0);
0258 }
0259
0260 TrackingGeometry::~TrackingGeometry() = default;
0261
0262 Result<const TrackingVolume*> TrackingGeometry::resolveLowestTrackingVolume(
0263 const GeometryContext& gctx, const Vector3& gp,
0264 const std::optional<Vector3>& direction, const Surface* associatedSurface,
0265 double tolerance) const {
0266 assert(
0267 (associatedSurface == nullptr || !direction.has_value() ||
0268 associatedSurface->isOnSurface(
0269 gctx, gp, *direction, BoundaryTolerance::Infinite(), tolerance)) &&
0270 "The associated surface must contain the position");
0271
0272 const TrackingVolume* volume =
0273 m_world->lowestTrackingVolume(gctx, gp, tolerance);
0274 if (volume == nullptr) {
0275 return nullptr;
0276 }
0277
0278 if (associatedSurface == nullptr || !direction.has_value()) {
0279
0280 return volume;
0281 }
0282
0283
0284
0285
0286
0287
0288
0289
0290
0291
0292
0293
0294 auto isOnBoundary = [&]() {
0295 return associatedSurface->isOnSurface(gctx, gp, *direction,
0296 BoundaryTolerance::None(), tolerance);
0297 };
0298
0299 if (auto it = m_portalsBySurface.find(associatedSurface);
0300 it != m_portalsBySurface.end()) {
0301 if (!isOnBoundary()) {
0302 return SurfaceError::GlobalPositionNotOnSurface;
0303 }
0304 return it->second->resolveVolume(gctx, gp, *direction);
0305 }
0306
0307 if (auto it = m_boundariesBySurface.find(associatedSurface);
0308 it != m_boundariesBySurface.end()) {
0309 if (!isOnBoundary()) {
0310 return SurfaceError::GlobalPositionNotOnSurface;
0311 }
0312 return it->second->attachedVolume(gctx, gp, *direction);
0313 }
0314
0315
0316 return volume;
0317 }
0318
0319 const TrackingVolume* TrackingGeometry::lowestTrackingVolume(
0320 const GeometryContext& gctx, const Vector3& gp) const {
0321 auto result = resolveLowestTrackingVolume(gctx, gp);
0322 if (!result.ok()) {
0323 return nullptr;
0324 }
0325 return *result;
0326 }
0327
0328 const TrackingVolume* TrackingGeometry::highestTrackingVolume() const {
0329 return m_world.get();
0330 }
0331
0332 TrackingVolume* TrackingGeometry::highestTrackingVolume() {
0333 return m_world.get();
0334 }
0335
0336 std::shared_ptr<const TrackingVolume>
0337 TrackingGeometry::highestTrackingVolumePtr() const {
0338 return m_world;
0339 }
0340
0341 const Layer* TrackingGeometry::associatedLayer(const GeometryContext& gctx,
0342 const Vector3& gp) const {
0343 auto lowestVol = resolveLowestTrackingVolume(gctx, gp);
0344 if (!lowestVol.ok() || *lowestVol == nullptr) {
0345 return nullptr;
0346 }
0347 return (*lowestVol)->associatedLayer(gctx, gp);
0348 }
0349
0350 const TrackingVolume* TrackingGeometry::findVolume(
0351 GeometryIdentifier id) const {
0352 auto vol = m_volumesById.find(id);
0353 if (vol == m_volumesById.end()) {
0354 return nullptr;
0355 }
0356 return vol->second;
0357 }
0358
0359 const TrackingVolume* TrackingGeometry::findVolumeByName(
0360 std::string_view name) const {
0361 const TrackingVolume* found = nullptr;
0362 apply([&](const TrackingVolume& volume) {
0363 if (found == nullptr && volume.volumeName() == name) {
0364 found = &volume;
0365 }
0366 });
0367 return found;
0368 }
0369
0370 const Surface* TrackingGeometry::findSurface(GeometryIdentifier id) const {
0371 auto srf = m_surfacesById.find(id);
0372 if (srf == m_surfacesById.end()) {
0373 return nullptr;
0374 }
0375 return srf->second;
0376 }
0377
0378 const Portal* TrackingGeometry::findPortal(std::string_view tag) const {
0379 auto it = m_portalsByTag.find(tag);
0380 if (it == m_portalsByTag.end()) {
0381 return nullptr;
0382 }
0383 return it->second;
0384 }
0385
0386 const std::unordered_map<GeometryIdentifier, const Surface*>&
0387 TrackingGeometry::geoIdSurfaceMap() const {
0388 return m_surfacesById;
0389 }
0390
0391 void TrackingGeometry::visualize(IVisualization3D& helper,
0392 const GeometryContext& gctx,
0393 const ViewConfig& viewConfig,
0394 const ViewConfig& portalViewConfig,
0395 const ViewConfig& sensitiveViewConfig) const {
0396 highestTrackingVolume()->visualize(helper, gctx, viewConfig, portalViewConfig,
0397 sensitiveViewConfig);
0398 }
0399
0400 void TrackingGeometry::apply(TrackingGeometryVisitor& visitor) const {
0401 highestTrackingVolume()->apply(visitor);
0402 }
0403
0404 void TrackingGeometry::apply(TrackingGeometryMutableVisitor& visitor) {
0405 highestTrackingVolume()->apply(visitor);
0406 }
0407
0408 TrackingGeometry::GeometryVersion TrackingGeometry::geometryVersion() const {
0409 if (highestTrackingVolume()->portals().empty()) {
0410 return GeometryVersion::Gen1;
0411 } else {
0412 return GeometryVersion::Gen3;
0413 }
0414 }
0415
0416 }