Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-15 08:18:17

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 #pragma once
0010 
0011 #include "Acts/Geometry/GeometryIdentifier.hpp"
0012 
0013 #include <algorithm>
0014 #include <cassert>
0015 #include <cstddef>
0016 #include <initializer_list>
0017 #include <iterator>
0018 #include <stdexcept>
0019 #include <utility>
0020 #include <vector>
0021 
0022 namespace Acts {
0023 
0024 /// Store values mapped into the geometry hierarchy.
0025 ///
0026 /// @tparam value_t stored value type
0027 ///
0028 /// The core functionality is to find an equivalent element, i.e. an
0029 /// identifier-value pair, for a given geometry identifier via
0030 ///
0031 ///     auto it = container.find(GeometryIdentifier(...));
0032 ///     if (it != container.end()) {
0033 ///         ...
0034 ///     }
0035 ///
0036 /// Trailing zero levels of stored geometry identifiers are used as
0037 /// broadcast values to refer to higher-level objects within the geometry, e.g.
0038 /// a geometry identifier with vanishing approach and sensitive index identifies
0039 /// a layer. An entry will all geometry identifier levels set to zero acts as
0040 /// the global default value.
0041 ///
0042 /// The container also supports range-based iteration over all stored elements
0043 ///
0044 ///     for (const auto& element : container) {
0045 ///         ...
0046 ///     }
0047 ///
0048 /// and index-based access to stored elements and associated geometry
0049 /// identifiers
0050 ///
0051 ///     GeometryIdentifier id3 = container.idAt(3);
0052 ///     const auto& element4 = container.valueAt(4);
0053 ///
0054 /// @note No guarantees are given for the element order when using range-based
0055 ///   or index-based access. Any apparent ordering must be considered an
0056 ///   implementation detail and might change.
0057 ///
0058 /// Adding elements is potentially expensive as the internal lookup structure
0059 /// must be updated. In addition, modifying an element in-place could change its
0060 /// identifier which would also break the lookup. Thus, the container can not be
0061 /// modified after construction to prevent misuse.
0062 template <typename value_t>
0063 class GeometryHierarchyMap {
0064  public:
0065   /// Combined geometry identifier and value element. Only used for input.
0066   using InputElement = typename std::pair<GeometryIdentifier, value_t>;
0067   /// Type alias for const iterator over stored values
0068   using Iterator = typename std::vector<value_t>::const_iterator;
0069   /// Type alias for stored value type
0070   using Value = value_t;
0071 
0072   /// Construct the container from the given elements.
0073   ///
0074   /// @param elements input elements (must be unique with respect to identifier)
0075   explicit GeometryHierarchyMap(std::vector<InputElement> elements);
0076 
0077   /// Construct the container from an initializer list.
0078   ///
0079   /// @param elements input initializer list
0080   GeometryHierarchyMap(std::initializer_list<InputElement> elements);
0081 
0082   // defaulted constructors and assignment operators
0083   GeometryHierarchyMap() = default;
0084   /// Copy constructor
0085   GeometryHierarchyMap(const GeometryHierarchyMap&) = default;
0086   /// Move constructor
0087   GeometryHierarchyMap(GeometryHierarchyMap&&) noexcept = default;
0088   ~GeometryHierarchyMap() = default;
0089   /// Copy assignment operator
0090   /// @return Reference to this object for chaining
0091   GeometryHierarchyMap& operator=(const GeometryHierarchyMap&) = default;
0092   /// Move assignment operator
0093   /// @return Reference to this object for chaining
0094   GeometryHierarchyMap& operator=(GeometryHierarchyMap&&) noexcept = default;
0095 
0096   /// Return an iterator pointing to the beginning of the stored values.
0097   /// @return Iterator to the first element
0098   Iterator begin() const { return m_values.begin(); }
0099 
0100   /// Return an iterator pointing to the end of the stored values.
0101   /// @return Iterator past the last element
0102   Iterator end() const { return m_values.end(); }
0103 
0104   /// Check if any elements are stored.
0105   /// @return True if the container is empty, false otherwise
0106   bool empty() const { return m_values.empty(); }
0107 
0108   /// Return the number of stored elements.
0109   /// @return Number of elements in the container
0110   std::size_t size() const { return m_values.size(); }
0111 
0112   /// Access the geometry identifier for the i-th element with bounds check.
0113   /// @param index The index of the element to access
0114   ///
0115   /// @throws std::out_of_range for invalid indices
0116   /// @return The geometry identifier at the specified index
0117   GeometryIdentifier idAt(std::size_t index) const {
0118     return GeometryIdentifier(m_ids.at(index));
0119   }
0120 
0121   /// Access the value of the i-th element in the container with bounds check.
0122   /// @param index The index of the element to access
0123   ///
0124   /// @throws std::out_of_range for invalid indices
0125   /// @return Reference to the value at the specified index
0126   const Value& valueAt(std::size_t index) const { return m_values.at(index); }
0127 
0128   /// Find the most specific value for a given geometry identifier.
0129   ///
0130   /// This can be either from the element matching exactly to the given geometry
0131   /// id, if it exists, or from the element for the next available higher level
0132   /// within the geometry hierarchy.
0133   ///
0134   /// @param id geometry identifier for which information is requested
0135   /// @retval iterator to an existing value
0136   /// @retval `.end()` iterator if no matching element exists
0137   Iterator find(const GeometryIdentifier& id) const;
0138 
0139   /// Check if the most specific value exists for a given geometry identifier.
0140   ///
0141   /// This function checks if there is an element matching exactly the given
0142   /// geometry id, or from the element for the next available higher level
0143   /// within the geometry hierarchy.
0144   ///
0145   /// @param id geometry identifier for which existence is being checked
0146   /// @retval `true` if a matching element exists
0147   /// @retval `false` if no matching element exists
0148   bool contains(const GeometryIdentifier& id) const;
0149 
0150  private:
0151   // NOTE this class assumes that it knows the ordering of the levels within
0152   //      the geometry id. if the geometry id changes, this code has to be
0153   //      adapted too. the asserts ensure that such a change is caught.
0154   static_assert(GeometryIdentifier().withVolume(1).value() <
0155                     GeometryIdentifier().withVolume(1).withBoundary(1).value(),
0156                 "Incompatible GeometryIdentifier hierarchy");
0157   static_assert(GeometryIdentifier().withBoundary(1).value() <
0158                     GeometryIdentifier().withBoundary(1).withLayer(1).value(),
0159                 "Incompatible GeometryIdentifier hierarchy");
0160   static_assert(GeometryIdentifier().withLayer(1).value() <
0161                     GeometryIdentifier().withLayer(1).withApproach(1).value(),
0162                 "Incompatible GeometryIdentifier hierarchy");
0163   static_assert(
0164       GeometryIdentifier().withApproach(1).value() <
0165           GeometryIdentifier().withApproach(1).withSensitive(1).value(),
0166       "Incompatible GeometryIdentifier hierarchy");
0167 
0168   using Identifier = GeometryIdentifier::Value;
0169 
0170   // encoded ids for all elements for faster lookup.
0171   std::vector<Identifier> m_ids;
0172   // validity bit masks for the ids: which parts to use for comparison
0173   std::vector<Identifier> m_masks;
0174   std::vector<Value> m_values;
0175 
0176   /// Construct a mask where all leading non-zero levels are set.
0177   static constexpr Identifier makeLeadingLevelsMask(GeometryIdentifier id) {
0178     // construct id from encoded value with all bits set
0179     const auto allSet = GeometryIdentifier(~GeometryIdentifier::Value{0u});
0180     // manually iterate over identifier levels starting from the lowest
0181     if (id.sensitive() != 0u) {
0182       // all levels are valid; keep all bits set.
0183       return allSet.withExtra(0u).value();
0184     }
0185     if (id.approach() != 0u) {
0186       return allSet.withExtra(0u).withSensitive(0u).value();
0187     }
0188     if (id.layer() != 0u) {
0189       return allSet.withExtra(0u).withSensitive(0u).withApproach(0u).value();
0190     }
0191     if (id.boundary() != 0u) {
0192       return allSet.withExtra(0u)
0193           .withSensitive(0u)
0194           .withApproach(0u)
0195           .withLayer(0u)
0196           .value();
0197     }
0198     if (id.volume() != 0u) {
0199       return allSet.withExtra(0u)
0200           .withSensitive(0u)
0201           .withApproach(0u)
0202           .withLayer(0u)
0203           .withBoundary(0u)
0204           .value();
0205     }
0206     // no valid levels; all bits are zero.
0207     return Identifier{0u};
0208   }
0209 
0210   /// Construct a mask where only the highest level is set.
0211   static constexpr Identifier makeHighestLevelMask() {
0212     return makeLeadingLevelsMask(GeometryIdentifier(0u).withVolume(1u));
0213   }
0214 
0215   /// Compare the two identifiers only within the masked bits.
0216   static constexpr bool equalWithinMask(Identifier lhs, Identifier rhs,
0217                                         Identifier mask) {
0218     return (lhs & mask) == (rhs & mask);
0219   }
0220 
0221   /// Ensure identifier ordering and uniqueness.
0222   static void sortAndCheckDuplicates(std::vector<InputElement>& elements);
0223 
0224   /// Fill the container from the input elements.
0225   ///
0226   /// This assumes that the elements are ordered and unique with respect to
0227   /// their identifiers.
0228   void fill(const std::vector<InputElement>& elements);
0229 };
0230 
0231 // implementations
0232 
0233 template <typename value_t>
0234 inline GeometryHierarchyMap<value_t>::GeometryHierarchyMap(
0235     std::vector<InputElement> elements) {
0236   sortAndCheckDuplicates(elements);
0237   fill(elements);
0238 }
0239 
0240 template <typename value_t>
0241 inline GeometryHierarchyMap<value_t>::GeometryHierarchyMap(
0242     std::initializer_list<InputElement> elements)
0243     : GeometryHierarchyMap(
0244           std::vector<InputElement>(elements.begin(), elements.end())) {}
0245 
0246 template <typename value_t>
0247 inline void GeometryHierarchyMap<value_t>::sortAndCheckDuplicates(
0248     std::vector<InputElement>& elements) {
0249   // ensure elements are sorted by identifier
0250   std::ranges::sort(elements, [=](const auto& lhs, const auto& rhs) {
0251     return lhs.first < rhs.first;
0252   });
0253 
0254   // Check that all elements have unique identifier
0255   auto dup = std::ranges::adjacent_find(
0256       elements,
0257       [](const auto& lhs, const auto& rhs) { return lhs.first == rhs.first; });
0258 
0259   if (dup != elements.end()) {
0260     throw std::invalid_argument("Input elements contain duplicates");
0261   }
0262 }
0263 
0264 template <typename value_t>
0265 inline void GeometryHierarchyMap<value_t>::fill(
0266     const std::vector<InputElement>& elements) {
0267   m_ids.clear();
0268   m_masks.clear();
0269   m_values.clear();
0270 
0271   m_ids.reserve(elements.size());
0272   m_masks.reserve(elements.size());
0273   m_values.reserve(elements.size());
0274 
0275   for (const auto& element : elements) {
0276     m_ids.push_back(element.first.value());
0277     m_masks.push_back(
0278         makeLeadingLevelsMask(GeometryIdentifier(element.first.value())));
0279     m_values.push_back(std::move(element.second));
0280   }
0281 }
0282 
0283 template <typename value_t>
0284 inline auto GeometryHierarchyMap<value_t>::find(
0285     const GeometryIdentifier& id) const -> Iterator {
0286   assert((m_ids.size() == m_values.size()) &&
0287          "Inconsistent container state: #ids != # values");
0288   assert((m_masks.size() == m_values.size()) &&
0289          "Inconsistent container state: #masks != #values");
0290 
0291   // we can not search for the element directly since the relevant one
0292   // might be stored at a higher level. ids for higher levels would always
0293   // be sorted before the requested id. searching for the first element
0294   // after the requested ensures that we include the full hierarchy.
0295   const auto it = std::ranges::upper_bound(m_ids, id.value());
0296   auto i = static_cast<std::size_t>(std::ranges::distance(m_ids.begin(), it));
0297 
0298   // now go up the hierarchy to find the first matching element.
0299   // example: the container stores four identifiers
0300   //
0301   //     2|x|x (volume-only)
0302   //     2|2|1 (volume, layer, and sensitive)
0303   //     2|3|x (volume and layer)
0304   //     2|3|4 (volume, layer, and sensitive)
0305   //
0306   // where | marks level boundaries. searching for either 2|3|4, 2|3|7, or
0307   // 2|4|x would first point to 2|3|4 and thus needs to go up the hierarchy.
0308   while (0 < i) {
0309     // index always starts after item of interest due to upper bound search
0310     --i;
0311 
0312     // if the input id does not even match at the highest hierarchy level
0313     // with the current comparison id, then have reached the end of this
0314     // hierarchy. having a special check for the highest level avoids an
0315     // unbounded search window all the way to the beginning of the container for
0316     // the global default entry.
0317     if (!equalWithinMask(id.value(), m_ids[i], makeHighestLevelMask())) {
0318       // check if a global default entry exists
0319       if (m_ids.front() == Identifier{0u}) {
0320         return begin();
0321       } else {
0322         return end();
0323       }
0324     }
0325 
0326     // since the search is going backwards in the sorted container, it
0327     // progresses from more specific to less specific elements. the first
0328     // match is automatically the appropriate one.
0329     if (equalWithinMask(id.value(), m_ids[i], m_masks[i])) {
0330       return std::next(begin(), static_cast<std::ptrdiff_t>(i));
0331     }
0332   }
0333 
0334   // all options are exhausted and no matching element was found.
0335   return end();
0336 }
0337 
0338 template <typename value_t>
0339 inline auto GeometryHierarchyMap<value_t>::contains(
0340     const GeometryIdentifier& id) const -> bool {
0341   return this->find(id) != this->end();
0342 }
0343 
0344 }  // namespace Acts