Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-27 08:31:04

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/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     // Increment the volume ID for this volume
0044     m_volumeID = GeometryIdentifier().withVolume(m_volumeID.volume() + 1);
0045     // Reset boundary id for this volume
0046     m_iboundary = 0;
0047     // Reset layer id for this volume
0048     m_ilayer = 0;
0049 
0050     // assign the Volume ID to the volume itself
0051     ACTS_VERBOSE("~> volumeID: " << m_volumeID);
0052     volume.assignGeometryId(m_volumeID);
0053 
0054     // assign the material if you have a decorator
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     // get the intersection solution
0074     auto& bSurface = boundary.surfaceRepresentation();
0075     // create the boundary surface id
0076     m_iboundary += 1;
0077     auto boundaryID = GeometryIdentifier(m_volumeID).withBoundary(m_iboundary);
0078     ACTS_VERBOSE("~> boundaryID: " << boundaryID);
0079     // now assign to the boundary surface
0080     auto& mutableBSurface = *(const_cast<RegularSurface*>(&bSurface));
0081 
0082     // assign the boundary ID to the surface
0083     ACTS_VERBOSE("~> assigning boundaryID: " << boundaryID);
0084     mutableBSurface.assignGeometryId(boundaryID);
0085 
0086     // Assign material if you have a decorator
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     // create the layer identification
0097     m_ilayer += 1;
0098     auto layerID = GeometryIdentifier(m_volumeID).withLayer(m_ilayer);
0099     ACTS_VERBOSE("~> layerID: " << layerID);
0100 
0101     // now close the geometry
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     // Layers ARE also GeometryObjects and have IDs.
0176     // Let's check that the layer has the same ID as it's surface
0177     // representation. Uniqueness of the surface IDs is checked in the surface
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     // A glued boundary surface is shared between the adjacent volumes, so it
0193     // is visited once per volume it bounds
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     // A fused portal is shared between the adjacent volumes, so it is visited
0203     // once per volume it bounds
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       // A fused/merged portal is shared between volumes, so it is visited once
0209       // per owning volume slot. Re-inserting the same tag for the *same* portal
0210       // is fine; a different portal claiming the same tag is a collision.
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 }  // namespace
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     // Without a boundary hint the lookup is position based
0280     return volume;
0281   }
0282 
0283   // If the surface acts as a boundary, the volume on the far side along the
0284   // direction is entered, which can be `nullptr` if there is no volume in
0285   // that direction (end of world). Boundary surfaces and portals are shared
0286   // between the adjacent volumes, so the association does not depend on which
0287   // side of the boundary the position based lookup returned.
0288   //
0289   // The position is on the plane of the surface by precondition. A position
0290   // inside the volume is within the bounds of a boundary it lies on up to the
0291   // lookup tolerance, since portals and boundary surfaces cover the volume
0292   // faces. The exact bounds check can still fail for positions grazing a
0293   // volume edge within the tolerance, which is reported as a failure.
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   // The surface does not act as a boundary, the lookup is position based
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 }  // namespace Acts