diff --git a/Core/include/Acts/Detector/CylindricalContainerBuilder.hpp b/Core/include/Acts/Detector/CylindricalContainerBuilder.hpp index e4e1cf11d4c..8d213b589f6 100644 --- a/Core/include/Acts/Detector/CylindricalContainerBuilder.hpp +++ b/Core/include/Acts/Detector/CylindricalContainerBuilder.hpp @@ -22,6 +22,7 @@ namespace Acts { namespace Experimental { class IRootVolumeFinderBuilder; +class IGeometryIdGenerator; /// @brief A dedicated container builder for cylindrical detector containers /// @@ -45,6 +46,10 @@ class CylindricalContainerBuilder : public IDetectorComponentBuilder { std::vector binning = {}; /// The root volume finder std::shared_ptr rootVolumeFinderBuilder = nullptr; + /// The geometry id generator + std::shared_ptr geoIdGenerator = nullptr; + /// An eventual reverse geometry id generation + bool geoIdReverseGen = false; /// Auxiliary information, mainly for screen output std::string auxiliary = ""; }; diff --git a/Core/include/Acts/Detector/Detector.hpp b/Core/include/Acts/Detector/Detector.hpp index 7c9aaf99e02..adbccfce213 100644 --- a/Core/include/Acts/Detector/Detector.hpp +++ b/Core/include/Acts/Detector/Detector.hpp @@ -12,6 +12,8 @@ #include "Acts/Definitions/Common.hpp" #include "Acts/Detector/DetectorVolume.hpp" #include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Geometry/GeometryHierarchyMap.hpp" +#include "Acts/Geometry/GeometryIdentifier.hpp" #include "Acts/Navigation/NavigationDelegates.hpp" #include "Acts/Utilities/Delegate.hpp" @@ -23,6 +25,9 @@ #include namespace Acts { + +class Surface; + namespace Experimental { struct NavigationState; @@ -90,6 +95,11 @@ class Detector : public std::enable_shared_from_this { /// @return a vector to const DetectorVolume raw pointers const std::vector& volumes() const; + /// Const access to the hierarchy map of all sensitive surfaces + /// + /// @return the map which can be queried with GeometryID for ranges + const GeometryHierarchyMap& sensitiveHierarchyMap() const; + /// Update the current volume of a given navigation state /// /// @param gctx is the Geometry context of the call @@ -142,6 +152,9 @@ class Detector : public std::enable_shared_from_this { /// Name/index map to find volumes by name and detect duplicates std::unordered_map m_volumeNameIndex; + + /// Geometry Id hierarchy map of all sensitive surfaces + GeometryHierarchyMap m_sensitiveHierarchyMap; }; } // namespace Experimental diff --git a/Core/include/Acts/Detector/DetectorBuilder.hpp b/Core/include/Acts/Detector/DetectorBuilder.hpp index 6c3bc2c503c..2638f20a28f 100644 --- a/Core/include/Acts/Detector/DetectorBuilder.hpp +++ b/Core/include/Acts/Detector/DetectorBuilder.hpp @@ -18,6 +18,8 @@ namespace Acts { namespace Experimental { +class IGeometryIdGenerator; + /// @brief Standard generic Detector builder that calls /// the top level component builder and transfers the /// result into a detector object @@ -33,6 +35,8 @@ class DetectorBuilder final : public IDetectorBuilder { std::string name = "unnamed"; /// An external builder std::shared_ptr builder = nullptr; + /// A geometry id generator + std::shared_ptr geoIdGenerator = nullptr; /// Auxiliary information std::string auxiliary = ""; }; diff --git a/Core/include/Acts/Detector/DetectorVolume.hpp b/Core/include/Acts/Detector/DetectorVolume.hpp index e10e456c491..1f831960520 100644 --- a/Core/include/Acts/Detector/DetectorVolume.hpp +++ b/Core/include/Acts/Detector/DetectorVolume.hpp @@ -330,6 +330,12 @@ class DetectorVolume : public std::enable_shared_from_this { /// @return the geometry identifier const GeometryIdentifier& geometryId() const; + /// Set the geometry identifier + /// @note no checking is done, it will overwrite any existing id + /// + /// @param geoID is the geometry Id that is set to the object + void assignGeometryId(const GeometryIdentifier& geoID); + /// Assign Detector to this volume (for back navigation issues) /// @param detector the parenting detector class void assignDetector(const Detector& detector); diff --git a/Core/include/Acts/Detector/DetectorVolumeBuilder.hpp b/Core/include/Acts/Detector/DetectorVolumeBuilder.hpp index 0cf3f0bc252..35fba51948e 100644 --- a/Core/include/Acts/Detector/DetectorVolumeBuilder.hpp +++ b/Core/include/Acts/Detector/DetectorVolumeBuilder.hpp @@ -22,6 +22,7 @@ namespace Acts { namespace Experimental { class IExternalStructureBuilder; class IInternalStructureBuilder; +class IGeometryIdGenerator; /// @brief A generic detector volume builder that uses /// an external builder for shape and portals and an internal @@ -41,6 +42,8 @@ class DetectorVolumeBuilder : public IDetectorComponentBuilder { std::shared_ptr externalsBuilder = nullptr; /// An internal builder std::shared_ptr internalsBuilder = nullptr; + /// The geometry id generator + std::shared_ptr geoIdGenerator = nullptr; /// Add eventual internal volume to root bool addInternalsToRoot = false; /// Auxiliary information diff --git a/Core/include/Acts/Detector/GeometryIdGenerator.hpp b/Core/include/Acts/Detector/GeometryIdGenerator.hpp new file mode 100644 index 00000000000..23bf1d21220 --- /dev/null +++ b/Core/include/Acts/Detector/GeometryIdGenerator.hpp @@ -0,0 +1,125 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Detector/interface/IGeometryIdGenerator.hpp" +#include "Acts/Geometry/GeometryIdentifier.hpp" +#include "Acts/Utilities/Logger.hpp" + +#include + +namespace Acts { + +class Surface; + +namespace Experimental { + +class Portal; +class DetectorVolume; + +/// @brief This is the default implementation of the geometry id generator +/// +/// It is a simple counter based generator that assigns ids to the geometry +/// and increments the counter for each object type. +/// +/// Sub counters, i.e. for the sensitive surfaces, are reset after the volume +/// call, such that a new volume or layer volume would start from counter 0 +/// again. +/// +/// If the generator is configured to override existing ids, it will do so +/// and not respect previously assigned ids. +/// +/// If the generator is configured in container mode, it will increase the +/// layer id for each layer volume with confined surfaces. +/// +class GeometryIdGenerator final : public IGeometryIdGenerator { + public: + /// @brief Nested config struct + struct Config { + /// Container mode + bool containerMode = false; + /// Container id (if container mode), will not be incremented + unsigned int containerId = 0u; + /// Resetting mode + bool resetSubCounters = true; + /// Force override existing ids + bool overrideExistingIds = false; + }; + + /// @brief Nested cache struct + struct Cache { + /// Cache count of the volume, for non-container mode + unsigned int volumeCount = 0u; + /// Cache count of the layer volume, for container mode + unsigned int layerCount = 0u; + /// Cache count of the portal surfaces + unsigned int portalCount = 0u; + /// Cache count of passive surfaces + unsigned int passiveCount = 0u; + /// Cache count of sensitive surfaces + unsigned int sensitiveCount = 0u; + }; + + /// @brief Constructor with config + /// + /// @param cfg is the geometry configuration object + /// @param mlogger is the logging instance + GeometryIdGenerator(const Config& cfg, + std::unique_ptr mlogger = getDefaultLogger( + "GeometryIdGenerator", Logging::INFO)) + : m_cfg(cfg), m_logger(std::move(mlogger)) {} + + ~GeometryIdGenerator() = default; + + /// @brief Interface method to generata a geometry id cache + /// @return a geometry id cache decorated in a std::any object + IGeometryIdGenerator::GeoIdCache generateCache() const final; + + /// @brief Method for assigning a geometry id to a detector volume + /// + /// @param cache is the cache object for e.g. object counting + /// @param dVolume the detector volume to assign the geometry id to + void assignGeometryId(IGeometryIdGenerator::GeoIdCache& cache, + DetectorVolume& dVolume) const final; + + /// @brief Method for assigning a geometry id to a portal + /// + /// @param cache is the cache object for e.g. object counting + /// @param portal the portal to assign the geometry id to + void assignGeometryId(IGeometryIdGenerator::GeoIdCache& cache, + Portal& portal) const final; + + /// @brief Method for assigning a geometry id to a surface + /// + /// @param cache is the cache object for e.g. object counting + /// @param surface the surface to assign the geometry id to + void assignGeometryId(IGeometryIdGenerator::GeoIdCache& cache, + Surface& surface) const final; + + private: + /// @brief Helper method to get the volume id from the cache + /// + /// @param cache the provided cache + /// @param incrementLayer if true, the layer counter is incremented + /// + /// @return a valid geometry identifier + GeometryIdentifier volumeId(Cache& cache, bool incrementLayer = true) const; + + /// Configuration object + Config m_cfg; + + /// Private access method to the logger + const Logger& logger() const { return *m_logger; } + + /// logging instance + std::unique_ptr m_logger; +}; + +} // namespace Experimental +} // namespace Acts diff --git a/Core/include/Acts/Detector/interface/IGeometryIdGenerator.hpp b/Core/include/Acts/Detector/interface/IGeometryIdGenerator.hpp new file mode 100644 index 00000000000..3f6c0bd4552 --- /dev/null +++ b/Core/include/Acts/Detector/interface/IGeometryIdGenerator.hpp @@ -0,0 +1,59 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include + +namespace Acts { + +class Surface; + +namespace Experimental { + +class Portal; +class DetectorVolume; + +/// @brief This is the interface for generating geometry ids and assign them +/// to detector volumes, portals and surfaces +/// +class IGeometryIdGenerator { + public: + using GeoIdCache = std::any; + + virtual ~IGeometryIdGenerator() = default; + + /// @brief Virtual interface method to generata a geometry id cache + /// @return a geometry id cache decorated in a std::any object + virtual GeoIdCache generateCache() const = 0; + + /// The virtual interface definition for assigning a geometry id to + /// a detector volume + /// + /// @param cache is the cache object for e.g. object counting + /// @param dVolume the detector volume to assign the geometry id to + virtual void assignGeometryId(GeoIdCache& cache, + DetectorVolume& dVolume) const = 0; + + /// The virtual interface definition for assigning a geometry id to + /// a portal + /// + /// @param cache is the cache object for e.g. object counting + /// @param portal the portal to assign the geometry id to + virtual void assignGeometryId(GeoIdCache& cache, Portal& portal) const = 0; + + /// @brief The virtual interface definition for assigning a geometry id to + /// a surface + /// + /// @param cache is the cache object for e.g. object counting + /// @param surface the surface to assign the geometry id to + virtual void assignGeometryId(GeoIdCache& cache, Surface& surface) const = 0; +}; + +} // namespace Experimental +} // namespace Acts diff --git a/Core/include/Acts/Geometry/GeometryIdentifier.hpp b/Core/include/Acts/Geometry/GeometryIdentifier.hpp index 29d20da89ac..685cf48a19f 100644 --- a/Core/include/Acts/Geometry/GeometryIdentifier.hpp +++ b/Core/include/Acts/Geometry/GeometryIdentifier.hpp @@ -53,6 +53,8 @@ class GeometryIdentifier { constexpr Value layer() const { return getBits(kLayerMask); } /// Return the approach identifier. constexpr Value approach() const { return getBits(kApproachMask); } + /// Return the approach identifier. + constexpr Value passive() const { return getBits(kApproachMask); } /// Return the sensitive identifier. constexpr Value sensitive() const { return getBits(kSensitiveMask); } /// Return the extra identifier @@ -76,6 +78,10 @@ class GeometryIdentifier { constexpr GeometryIdentifier& setApproach(Value approach) { return setBits(kApproachMask, approach); } + /// Set the approach identifier - shared with Passive + constexpr GeometryIdentifier& setPassive(Value approach) { + return setBits(kApproachMask, approach); + } /// Set the sensitive identifier. constexpr GeometryIdentifier& setSensitive(Value sensitive) { return setBits(kSensitiveMask, sensitive); @@ -95,6 +101,7 @@ class GeometryIdentifier { static constexpr Value kLayerMask = 0x0000fff000000000; /// (2^8)-1 = 255 approach surfaces static constexpr Value kApproachMask = 0x0000000ff0000000; + static constexpr Value kPassiveMask = kApproachMask; /// (2^20)-1 = 1048575 sensitive surfaces static constexpr Value kSensitiveMask = 0x000000000fffff00; /// (2^8)-1 = 255 extra values diff --git a/Core/src/Detector/CMakeLists.txt b/Core/src/Detector/CMakeLists.txt index ab78567ea3a..00ee9e9a813 100644 --- a/Core/src/Detector/CMakeLists.txt +++ b/Core/src/Detector/CMakeLists.txt @@ -15,6 +15,7 @@ target_sources( Portal.cpp PortalGenerators.cpp ProtoDetector.cpp + GeometryIdGenerator.cpp VolumeStructureBuilder.cpp MultiWireStructureBuilder.cpp diff --git a/Core/src/Detector/CylindricalContainerBuilder.cpp b/Core/src/Detector/CylindricalContainerBuilder.cpp index 56219257518..e4784790fd4 100644 --- a/Core/src/Detector/CylindricalContainerBuilder.cpp +++ b/Core/src/Detector/CylindricalContainerBuilder.cpp @@ -10,6 +10,7 @@ #include "Acts/Detector/DetectorComponents.hpp" #include "Acts/Detector/detail/CylindricalDetectorHelper.hpp" +#include "Acts/Detector/interface/IGeometryIdGenerator.hpp" #include "Acts/Detector/interface/IRootVolumeFinderBuilder.hpp" #include "Acts/Navigation/DetectorVolumeFinders.hpp" @@ -166,6 +167,21 @@ Acts::Experimental::CylindricalContainerBuilder::construct( m_cfg.rootVolumeFinderBuilder->construct(gctx, rootVolumes)}}; } + // Geometry Id generation + if (m_cfg.geoIdGenerator != nullptr) { + ACTS_DEBUG("Assigning geometry ids to the detector"); + auto cache = m_cfg.geoIdGenerator->generateCache(); + if (m_cfg.geoIdReverseGen) { + std::for_each(rootVolumes.rbegin(), rootVolumes.rend(), [&](auto& v) { + m_cfg.geoIdGenerator->assignGeometryId(cache, *v); + }); + } else { + std::for_each(rootVolumes.begin(), rootVolumes.end(), [&](auto& v) { + m_cfg.geoIdGenerator->assignGeometryId(cache, *v); + }); + } + } + // Return the container return Acts::Experimental::DetectorComponent{ {}, rContainer, RootDetectorVolumes{rootVolumes, tryRootVolumes()}}; diff --git a/Core/src/Detector/Detector.cpp b/Core/src/Detector/Detector.cpp index 5f2ba3df44b..8955d844a3e 100644 --- a/Core/src/Detector/Detector.cpp +++ b/Core/src/Detector/Detector.cpp @@ -9,11 +9,14 @@ #include "Acts/Detector/Detector.hpp" #include "Acts/Navigation/NavigationState.hpp" +#include "Acts/Surfaces/Surface.hpp" #include "Acts/Utilities/Delegate.hpp" #include "Acts/Utilities/Enumerate.hpp" #include +#include #include +#include #include Acts::Experimental::Detector::Detector( @@ -48,6 +51,8 @@ Acts::Experimental::Detector::Detector( m_volumes = DetectorVolume::ObjectStore>( collectVolumes()); + // Fill the surface map + std::unordered_map surfaceGeoIdMap; // Check for unique names and fill the volume name / index map for (auto [iv, v] : enumerate(m_volumes.internal)) { // Assign this detector @@ -61,7 +66,27 @@ Acts::Experimental::Detector::Detector( " detected."); } m_volumeNameIndex[vName] = iv; + + for (const auto* s : v->surfaces()) { + auto sgeoID = s->geometryId(); + if (surfaceGeoIdMap.find(sgeoID) != surfaceGeoIdMap.end()) { + std::stringstream ss; + ss << sgeoID; + throw std::invalid_argument( + "Detector: duplicate sensitive surface geometry id '" + ss.str() + + "' detected. Make sure a GeometryIdGenerator is used."); + } + surfaceGeoIdMap.emplace(sgeoID, s); + } } + // Let us transfer the surfaces into the hierarchy map + std::vector> surfaceGeoIdVec; + surfaceGeoIdVec.reserve(surfaceGeoIdMap.size()); + for (auto [geoID, surface] : surfaceGeoIdMap) { + surfaceGeoIdVec.emplace_back(geoID, surface); + } + m_sensitiveHierarchyMap = + GeometryHierarchyMap(std::move(surfaceGeoIdVec)); } std::shared_ptr @@ -141,3 +166,8 @@ Acts::Experimental::Detector::findDetectorVolume( } return nullptr; } + +const Acts::GeometryHierarchyMap& +Acts::Experimental::Detector::sensitiveHierarchyMap() const { + return m_sensitiveHierarchyMap; +} diff --git a/Core/src/Detector/DetectorBuilder.cpp b/Core/src/Detector/DetectorBuilder.cpp index aa918deb12b..3417d70f563 100644 --- a/Core/src/Detector/DetectorBuilder.cpp +++ b/Core/src/Detector/DetectorBuilder.cpp @@ -10,6 +10,7 @@ #include "Acts/Detector/Detector.hpp" #include "Acts/Detector/DetectorVolume.hpp" +#include "Acts/Detector/interface/IGeometryIdGenerator.hpp" #include "Acts/Navigation/DetectorVolumeFinders.hpp" #include @@ -35,6 +36,14 @@ Acts::Experimental::DetectorBuilder::construct( auto [volumes, portals, roots] = m_cfg.builder->construct(gctx); + if (m_cfg.geoIdGenerator != nullptr) { + ACTS_DEBUG("Assigning geometry ids to the detector"); + auto cache = m_cfg.geoIdGenerator->generateCache(); + std::for_each(roots.volumes.begin(), roots.volumes.end(), [&](auto& v) { + m_cfg.geoIdGenerator->assignGeometryId(cache, *v); + }); + } + return Detector::makeShared(m_cfg.name, std::move(roots.volumes), std::move(roots.volumeFinder)); } diff --git a/Core/src/Detector/DetectorVolume.cpp b/Core/src/Detector/DetectorVolume.cpp index e087ba9ac57..7a935d16e2e 100644 --- a/Core/src/Detector/DetectorVolume.cpp +++ b/Core/src/Detector/DetectorVolume.cpp @@ -164,6 +164,11 @@ const Acts::GeometryIdentifier& Acts::Experimental::DetectorVolume::geometryId() return m_geometryId; } +void Acts::Experimental::DetectorVolume::assignGeometryId( + const GeometryIdentifier& geoID) { + m_geometryId = geoID; +} + const std::string& Acts::Experimental::DetectorVolume::name() const { return m_name; } diff --git a/Core/src/Detector/DetectorVolumeBuilder.cpp b/Core/src/Detector/DetectorVolumeBuilder.cpp index d757896c4fa..fecc0326eda 100644 --- a/Core/src/Detector/DetectorVolumeBuilder.cpp +++ b/Core/src/Detector/DetectorVolumeBuilder.cpp @@ -10,6 +10,7 @@ #include "Acts/Detector/DetectorVolume.hpp" #include "Acts/Detector/interface/IExternalStructureBuilder.hpp" +#include "Acts/Detector/interface/IGeometryIdGenerator.hpp" #include "Acts/Detector/interface/IInternalStructureBuilder.hpp" #include "Acts/Geometry/VolumeBounds.hpp" #include "Acts/Navigation/DetectorVolumeFinders.hpp" @@ -81,6 +82,14 @@ Acts::Experimental::DetectorVolumeBuilder::construct( for (auto [ip, p] : enumerate(dVolume->portalPtrs())) { portalContainer[ip] = p; } + + // Assign the geometry ids if configured to do so + if (m_cfg.geoIdGenerator != nullptr) { + ACTS_DEBUG("Assigning geometry ids to the detector volume"); + auto cache = m_cfg.geoIdGenerator->generateCache(); + m_cfg.geoIdGenerator->assignGeometryId(cache, *dVolume); + } + // Add to the root volume collection if configured rootVolumes.push_back(dVolume); // The newly built volume is the single produced volume diff --git a/Core/src/Detector/GeometryIdGenerator.cpp b/Core/src/Detector/GeometryIdGenerator.cpp new file mode 100644 index 00000000000..106a98a2b56 --- /dev/null +++ b/Core/src/Detector/GeometryIdGenerator.cpp @@ -0,0 +1,107 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "Acts/Detector/GeometryIdGenerator.hpp" + +#include "Acts/Detector/DetectorVolume.hpp" +#include "Acts/Detector/Portal.hpp" +#include "Acts/Surfaces/Surface.hpp" + +Acts::Experimental::IGeometryIdGenerator::GeoIdCache +Acts::Experimental::GeometryIdGenerator::generateCache() const { + return Cache{}; +} + +void Acts::Experimental::GeometryIdGenerator::assignGeometryId( + IGeometryIdGenerator::GeoIdCache& cache, DetectorVolume& dVolume) const { + auto& ccache = std::any_cast(cache); + + ACTS_VERBOSE("Processing volume " << dVolume.name()); + // Set to the volume itself + if (dVolume.geometryId().volume() == 0 or m_cfg.overrideExistingIds) { + ++ccache.volumeCount; + GeometryIdentifier geoID = volumeId(ccache); + ACTS_VERBOSE("Assigning volume id " << geoID.volume()); + dVolume.assignGeometryId(geoID); + } + + // Portals + std::for_each(dVolume.portalPtrs().begin(), dVolume.portalPtrs().end(), + [&](auto& portal) { assignGeometryId(cache, *portal); }); + + // Surfaces + std::for_each(dVolume.surfacePtrs().begin(), dVolume.surfacePtrs().end(), + [&](auto& surface) { assignGeometryId(cache, *surface); }); + + if (m_cfg.resetSubCounters) { + ccache.portalCount = 0u; + ccache.sensitiveCount = 0u; + ccache.passiveCount = 0u; + } + + // Sub volumes + std::for_each(dVolume.volumePtrs().begin(), dVolume.volumePtrs().end(), + [&](auto& volume) { assignGeometryId(cache, *volume); }); +} + +void Acts::Experimental::GeometryIdGenerator::assignGeometryId( + IGeometryIdGenerator::GeoIdCache& cache, Portal& portal) const { + auto& ccache = std::any_cast(cache); + + auto& pSurface = portal.surface(); + if (pSurface.geometryId().boundary() == 0 or m_cfg.overrideExistingIds) { + GeometryIdentifier geoID = volumeId(ccache, false); + geoID.setBoundary(++ccache.portalCount); + ACTS_VERBOSE("Assigning portal id " << ccache.portalCount); + pSurface.assignGeometryId(geoID); + } +} + +void Acts::Experimental::GeometryIdGenerator::assignGeometryId( + IGeometryIdGenerator::GeoIdCache& cache, Surface& surface) const { + auto& ccache = std::any_cast(cache); + + auto rGeoID = surface.geometryId(); + auto geoID = volumeId(ccache, false); + if (not m_cfg.overrideExistingIds and rGeoID.value() != 0) { + return; + } else if ((rGeoID.sensitive() == 0 and rGeoID.passive() == 0) or + m_cfg.overrideExistingIds) { + if (surface.associatedDetectorElement() != nullptr) { + geoID.setSensitive(++ccache.sensitiveCount); + ACTS_VERBOSE("Assigning sensitive id " << ccache.sensitiveCount); + } else { + ACTS_VERBOSE("Assigning passive id " << ccache.passiveCount); + geoID.setPassive(++ccache.passiveCount); + } + surface.assignGeometryId(geoID); + } else if (rGeoID.sensitive() != 0 or rGeoID.passive() != 0) { + ACTS_VERBOSE( + "Surface already has a geometry id, only setting volume and layer id."); + rGeoID.setVolume(geoID.volume()); + rGeoID.setLayer(geoID.layer()); + surface.assignGeometryId(rGeoID); + } +} + +Acts::GeometryIdentifier Acts::Experimental::GeometryIdGenerator::volumeId( + Cache& cache, bool incrementLayer) const { + GeometryIdentifier geoID(0u); + if (not m_cfg.containerMode) { + geoID.setVolume(cache.volumeCount); + } else { + geoID.setVolume(m_cfg.containerId); + if (incrementLayer) { + ++cache.layerCount; + } + geoID.setLayer(cache.layerCount); + ACTS_VERBOSE("Container mode: assigning volume id " + << m_cfg.containerId << ", layer id " << cache.layerCount); + } + return geoID; +} diff --git a/Core/src/Surfaces/CylinderSurface.cpp b/Core/src/Surfaces/CylinderSurface.cpp index 42679e18c4b..bc19c446c91 100644 --- a/Core/src/Surfaces/CylinderSurface.cpp +++ b/Core/src/Surfaces/CylinderSurface.cpp @@ -47,7 +47,7 @@ Acts::CylinderSurface::CylinderSurface(const Transform3& transform, Acts::CylinderSurface::CylinderSurface( std::shared_ptr cbounds, - const Acts::DetectorElementBase& detelement) + const DetectorElementBase& detelement) : Surface(detelement), m_bounds(std::move(cbounds)) { /// surfaces representing a detector element must have bounds throw_assert(m_bounds, "CylinderBounds must not be nullptr"); diff --git a/Examples/Python/python/acts/examples/detector.py b/Examples/Python/python/acts/examples/detector.py index b651fa17d69..03fd983fe32 100644 --- a/Examples/Python/python/acts/examples/detector.py +++ b/Examples/Python/python/acts/examples/detector.py @@ -112,6 +112,8 @@ def __init__( layers=None, binning=[], rootbuilder=None, + geoidgenerator=None, + reversegeoids=False, loglevel=logging.INFO, ): """Create a cylindrical container builder from volumes or layer definitions @@ -121,6 +123,9 @@ def __init__( :param volumes: list of volumes :param layers: list of layers [ [extent, provider, binnings, supports], ... ] :param binning: binning of surfces in this container + :param rootbuilder: root volume finder builder + :param geoidgenerator: geoid generator for setting geo ids + :param reversegeoids: reverse the geo id order :param loglevel: logging level """ @@ -130,6 +135,8 @@ def __init__( self._volumes = volumes self._binning = binning self._rootbuilder = rootbuilder + self._geoidgenerator = geoidgenerator + self._reversegeoids = reversegeoids self._loglevel = loglevel def builder(self): @@ -189,5 +196,7 @@ def builder(self): containerConfig.builders = builders containerConfig.binning = [self._binning] containerConfig.rootVolumeFinderBuilder = self._rootbuilder + containerConfig.geoIdGenerator = self._geoidgenerator + containerConfig.geoIdReverseGen = self._reversegeoids containerConfig.auxiliary = "Container[" + self._name + "]" return CylindricalContainerBuilder(containerConfig, self._name, self._loglevel) diff --git a/Examples/Python/python/acts/examples/odd_light.py b/Examples/Python/python/acts/examples/odd_light.py index 1121f203aa1..8ce2b62fdb3 100644 --- a/Examples/Python/python/acts/examples/odd_light.py +++ b/Examples/Python/python/acts/examples/odd_light.py @@ -8,12 +8,12 @@ from acts import ( Binning, DetectorBuilder, + GeometryIdGenerator, Extent, GeometryContext, KdtSurfaces2D, KdtSurfacesProvider2D, IndexedRootVolumeFinderBuilder, - logging, ProtoBinning, ) @@ -29,7 +29,8 @@ def necBarrelPec( barrelPositionsR, barrelHalfThickness, barrelBinnings, - llevel=logging.INFO, + containerIds=[], + llevel=acts.logging.INFO, ): # Negative Endcap @@ -51,12 +52,22 @@ def necBarrelPec( ) ] + necGig = None + if len(containerIds) > 0: + necGigConf = GeometryIdGenerator.Config() + necGigConf.containerMode = True + necGigConf.containerId = containerIds[0] + necGig = GeometryIdGenerator(necGigConf, name + "_nec_gig", llevel) + nec = detector.CylindricalDetectorContainer( name=name + "_nec", extent=necEndcapExtent, volumes=None, layers=necLayers, binning=Binning.z, + rootbuilder=None, + geoidgenerator=necGig, + reversegeoids=True, loglevel=llevel, ) @@ -84,13 +95,23 @@ def necBarrelPec( ) ] + barrelGig = None + if len(containerIds) > 1: + barrelGiConfg = GeometryIdGenerator.Config() + barrelGiConfg.containerMode = True + barrelGiConfg.containerId = containerIds[1] + barrelGig = GeometryIdGenerator(barrelGiConfg, name + "_barrel_gig", llevel) + barrel = detector.CylindricalDetectorContainer( name=name + "_barrel", extent=barrelExtent, volumes=None, layers=barrelLayers, binning=Binning.r, - loglevel=logging.VERBOSE, + rootbuilder=None, + geoidgenerator=barrelGig, + reversegeoids=False, + loglevel=llevel, ) # Positive Endcap @@ -116,12 +137,22 @@ def necBarrelPec( ) ] + pecGig = None + if len(containerIds) > 2: + pecGigConf = GeometryIdGenerator.Config() + pecGigConf.containerMode = True + pecGigConf.containerId = containerIds[2] + pecGig = GeometryIdGenerator(pecGigConf, name + "_pec_gig", llevel) + pec = detector.CylindricalDetectorContainer( name=name + "_pec", extent=pecEndcapExtent, volumes=None, layers=pecLayers, binning=Binning.z, + rootbuilder=None, + geoidgenerator=pecGig, + reversegeoids=False, loglevel=llevel, ) @@ -133,11 +164,12 @@ def necBarrelPec( layers=None, binning=Binning.z, rootbuilder=None, + geoidgenerator=None, loglevel=llevel, ) -def get_detector(geoContext, ssurfaces, psurfaces, pMatches, llevel=logging.VERBOSE): +def get_detector(geoContext, ssurfaces, psurfaces, llevel=acts.logging.DEBUG): # Build the geometry context & a kdtree for the surfaces sensitivesKdt = KdtSurfaces2D(geoContext, ssurfaces, [Binning.z, Binning.r]) @@ -185,6 +217,7 @@ def get_detector(geoContext, ssurfaces, psurfaces, pMatches, llevel=logging.VERB pixBarrelPositionsR, pixBarrelHalfThickness, pixBarrelBinning, + [16, 17, 18], llevel, ) @@ -227,6 +260,7 @@ def get_detector(geoContext, ssurfaces, psurfaces, pMatches, llevel=logging.VERB sstripBarrelPositionsR, sstripBarrelHalfThickness, sstripBarrelBinning, + [23, 24, 25], llevel, ) @@ -261,10 +295,11 @@ def get_detector(geoContext, ssurfaces, psurfaces, pMatches, llevel=logging.VERB lstripBarrelPositionsR, lstripBarrelHalfThickness, lstripBarrelBinning, + [28, 29, 30], llevel, ) - # Container of beam pipe and pixel + # Root builder from the detector container rootBuilder = IndexedRootVolumeFinderBuilder([Binning.z, Binning.r]) det = detector.CylindricalDetectorContainer( @@ -274,13 +309,21 @@ def get_detector(geoContext, ssurfaces, psurfaces, pMatches, llevel=logging.VERB layers=None, binning=Binning.r, rootbuilder=rootBuilder, + geoidgenerator=None, loglevel=llevel, ) - # Pixel Endcap + # All objects that do not have a geometry id will be assigned one + gigConfig = GeometryIdGenerator.Config() + gigConfig.overrideExistingIds = False + gigConfig.resetSubCounters = True + gig = GeometryIdGenerator(gigConfig, "GeometryIdGenerator", llevel) + + # Full detector configuration detConfig = DetectorBuilder.Config() detConfig.name = "ODD" detConfig.builder = det.builder() + detConfig.geoIdGenerator = gig detConfig.auxiliary = "Detector[" + detConfig.name + "]" detBuilder = DetectorBuilder(detConfig, detConfig.auxiliary, llevel) @@ -318,7 +361,7 @@ def main(): [elements, ssurfaces, psurfaces] = acts_g4.convertSurfaces( args.input, [args.sensitives], [args.passives] ) - odd_light = get_detector(geoContext, ssurfaces, psurfaces, logging.DEBUG) + odd_light = get_detector(geoContext, ssurfaces, psurfaces, acts.logging.DEBUG) if "__main__" == __name__: diff --git a/Examples/Python/src/Geometry.cpp b/Examples/Python/src/Geometry.cpp index ebbd70267c5..f5d37c93996 100644 --- a/Examples/Python/src/Geometry.cpp +++ b/Examples/Python/src/Geometry.cpp @@ -12,6 +12,7 @@ #include "Acts/Detector/DetectorBuilder.hpp" #include "Acts/Detector/DetectorVolume.hpp" #include "Acts/Detector/DetectorVolumeBuilder.hpp" +#include "Acts/Detector/GeometryIdGenerator.hpp" #include "Acts/Detector/IndexedRootVolumeFinderBuilder.hpp" #include "Acts/Detector/KdtSurfacesProvider.hpp" #include "Acts/Detector/LayerStructureBuilder.hpp" @@ -19,6 +20,7 @@ #include "Acts/Detector/interface/IDetectorBuilder.hpp" #include "Acts/Detector/interface/IDetectorComponentBuilder.hpp" #include "Acts/Detector/interface/IExternalStructureBuilder.hpp" +#include "Acts/Detector/interface/IGeometryIdGenerator.hpp" #include "Acts/Detector/interface/IInternalStructureBuilder.hpp" #include "Acts/Detector/interface/IRootVolumeFinderBuilder.hpp" #include "Acts/Geometry/CylinderVolumeBounds.hpp" @@ -329,6 +331,38 @@ void addExperimentalGeometry(Context& ctx) { ACTS_PYTHON_STRUCT_END(); } + { + py::class_>( + m, "IGeometryIdGenerator"); + + auto geoIdGen = + py::class_>( + m, "GeometryIdGenerator") + .def(py::init([](Acts::Experimental::GeometryIdGenerator::Config& + config, + const std::string& name, + Acts::Logging::Level level) { + return std::make_shared( + config, getDefaultLogger(name, level)); + })); + + auto geoIdGenConfig = + py::class_(geoIdGen, + "Config") + .def(py::init<>()); + + ACTS_PYTHON_STRUCT_BEGIN(geoIdGenConfig, + Acts::Experimental::GeometryIdGenerator::Config); + ACTS_PYTHON_MEMBER(containerMode); + ACTS_PYTHON_MEMBER(containerId); + ACTS_PYTHON_MEMBER(resetSubCounters); + ACTS_PYTHON_MEMBER(overrideExistingIds); + ACTS_PYTHON_STRUCT_END(); + } + { // Put them together to a detector volume py::class_ cBounds, double thickness, + std::shared_ptr material = nullptr) + : DetectorElementBase(), + m_elementTransform(transform), + m_elementThickness(thickness) { + m_elementSurface = Surface::makeShared(cBounds, *this); + m_elementSurface->assignSurfaceMaterial(std::move(material)); + } + /// Constructor for single sided detector element /// - bound to a Plane Surface /// - /// @param transform is the transform that element the layer in 3D frame + /// @param transform places the element in global frame /// @param pBounds is the planar bounds for the planar detector element /// @param thickness is the module thickness /// @param material is the (optional) Surface material associated to it DetectorElementStub( - const Transform3& transform, - const std::shared_ptr& pBounds, double thickness, + const Transform3& transform, std::shared_ptr pBounds, + double thickness, std::shared_ptr material = nullptr) : DetectorElementBase(), m_elementTransform(transform), @@ -59,13 +75,13 @@ class DetectorElementStub : public DetectorElementBase { /// Constructor for single sided detector element /// - bound to a Line Surface /// - /// @param transform is the transform that element the layer in 3D frame + /// @param transform places the element in global frame /// @param dBounds is the line bounds for the line like detector element /// @param thickness is the module thickness /// @param material is the (optional) Surface material associated to it DetectorElementStub( - const Transform3& transform, - const std::shared_ptr& lBounds, double thickness, + const Transform3& transform, std::shared_ptr lBounds, + double thickness, std::shared_ptr material = nullptr) : DetectorElementBase(), m_elementTransform(transform), diff --git a/Tests/UnitTests/Core/Detector/CMakeLists.txt b/Tests/UnitTests/Core/Detector/CMakeLists.txt index 81969813d2d..62e534ef637 100644 --- a/Tests/UnitTests/Core/Detector/CMakeLists.txt +++ b/Tests/UnitTests/Core/Detector/CMakeLists.txt @@ -5,6 +5,7 @@ add_unittest(Detector DetectorTests.cpp) add_unittest(DetectorBuilder DetectorBuilderTests.cpp) add_unittest(DetectorVolume DetectorVolumeTests.cpp) add_unittest(DetectorVolumeBuilder DetectorVolumeBuilderTests.cpp) +add_unittest(GeometryIdGenerator GeometryIdGeneratorTests.cpp) add_unittest(IndexedRootVolumeFinderBuilder IndexedRootVolumeFinderBuilderTests.cpp) add_unittest(IndexedSurfaceGridFiller IndexedSurfaceGridFillerTests.cpp) add_unittest(IndexedSurfacesGenerator IndexedSurfacesGeneratorTests.cpp) diff --git a/Tests/UnitTests/Core/Detector/CylindricalContainerBuilderTests.cpp b/Tests/UnitTests/Core/Detector/CylindricalContainerBuilderTests.cpp index 21767fca40a..73adeb44a5d 100644 --- a/Tests/UnitTests/Core/Detector/CylindricalContainerBuilderTests.cpp +++ b/Tests/UnitTests/Core/Detector/CylindricalContainerBuilderTests.cpp @@ -14,8 +14,10 @@ #include "Acts/Detector/DetectorVolume.hpp" #include "Acts/Detector/PortalGenerators.hpp" #include "Acts/Detector/interface/IDetectorComponentBuilder.hpp" +#include "Acts/Detector/interface/IGeometryIdGenerator.hpp" #include "Acts/Geometry/CylinderVolumeBounds.hpp" #include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Geometry/GeometryIdentifier.hpp" #include "Acts/Navigation/DetectorVolumeFinders.hpp" #include "Acts/Navigation/SurfaceCandidatesUpdators.hpp" #include "Acts/Surfaces/CylinderBounds.hpp" @@ -27,6 +29,7 @@ #include "Acts/Utilities/Enumerate.hpp" #include "Acts/Utilities/Logger.hpp" +#include #include #include #include @@ -91,6 +94,34 @@ class CylindricalVolumeBuilder : public IDetectorComponentBuilder { std::string m_name; }; +class VolumeGeoIdGenerator : public IGeometryIdGenerator { + public: + struct Cache { + unsigned int volumeCount = 0; + }; + + IGeometryIdGenerator::GeoIdCache generateCache() const final { + return Cache{0}; + } + + void assignGeometryId(IGeometryIdGenerator::GeoIdCache& cache, + DetectorVolume& dVolume) const final { + auto& ccache = std::any_cast(cache); + ccache.volumeCount += 1; + Acts::GeometryIdentifier geoID; + geoID.setVolume(ccache.volumeCount); + dVolume.assignGeometryId(geoID); + } + + void assignGeometryId( + Acts::Experimental::IGeometryIdGenerator::GeoIdCache& /*cache*/, + Acts::Experimental::Portal& /*portal*/) const final {} + + void assignGeometryId( + Acts::Experimental::IGeometryIdGenerator::GeoIdCache& /*cache*/, + Acts::Surface& /*surface*/) const final {} +}; + BOOST_AUTO_TEST_SUITE(Detector) BOOST_AUTO_TEST_CASE(CylindricaContainerBuilder_Misconfiguration) { @@ -144,6 +175,9 @@ BOOST_AUTO_TEST_CASE(CylindricaContainerBuildingZ) { tripleZCfg.auxiliary = "*** Test 0 - Build triple in Z ***"; tripleZCfg.builders = {negDisc, barrel, posDisc}; tripleZCfg.binning = {binZ}; + tripleZCfg.geoIdGenerator = std::make_shared(); + // Let's test the reverse generation + tripleZCfg.geoIdReverseGen = true; auto tripleZ = std::make_shared( tripleZCfg, getDefaultLogger("TripleBuilderZ", Logging::VERBOSE)); @@ -152,6 +186,9 @@ BOOST_AUTO_TEST_CASE(CylindricaContainerBuildingZ) { BOOST_CHECK(portals.size() == 4u); BOOST_CHECK(roots.volumes.size() == 3u); + BOOST_CHECK(roots.volumes[0]->geometryId().volume() == 3u); + BOOST_CHECK(roots.volumes[1]->geometryId().volume() == 2u); + BOOST_CHECK(roots.volumes[2]->geometryId().volume() == 1u); } BOOST_AUTO_TEST_CASE(CylindricaContainerBuildingR) { @@ -178,6 +215,7 @@ BOOST_AUTO_TEST_CASE(CylindricaContainerBuildingR) { barrelRCfg.auxiliary = "*** Test 1 - Build multilayer barrel ***"; barrelRCfg.builders = {barrel0, barrel1, barrel2}; barrelRCfg.binning = {binR}; + barrelRCfg.geoIdGenerator = std::make_shared(); auto barrelR = std::make_shared( barrelRCfg, getDefaultLogger("BarrelBuilderR", Logging::VERBOSE)); @@ -186,6 +224,9 @@ BOOST_AUTO_TEST_CASE(CylindricaContainerBuildingR) { BOOST_CHECK(portals.size() == 4u); BOOST_CHECK(roots.volumes.size() == 3u); + BOOST_CHECK(roots.volumes[0]->geometryId().volume() == 1u); + BOOST_CHECK(roots.volumes[1]->geometryId().volume() == 2u); + BOOST_CHECK(roots.volumes[2]->geometryId().volume() == 3u); } BOOST_AUTO_TEST_CASE(CylindricaContainerBuildingPhi) { diff --git a/Tests/UnitTests/Core/Detector/DetectorBuilderTests.cpp b/Tests/UnitTests/Core/Detector/DetectorBuilderTests.cpp index dfb42b94aaa..76dd1355e9e 100644 --- a/Tests/UnitTests/Core/Detector/DetectorBuilderTests.cpp +++ b/Tests/UnitTests/Core/Detector/DetectorBuilderTests.cpp @@ -15,24 +15,44 @@ #include "Acts/Detector/DetectorVolume.hpp" #include "Acts/Detector/PortalGenerators.hpp" #include "Acts/Detector/interface/IDetectorComponentBuilder.hpp" +#include "Acts/Detector/interface/IGeometryIdGenerator.hpp" #include "Acts/Geometry/CuboidVolumeBounds.hpp" #include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Geometry/GeometryIdentifier.hpp" #include "Acts/Navigation/DetectorVolumeFinders.hpp" #include "Acts/Navigation/SurfaceCandidatesUpdators.hpp" +#include "Acts/Surfaces/PlaneSurface.hpp" +#include "Acts/Surfaces/RectangleBounds.hpp" +#include "Acts/Surfaces/Surface.hpp" +#include "Acts/Tests/CommonHelpers/DetectorElementStub.hpp" #include "Acts/Utilities/Enumerate.hpp" +#include #include +#include class CompBuilder final : public Acts::Experimental::IDetectorComponentBuilder { public: + CompBuilder( + const std::vector>& sensitives = {}) + : m_sensitives(sensitives) {} + Acts::Experimental::DetectorComponent construct( const Acts::GeometryContext& gctx) const final { auto bounds = std::make_unique(10., 10., 10.); // Construct the DetectorVolume - auto dVolume = Acts::Experimental::DetectorVolumeFactory::construct( - Acts::Experimental::defaultPortalGenerator(), gctx, "TestVolume", - Acts::Transform3::Identity(), std::move(bounds), - Acts::Experimental::tryAllPortals()); + auto dVolume = + m_sensitives.empty() + ? Acts::Experimental::DetectorVolumeFactory::construct( + Acts::Experimental::defaultPortalGenerator(), gctx, + "TestVolume", Acts::Transform3::Identity(), std::move(bounds), + Acts::Experimental::tryAllPortals()) + : Acts::Experimental::DetectorVolumeFactory::construct( + Acts::Experimental::defaultPortalGenerator(), gctx, + "TestVolumeWithSurfaces", Acts::Transform3::Identity(), + std::move(bounds), m_sensitives, {}, + Acts::Experimental::tryNoVolumes(), + Acts::Experimental::tryAllPortalsAndSurfaces()); // Fill the portal container Acts::Experimental::DetectorComponent::PortalContainer portalContainer; @@ -45,8 +65,39 @@ class CompBuilder final : public Acts::Experimental::IDetectorComponentBuilder { portalContainer, {{dVolume}, Acts::Experimental::tryRootVolumes()}}; } + + private: + std::vector> m_sensitives; }; +class SurfaceGeoIdGenerator : public Acts::Experimental::IGeometryIdGenerator { + public: + Acts::Experimental::IGeometryIdGenerator::GeoIdCache generateCache() + const final { + return std::any(); + } + + void assignGeometryId( + Acts::Experimental::IGeometryIdGenerator::GeoIdCache& /*cache*/, + Acts::Experimental::DetectorVolume& dVolume) const final { + for (auto [is, s] : Acts::enumerate(dVolume.surfacePtrs())) { + Acts::GeometryIdentifier geoID; + geoID.setSensitive(is + 1); + s->assignGeometryId(geoID); + } + } + + void assignGeometryId( + Acts::Experimental::IGeometryIdGenerator::GeoIdCache& /*cache*/, + Acts::Experimental::Portal& /*portal*/) const final {} + + void assignGeometryId( + Acts::Experimental::IGeometryIdGenerator::GeoIdCache& /*cache*/, + Acts::Surface& /*surface*/) const final {} +}; + +Acts::GeometryContext tContext; + BOOST_AUTO_TEST_SUITE(Detector) BOOST_AUTO_TEST_CASE(DetectorBuilder_Misconfigured) { @@ -58,11 +109,26 @@ BOOST_AUTO_TEST_CASE(DetectorBuilder_Misconfigured) { BOOST_CHECK_THROW(auto a = Acts::Experimental::DetectorBuilder(dCfg), std::invalid_argument); + + // Detector builder with sensitives but no assigned geometry ids to them + Acts::Test::DetectorElementStub detElement0( + Acts::Transform3::Identity(), + std::make_shared(5., 5.), 0.1); + Acts::Test::DetectorElementStub detElement1( + Acts::Transform3::Identity(), + std::make_shared(5., 5.), 0.1); + + std::vector> sensitives; + sensitives.push_back(detElement0.surface().getSharedPtr()); + sensitives.push_back(detElement1.surface().getSharedPtr()); + dCfg.builder = std::make_shared(sensitives); + + BOOST_CHECK_THROW( + Acts::Experimental::DetectorBuilder(dCfg).construct(tContext), + std::invalid_argument); } BOOST_AUTO_TEST_CASE(DetectorBuilder_test) { - Acts::GeometryContext tContext; - // Detector builder Acts::Experimental::DetectorBuilder::Config dCfg; dCfg.auxiliary = "*** Test : Detector ***"; @@ -77,4 +143,33 @@ BOOST_AUTO_TEST_CASE(DetectorBuilder_test) { BOOST_CHECK(detector->rootVolumes().size() == 1); } +BOOST_AUTO_TEST_CASE(DetectorBuilder_testWithSurfaces) { + // Detector builder + Acts::Experimental::DetectorBuilder::Config dCfg; + dCfg.auxiliary = "*** Test : Detector ***"; + dCfg.name = "TestDetectorWithSurfaces"; + dCfg.builder = std::make_shared(); + + // Test detector with surfaces + Acts::Test::DetectorElementStub detElement0( + Acts::Transform3::Identity(), + std::make_shared(5., 5.), 0.1); + Acts::Test::DetectorElementStub detElement1( + Acts::Transform3::Identity(), + std::make_shared(5., 5.), 0.1); + + std::vector> sensitives; + sensitives.push_back(detElement0.surface().getSharedPtr()); + sensitives.push_back(detElement1.surface().getSharedPtr()); + dCfg.builder = std::make_shared(sensitives); + dCfg.geoIdGenerator = std::make_shared(); + + auto detector = Acts::Experimental::DetectorBuilder(dCfg).construct(tContext); + BOOST_CHECK_EQUAL(detector->name(), "TestDetectorWithSurfaces"); + BOOST_CHECK(detector->volumes()[0]->surfaces()[0]->geometryId().sensitive() == + 1); + BOOST_CHECK(detector->volumes()[0]->surfaces()[1]->geometryId().sensitive() == + 2); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Core/Detector/DetectorTests.cpp b/Tests/UnitTests/Core/Detector/DetectorTests.cpp index 55a9e382e5f..bb876475170 100644 --- a/Tests/UnitTests/Core/Detector/DetectorTests.cpp +++ b/Tests/UnitTests/Core/Detector/DetectorTests.cpp @@ -14,10 +14,14 @@ #include "Acts/Detector/PortalGenerators.hpp" #include "Acts/Geometry/CylinderVolumeBounds.hpp" #include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Geometry/GeometryHierarchyMap.hpp" +#include "Acts/Geometry/GeometryIdentifier.hpp" #include "Acts/Navigation/DetectorVolumeFinders.hpp" #include "Acts/Navigation/NavigationDelegates.hpp" #include "Acts/Navigation/NavigationState.hpp" #include "Acts/Navigation/SurfaceCandidatesUpdators.hpp" +#include "Acts/Surfaces/CylinderBounds.hpp" +#include "Acts/Tests/CommonHelpers/DetectorElementStub.hpp" #include #include @@ -137,4 +141,35 @@ BOOST_AUTO_TEST_CASE(DetectorConstruction) { std::invalid_argument); } +BOOST_AUTO_TEST_CASE(DetectorConstructionWithHierarchyMap) { + auto portalGenerator = Acts::Experimental::defaultPortalGenerator(); + + std::vector> detStore; + std::vector radii = {100, 102, 104, 106, 108, 110}; + auto cylinderVoumeBounds = + std::make_unique(80, 130, 200); + std::vector> surfaces = {}; + for (auto [ir, r] : Acts::enumerate(radii)) { + auto detElement = std::make_unique( + Acts::Transform3::Identity(), + std::make_shared(r, 190.), 0.1); + auto surface = detElement->surface().getSharedPtr(); + surface->assignGeometryId(Acts::GeometryIdentifier{}.setSensitive(ir + 1)); + surfaces.push_back(std::move(surface)); + detStore.push_back(std::move(detElement)); + } + + auto cylVolume = Acts::Experimental::DetectorVolumeFactory::construct( + portalGenerator, tContext, "CylinderVolume", Acts::Transform3::Identity(), + std::move(cylinderVoumeBounds), surfaces, {}, + Acts::Experimental::tryNoVolumes(), + Acts::Experimental::tryAllPortalsAndSurfaces()); + + auto det = Acts::Experimental::Detector::makeShared( + "DetWithSurfaces", {cylVolume}, Acts::Experimental::tryRootVolumes()); + + const auto& sensitiveHierarchyMap = det->sensitiveHierarchyMap(); + BOOST_CHECK(sensitiveHierarchyMap.size() == 6u); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Core/Detector/DetectorVolumeBuilderTests.cpp b/Tests/UnitTests/Core/Detector/DetectorVolumeBuilderTests.cpp index dbe6af7b4d4..9f59a0dd303 100644 --- a/Tests/UnitTests/Core/Detector/DetectorVolumeBuilderTests.cpp +++ b/Tests/UnitTests/Core/Detector/DetectorVolumeBuilderTests.cpp @@ -14,9 +14,11 @@ #include "Acts/Detector/DetectorVolumeBuilder.hpp" #include "Acts/Detector/PortalGenerators.hpp" #include "Acts/Detector/interface/IExternalStructureBuilder.hpp" +#include "Acts/Detector/interface/IGeometryIdGenerator.hpp" #include "Acts/Detector/interface/IInternalStructureBuilder.hpp" #include "Acts/Geometry/CylinderVolumeBounds.hpp" #include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Geometry/GeometryIdentifier.hpp" #include "Acts/Navigation/DetectorVolumeFinders.hpp" #include "Acts/Navigation/SurfaceCandidatesUpdators.hpp" #include "Acts/Surfaces/CylinderBounds.hpp" @@ -79,6 +81,32 @@ class InternalSurfaceBuilder : public IInternalStructureBuilder { bounds_type m_bounds; }; +class SurfaceGeoIdGenerator : public Acts::Experimental::IGeometryIdGenerator { + public: + Acts::Experimental::IGeometryIdGenerator::GeoIdCache generateCache() + const final { + return std::any(); + } + + void assignGeometryId( + Acts::Experimental::IGeometryIdGenerator::GeoIdCache& /*cache*/, + Acts::Experimental::DetectorVolume& dVolume) const final { + for (auto [is, s] : Acts::enumerate(dVolume.surfacePtrs())) { + Acts::GeometryIdentifier geoID; + geoID.setPassive(is + 1); + s->assignGeometryId(geoID); + } + } + + void assignGeometryId( + Acts::Experimental::IGeometryIdGenerator::GeoIdCache& /*cache*/, + Acts::Experimental::Portal& /*portal*/) const final {} + + void assignGeometryId( + Acts::Experimental::IGeometryIdGenerator::GeoIdCache& /*cache*/, + Acts::Surface& /*surface*/) const final {} +}; + /// @brief Mockup internal surface builder /// @tparam surface_type the surface type to be constructed /// @tparam bounds_type the bounds type that is constructed @@ -160,6 +188,7 @@ BOOST_AUTO_TEST_CASE(DetectorVolumeBuilder_VolumeWithSurface) { dvCfg.name = "CylinderWithSurface"; dvCfg.externalsBuilder = cBuilder; dvCfg.internalsBuilder = sBuilder; + dvCfg.geoIdGenerator = std::make_shared(); auto dvBuilder = std::make_shared( dvCfg, getDefaultLogger("DetectorVolumeBuilder", Logging::VERBOSE)); @@ -168,6 +197,9 @@ BOOST_AUTO_TEST_CASE(DetectorVolumeBuilder_VolumeWithSurface) { BOOST_CHECK(volumes.size() == 1u); BOOST_CHECK(volumes.front()->surfaces().size() == 1u); + BOOST_CHECK(volumes.front()->surfaces().front()->geometryId().passive() == + 1u); + BOOST_CHECK(volumes.front()->volumes().empty()); BOOST_CHECK(portals.size() == 4u); diff --git a/Tests/UnitTests/Core/Detector/GeometryIdGeneratorTests.cpp b/Tests/UnitTests/Core/Detector/GeometryIdGeneratorTests.cpp new file mode 100644 index 00000000000..ce279950f0a --- /dev/null +++ b/Tests/UnitTests/Core/Detector/GeometryIdGeneratorTests.cpp @@ -0,0 +1,177 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Detector/DetectorVolume.hpp" +#include "Acts/Detector/GeometryIdGenerator.hpp" +#include "Acts/Detector/Portal.hpp" +#include "Acts/Detector/PortalGenerators.hpp" +#include "Acts/Geometry/CylinderVolumeBounds.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Navigation/DetectorVolumeFinders.hpp" +#include "Acts/Navigation/SurfaceCandidatesUpdators.hpp" +#include "Acts/Surfaces/CylinderSurface.hpp" +#include "Acts/Tests/CommonHelpers/DetectorElementStub.hpp" +#include "Acts/Utilities/Enumerate.hpp" +#include "Acts/Utilities/Logger.hpp" + +#include +#include +#include + +using namespace Acts; +using namespace Acts::Experimental; + +GeometryContext tContext; + +namespace { + +std::vector> createVolumes( + std::vector>& detectorStore) { + auto portalGenerator = defaultPortalGenerator(); + + auto gap0VoumeBounds = std::make_unique(0, 80, 200); + + auto gap0Volume = DetectorVolumeFactory::construct( + portalGenerator, tContext, "Gap0Volume", Transform3::Identity(), + std::move(gap0VoumeBounds), {}, {}, tryNoVolumes(), tryAllPortals()); + + std::vector layer0Radii = {100, 102, 104, 106, 108, 110}; + auto layer0VolumeBounds = + std::make_unique(80, 130, 200); + std::vector> layer0Surfaces = {}; + for (const auto [ir, r] : enumerate(layer0Radii)) { + // First 4 surfaces are active + if (ir < 4u) { + auto detElement = std::make_shared( + Transform3::Identity(), std::make_shared(r, 190), + 0.1); + detectorStore.push_back(detElement); + layer0Surfaces.push_back(detElement->surface().getSharedPtr()); + } else { + // Last 2 surfaces are passive + layer0Surfaces.push_back(Surface::makeShared( + Transform3::Identity(), std::make_shared(r, 190))); + } + } + + auto layer0Volume = DetectorVolumeFactory::construct( + portalGenerator, tContext, "CylinderVolume", Transform3::Identity(), + std::move(layer0VolumeBounds), layer0Surfaces, {}, tryNoVolumes(), + tryAllPortalsAndSurfaces()); + + auto gap1VoumeBounds = std::make_unique(130, 200, 200); + + auto gap1Volume = DetectorVolumeFactory::construct( + portalGenerator, tContext, "Gap1Volume", Transform3::Identity(), + std::move(gap1VoumeBounds), {}, {}, tryNoVolumes(), tryAllPortals()); + + return {gap0Volume, layer0Volume, gap1Volume}; +} +} // namespace + +BOOST_AUTO_TEST_SUITE(Detector) + +BOOST_AUTO_TEST_CASE(SequentialGeoIdGeneratorReset) { + std::vector> detectorStore; + + auto volumes = createVolumes(detectorStore); + + GeometryIdGenerator::Config cfg; + GeometryIdGenerator generator( + cfg, getDefaultLogger("SequentialIdGenerator", Logging::VERBOSE)); + + auto cache = generator.generateCache(); + for (auto& volume : volumes) { + generator.assignGeometryId(cache, *volume); + } + + // Checking the volume + BOOST_CHECK(volumes[0]->geometryId().volume() == 1); + for (auto [ip, p] : enumerate(volumes[0]->portals())) { + BOOST_CHECK(p->surface().geometryId().boundary() == ip + 1); + } + + BOOST_CHECK(volumes[1]->geometryId().volume() == 2); + for (auto [ip, p] : enumerate(volumes[1]->portals())) { + BOOST_CHECK(p->surface().geometryId().boundary() == ip + 1); + } + for (auto [is, s] : enumerate(volumes[1]->surfaces())) { + if (is < 4u) { + BOOST_CHECK(s->geometryId().sensitive() == is + 1); + } else { + BOOST_CHECK(s->geometryId().passive() == is - 3); + } + } + + BOOST_CHECK(volumes[2]->geometryId().volume() == 3); + for (auto [ip, p] : enumerate(volumes[2]->portals())) { + BOOST_CHECK(p->surface().geometryId().boundary() == ip + 1); + } +} + +BOOST_AUTO_TEST_CASE(SequentialGeoIdGeneratorNoReset) { + std::vector> detectorStore; + + auto volumes = createVolumes(detectorStore); + + GeometryIdGenerator::Config cfg; + cfg.resetSubCounters = false; + GeometryIdGenerator generator( + cfg, getDefaultLogger("SequentialIdGenerator", Logging::VERBOSE)); + + auto cache = generator.generateCache(); + for (auto& volume : volumes) { + generator.assignGeometryId(cache, *volume); + } + + // Checking the volume + BOOST_CHECK(volumes[0]->geometryId().volume() == 1); + unsigned int portalCounter = 1; + for (auto [ip, p] : enumerate(volumes[0]->portals())) { + BOOST_CHECK(p->surface().geometryId().boundary() == portalCounter++); + } + + BOOST_CHECK(volumes[1]->geometryId().volume() == 2); + for (auto [ip, p] : enumerate(volumes[1]->portals())) { + BOOST_CHECK(p->surface().geometryId().boundary() == portalCounter++); + } + + BOOST_CHECK(volumes[2]->geometryId().volume() == 3); + for (auto [ip, p] : enumerate(volumes[2]->portals())) { + BOOST_CHECK(p->surface().geometryId().boundary() == portalCounter++); + } +} + +BOOST_AUTO_TEST_CASE(ContainerGeoIdGenerator) { + std::vector> detectorStore; + + auto volumes = createVolumes(detectorStore); + + GeometryIdGenerator::Config cfg; + cfg.containerMode = true; + cfg.containerId = 15; + GeometryIdGenerator generator( + cfg, getDefaultLogger("ContainerIdGenerator", Logging::VERBOSE)); + + auto cache = generator.generateCache(); + for (auto& volume : volumes) { + generator.assignGeometryId(cache, *volume); + } + + BOOST_CHECK(volumes[0]->geometryId().volume() == 15); + BOOST_CHECK(volumes[0]->geometryId().layer() == 1); + BOOST_CHECK(volumes[1]->geometryId().volume() == 15); + BOOST_CHECK(volumes[1]->geometryId().layer() == 2); + BOOST_CHECK(volumes[2]->geometryId().volume() == 15); + BOOST_CHECK(volumes[2]->geometryId().layer() == 3); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Core/Digitization/CartesianSegmentationTests.cpp b/Tests/UnitTests/Core/Digitization/CartesianSegmentationTests.cpp index 2b9650fe03a..3a7a831f558 100644 --- a/Tests/UnitTests/Core/Digitization/CartesianSegmentationTests.cpp +++ b/Tests/UnitTests/Core/Digitization/CartesianSegmentationTests.cpp @@ -111,8 +111,8 @@ BOOST_AUTO_TEST_CASE(cartesian_segmentation) { // Check the boundary surfaces are thickness away auto centerReadoutPL = boundariesPL[0]->center(tgContext); - auto centerCoutnerPL = boundariesPL[1]->center(tgContext); - double thicknessPL = abs((centerReadoutPL - centerCoutnerPL).z()); + auto centerCounterPL = boundariesPL[1]->center(tgContext); + double thicknessPL = abs((centerReadoutPL - centerCounterPL).z()); CHECK_CLOSE_REL(thicknessPL, 2 * hThickness, 10e-6); diff --git a/Tests/UnitTests/Plugins/ActSVG/DetectorSvgConverterTests.cpp b/Tests/UnitTests/Plugins/ActSVG/DetectorSvgConverterTests.cpp index 99283031c30..3ff15e98348 100644 --- a/Tests/UnitTests/Plugins/ActSVG/DetectorSvgConverterTests.cpp +++ b/Tests/UnitTests/Plugins/ActSVG/DetectorSvgConverterTests.cpp @@ -14,6 +14,7 @@ #include "Acts/Detector/DetectorBuilder.hpp" #include "Acts/Detector/DetectorComponents.hpp" #include "Acts/Detector/DetectorVolume.hpp" +#include "Acts/Detector/GeometryIdGenerator.hpp" #include "Acts/Detector/PortalGenerators.hpp" #include "Acts/Detector/interface/IDetectorComponentBuilder.hpp" #include "Acts/Geometry/CylinderVolumeBounds.hpp" @@ -158,10 +159,14 @@ BOOST_AUTO_TEST_CASE(CylindricalDetector) { detectorCfg, getDefaultLogger("DetectorBuilder", Logging::VERBOSE)); // Detector builder + auto gigConfig = GeometryIdGenerator::Config(); + auto gig = std::make_shared(gigConfig); + Acts::Experimental::DetectorBuilder::Config dCfg; dCfg.auxiliary = "*** Test : Cylindrical Detector ***"; dCfg.name = "CylindricalDetector"; dCfg.builder = containerBuilder; + dCfg.geoIdGenerator = gig; auto detector = DetectorBuilder(dCfg).construct(tContext); diff --git a/Tests/UnitTests/Plugins/Json/DetectorJsonConverterTests.cpp b/Tests/UnitTests/Plugins/Json/DetectorJsonConverterTests.cpp index 987963157f0..2f0b1897c40 100644 --- a/Tests/UnitTests/Plugins/Json/DetectorJsonConverterTests.cpp +++ b/Tests/UnitTests/Plugins/Json/DetectorJsonConverterTests.cpp @@ -14,6 +14,7 @@ #include "Acts/Detector/DetectorBuilder.hpp" #include "Acts/Detector/DetectorVolume.hpp" #include "Acts/Detector/DetectorVolumeBuilder.hpp" +#include "Acts/Detector/GeometryIdGenerator.hpp" #include "Acts/Detector/LayerStructureBuilder.hpp" #include "Acts/Detector/PortalGenerators.hpp" #include "Acts/Detector/VolumeStructureBuilder.hpp" @@ -303,9 +304,14 @@ BOOST_AUTO_TEST_CASE(BeamPipeEndcapBarrelDetector) { std::make_shared( detCompBuilderCfg); + auto gigConfig = Acts::Experimental::GeometryIdGenerator::Config(); + auto gig = + std::make_shared(gigConfig); + Acts::Experimental::DetectorBuilder::Config detBuilderCfg; detBuilderCfg.name = "Detector"; detBuilderCfg.builder = detCompBuilder; + detBuilderCfg.geoIdGenerator = gig; auto detBuilder = std::make_shared(detBuilderCfg); diff --git a/docs/core/experimental_geometry.md b/docs/core/experimental_geometry.md index f800e86cd98..be0bd5f4d9e 100644 --- a/docs/core/experimental_geometry.md +++ b/docs/core/experimental_geometry.md @@ -89,7 +89,7 @@ Illustration of a planar module andcap detector with a grid holding the indices ::: :::{note} -When building in `Debug` mode the containment of objects inside a `DetectorVolume` is checked with an `assert(...)` statement. +When building in `Debug` mode the containment of objects inside a `Acts::DetectorVolume` is checked with an `assert(...)` statement. ::: ### The Detector object @@ -101,7 +101,7 @@ The detector object is the holder class of all geometry objects, it has to conta - a volume finder delegate (as `Acts::Experimental::DetecorVolumeFinder`) that allows to uniquely associate a point in space with a contained volume of the detector. :::{note} -When the detector is constructed, name duplicates are checked for an when failing an `std::exception` is thrown. +When the detector is constructed, name duplicates are checked for and if found a `std::exception` is thrown. Similarly, when sensitive surfaces are provided and duplicate `Acts::GeometryIdentifier` objects are found during detector construction a `std::exception` is thrown. The latter can be avoided by using an appropriate (set of) `Acts::GeometyIdGenerator` tool(s) which will guarantee some level of uniqueness. ::: :::{figure} /figures/geometry/ODD_Detector.png