Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-05 08:10:56

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/Definitions/Algebra.hpp"
0012 #include "Acts/Detector/detail/ReferenceGenerators.hpp"
0013 #include "Acts/Detector/interface/ISurfacesProvider.hpp"
0014 #include "Acts/Surfaces/Surface.hpp"
0015 #include "Acts/Utilities/AxisDefinitions.hpp"
0016 #include "Acts/Utilities/KDTree.hpp"
0017 
0018 #include <array>
0019 #include <ranges>
0020 #include <stdexcept>
0021 #include <tuple>
0022 #include <vector>
0023 
0024 namespace Acts::Experimental {
0025 
0026 /// @brief A wrapper class around a KDTree of surfaces
0027 ///
0028 /// It also deals with the conversion from global query to
0029 /// KDTree lookup positions
0030 ///
0031 template <std::size_t kDIM = 2u, std::size_t bSize = 100u,
0032           typename reference_generator =
0033               detail::PolyhedronReferenceGenerator<1u, false>>
0034 class KdtSurfaces {
0035  public:
0036   /// Broadcast the surface KDT type
0037   using KDTS =
0038       KDTree<kDIM, std::shared_ptr<Surface>, double, std::array, bSize>;
0039 
0040   /// Broadcast the query definition
0041   using Query = std::array<double, kDIM>;
0042 
0043   /// Broadcast the entry
0044   using Entry = std::pair<Query, std::shared_ptr<Surface>>;
0045 
0046   /// Constructor from a vector of surfaces
0047   ///
0048   /// @param gctx the geometry context of this call
0049   /// @param surfaces the surfaces to be filled into the tree
0050   /// @param casts the cast list from global position into kdtree local
0051   /// @param rgen the reference point generator
0052   KdtSurfaces(const GeometryContext& gctx,
0053               const std::vector<std::shared_ptr<Surface>>& surfaces,
0054               const std::array<AxisDirection, kDIM>& casts,
0055               const reference_generator& rgen =
0056                   detail::PolyhedronReferenceGenerator<1u, false>{})
0057       : m_kdt(nullptr), m_casts(casts), m_rGenerator(rgen) {
0058     // Simple check if the dimension is correct
0059     if (kDIM == 0u) {
0060       throw std::invalid_argument(
0061           "KdtSurfaces: dimension and/or cast rules are incorrect.");
0062     }
0063     // Fill the tree from surfaces
0064     std::vector<Entry> kdtEntries;
0065     kdtEntries.reserve(surfaces.size());
0066     for (auto& s : surfaces) {
0067       // Generate the references and the center of gravity from it
0068       const auto references = m_rGenerator.references(gctx, *s);
0069       std::vector<Query> castedReferences;
0070       castedReferences.reserve(references.size());
0071       for (const auto& r : references) {
0072         //  Now cast into the correct fill position
0073         Query rc = {};
0074         fillCasts(r, rc, std::make_integer_sequence<std::size_t, kDIM>{});
0075         castedReferences.push_back(rc);
0076       }
0077       // Calculate the center of gravity in casted frame
0078       kdtEntries.push_back({cog(castedReferences), s});
0079     }
0080     // Create the KDTree
0081     m_kdt = std::make_unique<KDTS>(std::move(kdtEntries));
0082   }
0083 
0084   /// Query with a Range object
0085   ///
0086   /// @param range is the range to be queried
0087   ///
0088   /// @return the matching surfaces from the KDT structure
0089   std::vector<std::shared_ptr<Surface>> surfaces(
0090       const RangeXD<kDIM, double>& range) const {
0091     // Strip the surfaces
0092     std::vector<std::shared_ptr<Surface>> surfacePtrs;
0093     auto surfaceQuery = m_kdt->rangeSearchWithKey(range);
0094     std::ranges::for_each(
0095         surfaceQuery, [&](auto& surf) { surfacePtrs.push_back(surf.second); });
0096     return surfacePtrs;
0097   }
0098 
0099   /// Query with an Extent object
0100   ///
0101   /// @param extent is the range Extent to be queried
0102   ///
0103   /// @return the matching surfaces fpulled from the KDT structure
0104   std::vector<std::shared_ptr<Surface>> surfaces(const Extent& extent) const {
0105     RangeXD<kDIM, double> qRange;
0106     for (auto [ibv, v] : enumerate(m_casts)) {
0107       qRange[ibv] = extent.range(v);
0108     }
0109     return surfaces(qRange);
0110   }
0111 
0112  private:
0113   /// The KDTree as single source for the surfaces (maybe shared)
0114   std::unique_ptr<KDTS> m_kdt = nullptr;
0115 
0116   /// Cast values that turn a global position to lookup position
0117   std::array<AxisDirection, kDIM> m_casts = {};
0118 
0119   /// Helper to generate reference points for filling
0120   reference_generator m_rGenerator;
0121 
0122   /// Unroll the cast loop
0123   /// @param position is the position of the update call
0124   /// @param a is the array to be filled
0125   template <typename Array, std::size_t... idx>
0126   void fillCasts(const Vector3& position, Array& a,
0127                  std::index_sequence<idx...> /*indices*/) const {
0128     ((a[idx] = VectorHelpers::cast(position, m_casts[idx])), ...);
0129   }
0130 
0131   /// Helper method to calculate the center of gravity in the
0132   /// casted frame (i.e. query frame)
0133   ///
0134   /// @param cQueries are the casted query positions
0135   /// @note will do nothing if vector size is equal to 1
0136   ///
0137   /// @note no checking on qQueries.empty() is done as the
0138   /// positions are to be provided by a generator which itself
0139   /// is tested for consistency
0140   ///
0141   /// @return the center of gravity as a query object
0142   Query cog(const std::vector<Query>& cQueries) const {
0143     // If there is only one position, return it
0144     if (cQueries.size() == 1) {
0145       return cQueries.front();
0146     }
0147     // Build the center of gravity of the n positions
0148     Query c{};
0149     float weight = 1. / cQueries.size();
0150     for (auto& q : cQueries) {
0151       std::transform(c.begin(), c.end(), q.begin(), c.begin(),
0152                      std::plus<double>());
0153     }
0154     std::ranges::for_each(c, [&](auto& v) { v *= weight; });
0155     return c;
0156   }
0157 };
0158 
0159 /// @brief Callable struct wrapper around the KDT surface structure
0160 ///
0161 /// This allows to create small region based callable structs at
0162 /// configuration level that are then connected to an InternalStructureBuilder
0163 template <std::size_t kDIM = 2u, std::size_t bSize = 100u,
0164           typename reference_generator =
0165               detail::PolyhedronReferenceGenerator<1u, false>>
0166 class KdtSurfacesProvider : public ISurfacesProvider {
0167  public:
0168   /// The prefilled surfaces in a KD tree structure, it is generally shared
0169   /// amongst different providers
0170   ///
0171   /// @param kdts the prefilled KDTree structure
0172   /// @param kregion the region where these are pulled from
0173   KdtSurfacesProvider(
0174       std::shared_ptr<KdtSurfaces<kDIM, bSize, reference_generator>> kdts,
0175       const Extent& kregion)
0176       : m_kdt(std::move(kdts)), m_region(kregion) {
0177     /// Sanity check that the KDTree is not empty
0178     if (m_kdt == nullptr) {
0179       throw std::invalid_argument(
0180           "KdtSurfacesProvider: no KDTree structure provided.");
0181     }
0182   }
0183 
0184   /// The call to provide the surfaces
0185   std::vector<std::shared_ptr<Surface>> surfaces(
0186       [[maybe_unused]] const GeometryContext& gctx) const final {
0187     return m_kdt->surfaces(m_region);
0188   }
0189 
0190  private:
0191   std::shared_ptr<KdtSurfaces<kDIM, bSize, reference_generator>> m_kdt =
0192       nullptr;
0193   /// The query region
0194   Extent m_region;
0195 };
0196 
0197 }  // namespace Acts::Experimental