Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:10:53

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/Definitions/Units.hpp"
0013 #include "Acts/Geometry/GeometryContext.hpp"
0014 #include "Acts/Geometry/ProtoLayer.hpp"
0015 #include "Acts/Surfaces/Surface.hpp"
0016 #include "Acts/Surfaces/SurfaceArray.hpp"
0017 #include "Acts/Utilities/AxisDefinitions.hpp"
0018 #include "Acts/Utilities/BinningType.hpp"
0019 #include "Acts/Utilities/Logger.hpp"
0020 
0021 #include <algorithm>
0022 #include <cmath>
0023 #include <cstddef>
0024 #include <functional>
0025 #include <iterator>
0026 #include <memory>
0027 #include <numbers>
0028 #include <optional>
0029 #include <ostream>
0030 #include <tuple>
0031 #include <utility>
0032 #include <vector>
0033 
0034 namespace Acts {
0035 namespace Test {
0036 struct SurfaceArrayCreatorFixture;
0037 }
0038 
0039 using SurfaceMatcher =
0040     std::function<bool(const GeometryContext& gctx, AxisDirection,
0041                        const Surface*, const Surface*)>;
0042 
0043 using SurfaceVector = std::vector<const Surface*>;
0044 using SurfaceMatrix = std::vector<SurfaceVector>;
0045 
0046 using V3Vector = std::vector<Vector3>;
0047 using V3Matrix = std::vector<V3Vector>;
0048 
0049 using AxisScalar = Vector3::Scalar;
0050 
0051 /// @class SurfaceArrayCreator
0052 ///
0053 /// It is designed create sub surface arrays to be ordered on Surfaces
0054 ///
0055 /// @todo write more documentation on how this is done
0056 class SurfaceArrayCreator {
0057  public:
0058   friend struct Acts::Test::SurfaceArrayCreatorFixture;
0059   friend class Acts::SurfaceArray;
0060 
0061   struct ProtoAxis {
0062     BinningType bType = BinningType::equidistant;
0063     AxisDirection axisDir = AxisDirection::AxisX;
0064     std::size_t nBins = 0;
0065     AxisScalar min = 0;
0066     AxisScalar max = 0;
0067     std::vector<AxisScalar> binEdges;
0068 
0069     std::size_t getBin(AxisScalar x) const {
0070       if (binEdges.empty()) {
0071         // equidistant
0072         AxisScalar w = (max - min) / nBins;
0073         return static_cast<std::size_t>(std::floor((x - min) / w));
0074       } else {
0075         // variable
0076         const auto it =
0077             std::upper_bound(std::begin(binEdges), std::end(binEdges), x);
0078         return std::distance(std::begin(binEdges), it) - 1;
0079       }
0080     }
0081   };
0082 
0083   // Configuration struct
0084   struct Config {
0085     /// Type-erased function which determines whether two surfaces are supposed
0086     /// to be considered equivalent in terms of the binning
0087     SurfaceMatcher surfaceMatcher = SurfaceArrayCreator::isSurfaceEquivalent;
0088 
0089     /// Optimize the binning in phi for disc layers. Reduces the number
0090     /// of bins to the lowest number of non-equivalent phi surfaces
0091     /// of all r-bins. If false, this step is skipped.
0092     bool doPhiBinningOptimization = true;
0093   };
0094 
0095   /// Constructor with default config
0096   ///
0097   /// @param logger logging instance
0098   SurfaceArrayCreator(std::unique_ptr<const Logger> logger = getDefaultLogger(
0099                           "SurfaceArrayCreator", Logging::INFO))
0100       : m_cfg(Config()), m_logger(std::move(logger)) {}
0101   /// Constructor with explicit config
0102   ///
0103   /// @param cfg Explicit config struct
0104   /// @param logger logging instance
0105   SurfaceArrayCreator(const Config& cfg,
0106                       std::unique_ptr<const Logger> logger = getDefaultLogger(
0107                           "SurfaceArrayCreator", Logging::INFO))
0108       : m_cfg(cfg), m_logger(std::move(logger)) {}
0109 
0110   /// Destructor
0111   virtual ~SurfaceArrayCreator() = default;
0112 
0113   /// SurfaceArrayCreator interface method
0114   ///
0115   /// - create an array in a cylinder, binned in phi, z when extremas and
0116   /// bin numbers are known
0117   /// @warning This function requires the cylinder aligned with the z-axis
0118   /// @param surfaces is the vector of pointers to sensitive surfaces
0119   /// to be ordered on the cylinder
0120   /// @pre the pointers to the sensitive surfaces in the surfaces vectors all
0121   /// need to be valid, since no check is performed
0122   /// @param [in] gctx The gometry context for this building call
0123   /// @param protoLayerOpt The proto layer containing the layer size
0124   /// @param binsPhi is the number of bins in phi for the surfaces
0125   /// @param binsZ is the number of bin in Z for the surfaces
0126   /// @param transform is the (optional) additional transform applied
0127   ///
0128   /// @return a unique pointer to a new SurfaceArray
0129   std::unique_ptr<SurfaceArray> surfaceArrayOnCylinder(
0130       const GeometryContext& gctx,
0131       std::vector<std::shared_ptr<const Surface>> surfaces, std::size_t binsPhi,
0132       std::size_t binsZ, std::optional<ProtoLayer> protoLayerOpt = std::nullopt,
0133       const Transform3& transform = Transform3::Identity()) const;
0134 
0135   /// SurfaceArrayCreator interface method
0136   ///
0137   /// - create an array in a cylinder, binned in phi, z when extremas and bin
0138   /// numbers are unknown - this method goes through the surfaces and finds out
0139   /// the needed information
0140   /// @warning This function requires the cylinder aligned with the z-axis
0141   /// @param surfaces is the vector of pointers to sensitive surfaces
0142   /// to be ordered on the cylinder
0143   /// @pre the pointers to the sensitive surfaces in the surfaces vectors all
0144   /// need to be valid, since no check is performed
0145   /// @param [in] gctx The gometry context for this building call
0146   /// @param protoLayerOpt The proto layer containing the layer size
0147   /// @param bTypePhi the binning type in phi direction (equidistant/arbitrary)
0148   /// @param bTypeZ the binning type in z direction (equidistant/arbitrary)
0149   /// @param transform is the (optional) additional transform applied
0150   ///
0151   /// @return a unique pointer a new SurfaceArray
0152   std::unique_ptr<Acts::SurfaceArray> surfaceArrayOnCylinder(
0153       const GeometryContext& gctx,
0154       std::vector<std::shared_ptr<const Surface>> surfaces,
0155       BinningType bTypePhi = equidistant, BinningType bTypeZ = equidistant,
0156       std::optional<ProtoLayer> protoLayerOpt = std::nullopt,
0157       const Transform3& transform = Transform3::Identity()) const;
0158 
0159   /// SurfaceArrayCreator interface method
0160   /// - create an array on a disc, binned in r, phi when extremas and
0161   /// bin numbers are known
0162   ///
0163   /// @param surfaces is the vector of pointers to sensitive surfaces
0164   /// to be ordered on the disc
0165   /// @pre the pointers to the sensitive surfaces in the surfaces vectors all
0166   /// need to be valid, since no check is performed
0167   /// @warning This function requires the disc aligned with the z-axis
0168   /// @param [in] gctx The gometry context for this building call
0169   /// @param protoLayerOpt The proto layer containing the layer size
0170   /// @param binsPhi is the number of bins in phi for the surfaces
0171   /// @param binsR is the number of bin in R for the surfaces
0172   /// @param transform is the (optional) additional transform applied
0173   ///
0174   /// @return a unique pointer a new SurfaceArray
0175   std::unique_ptr<SurfaceArray> surfaceArrayOnDisc(
0176       const GeometryContext& gctx,
0177       std::vector<std::shared_ptr<const Surface>> surfaces, std::size_t binsR,
0178       std::size_t binsPhi,
0179       std::optional<ProtoLayer> protoLayerOpt = std::nullopt,
0180       const Transform3& transform = Transform3::Identity()) const;
0181 
0182   /// SurfaceArrayCreator interface method
0183   ///
0184   /// - create an array in a cylinder, binned in phi, r when extremas and bin
0185   /// numbers are unknown - this method goes through the surfaces and finds out
0186   /// the needed information
0187   /// @param surfaces is the vector of pointers to sensitive surfaces
0188   /// to be ordered on the disc
0189   /// @pre the pointers to the sensitive surfaces in the surfaces vectors all
0190   /// need to be valid, since no check is performed
0191   /// @warning This function requires the disc aligned with the z-axis
0192   /// @param [in] gctx The gometry context for this building call
0193   /// @param protoLayerOpt The proto layer containing the layer size
0194   /// @param bTypeR the binning type in r direction (equidistant/arbitrary)
0195   /// @param bTypePhi the binning type in phi direction (equidistant/arbitrary)
0196   /// @param transform is the (optional) additional transform applied
0197   ///
0198   /// @return a unique pointer a new SurfaceArray
0199   /// @note If there is more than on R-Ring, number of phi bins
0200   ///       will be set to lowest number of surfaces of any R-ring.
0201   ///       This ignores bTypePhi and produces equidistant binning in phi
0202   std::unique_ptr<Acts::SurfaceArray> surfaceArrayOnDisc(
0203       const GeometryContext& gctx,
0204       std::vector<std::shared_ptr<const Surface>> surfaces, BinningType bTypeR,
0205       BinningType bTypePhi,
0206       std::optional<ProtoLayer> protoLayerOpt = std::nullopt,
0207       const Transform3& transform = Transform3::Identity()) const;
0208 
0209   /// SurfaceArrayCreator interface method
0210   /// - create an array on a plane
0211   ///
0212   /// @param [in] gctx The gometry context for this building call
0213   /// @param [in] surfaces is the vector of pointers to sensitive surfaces
0214   /// to be ordered on the plane
0215   /// @pre the pointers to the sensitive surfaces in the surfaces vectors all
0216   /// need to be valid, since no check is performed
0217   /// @warning This function requires the plane aligned with either the x-, y-
0218   /// or z-axis
0219   /// @param [in] bins1 is the number of bins in the orthogonal direction to @p
0220   /// aDir
0221   /// @param [in] bins2 is the number of bins in the orthogonal direction to @p
0222   /// aDir
0223   /// @param [in] aDir Direction of the aligned surfaces
0224   /// @param [in] protoLayerOpt Optional @c ProtoLayer instance
0225   /// @param [in] transform is the (optional) additional transform applied
0226   ///
0227   /// @return a unique pointer a new SurfaceArray
0228   std::unique_ptr<SurfaceArray> surfaceArrayOnPlane(
0229       const GeometryContext& gctx,
0230       std::vector<std::shared_ptr<const Surface>> surfaces, std::size_t bins1,
0231       std::size_t bins2, AxisDirection aDir,
0232       std::optional<ProtoLayer> protoLayerOpt = std::nullopt,
0233       const Transform3& transform = Transform3::Identity()) const;
0234 
0235   /// Static check function for surface equivalent
0236   ///
0237   /// @param [in] gctx the geometry context for this check
0238   /// @param aDir the axis direction for the binning
0239   /// @param a first surface for checking
0240   /// @param b second surface for checking
0241   static bool isSurfaceEquivalent(const GeometryContext& gctx,
0242                                   AxisDirection aDir, const Surface* a,
0243                                   const Surface* b) {
0244     using namespace UnitLiterals;
0245     using VectorHelpers::perp;
0246 
0247     if (aDir == Acts::AxisDirection::AxisPhi) {
0248       // Take the two binning positions
0249       Vector3 pos1 = a->referencePosition(gctx, AxisDirection::AxisR);
0250       Vector3 pos2 = b->referencePosition(gctx, AxisDirection::AxisR);
0251 
0252       // Project them on the (x, y) plane, where Phi angles are calculated
0253       auto proj1 = pos1.head<2>(), proj2 = pos2.head<2>();
0254 
0255       // Basic dot and cross products identities give us the cosine and sine
0256       // of these angles, time the squared vector norm
0257       auto cos_dPhi_n2 = proj1.dot(proj2);
0258       auto sin_dPhi_n2 = proj1.x() * proj2.y() - proj2.x() * proj1.y();
0259 
0260       // ...so by injecting them into atan2, we get the angle between them
0261       auto dPhi = std::atan2(sin_dPhi_n2, cos_dPhi_n2);
0262       return std::abs(dPhi) < std::numbers::pi / 180.;
0263     }
0264 
0265     if (aDir == Acts::AxisDirection::AxisZ) {
0266       return (std::abs(a->referencePosition(gctx, AxisDirection::AxisR).z() -
0267                        b->referencePosition(gctx, AxisDirection::AxisR).z()) <
0268               1_um);
0269     }
0270 
0271     if (aDir == Acts::AxisDirection::AxisR) {
0272       return (std::abs(perp(a->referencePosition(gctx, AxisDirection::AxisR)) -
0273                        perp(b->referencePosition(gctx, AxisDirection::AxisR))) <
0274               1_um);
0275     }
0276 
0277     return false;
0278   }
0279 
0280   /// Set logging instance
0281   /// @param logger is the logging instance to be set
0282   void setLogger(std::unique_ptr<const Logger> logger) {
0283     m_logger = std::move(logger);
0284   }
0285 
0286  private:
0287   /// configuration object
0288   Config m_cfg;
0289 
0290   /// Private access to logger
0291   const Logger& logger() const { return *m_logger; }
0292 
0293   std::vector<const Surface*> findKeySurfaces(
0294       const std::vector<const Surface*>& surfaces,
0295       const std::function<bool(const Surface*, const Surface*)>& equal) const;
0296 
0297   std::size_t determineBinCount(const GeometryContext& gctx,
0298                                 const std::vector<const Surface*>& surfaces,
0299                                 AxisDirection aDir) const;
0300 
0301   /// SurfaceArrayCreator internal method
0302   /// Creates a variable @c ProtoAxis from a vector of (unsorted) surfaces with
0303   /// PlanarBounds
0304   /// It loops through the surfaces and finds out the needed information
0305   /// First the surfaces are sorted in the binning direction and the so called
0306   /// "key" surfaces (surfaces with different positions in the binning
0307   /// direction) are extracted. The boundary value between two surfaces is the
0308   /// mean value of the two center position in the binning direction. The first
0309   /// and the last boundaries are calculated from the vertices of the first and
0310   /// last surface.
0311   /// @note currently implemented for phi, r and z bining
0312   /// @todo implement for x,y binning
0313   /// @param [in] gctx the geometry context for this call
0314   /// @param surfaces are the sensitive surfaces to be
0315   /// @param aDir the AxisDirection in which direction should be binned
0316   /// (currently possible: AxisPhi, AxisR, AxisZ)
0317   /// @param protoLayer Instance of @c ProtoLayer holding generic layer info
0318   /// @param transform is the (optional) additional transform applied
0319   /// @return Instance of @c ProtoAxis containing determined properties
0320   /// @note This only creates the @c ProtoAxis, this needs to be turned
0321   ///       into an actual @c Axis object to be used
0322   ProtoAxis createVariableAxis(const GeometryContext& gctx,
0323                                const std::vector<const Surface*>& surfaces,
0324                                AxisDirection aDir, const ProtoLayer& protoLayer,
0325                                Transform3& transform) const;
0326 
0327   /// SurfaceArrayCreator internal method
0328   /// Creates a equidistant @c ProtoAxis when the extremas and the bin number
0329   /// are
0330   /// It loops through the surfaces and finds out the needed information
0331   /// First the surfaces are sorted in the binning direction and the so called
0332   /// "key" surfaces (surfaces with different positions in the binning
0333   /// direction) are extracted. The number of key surfaces euqals the number of
0334   /// bins. Afterwards the minimum and maximum are calculated by
0335   /// subtracting/adding half of a bin size to the center position (in the
0336   /// binning direction) to the first/last surface.
0337   /// @note currently implemented for phi, r and z bining
0338   /// @todo implement for x,y binning
0339   /// @param [in] gctx the geometry context for this call
0340   /// @param surfaces are the sensitive surfaces to be
0341   /// @param aDir the AxisDirection in which direction should be binned
0342   /// (currently possible: AxisPhi, AxisR, AxisZ)
0343   /// @param protoLayer Instance of @c ProtoLayer holding generic layer info
0344   /// @param transform is the (optional) additional transform applied
0345   /// @param nBins Number of bins to use, 0 means determine automatically
0346   /// @return Instance of @c ProtoAxis containing determined properties
0347   /// @note This only creates the @c ProtoAxis, this needs to be turned
0348   ///       into an actual @c Axis object to be used
0349   ProtoAxis createEquidistantAxis(const GeometryContext& gctx,
0350                                   const std::vector<const Surface*>& surfaces,
0351                                   AxisDirection aDir,
0352                                   const ProtoLayer& protoLayer,
0353                                   Transform3& transform,
0354                                   std::size_t nBins = 0) const;
0355 
0356   /// SurfaceArrayCreator internal method
0357   /// @brief Creates a SurfaceGridLookup instance within an any
0358   /// This is essentially a factory which absorbs some if/else logic
0359   /// that is required by the templating.
0360   /// @tparam bdtA AxisBoundaryType of axis A
0361   /// @tparam bdtB AxisBoundaryType of axis B
0362   /// @tparam F1 type-deducted value of g2l lambda
0363   /// @tparam F2 type-deducted value of l2g lambda
0364   /// @param globalToLocal transform callable
0365   /// @param localToGlobal transform callable
0366   /// @param pAxisA ProtoAxis object for axis A
0367   /// @param pAxisB ProtoAxis object for axis B
0368   template <AxisBoundaryType bdtA, AxisBoundaryType bdtB, typename F1,
0369             typename F2>
0370   static std::unique_ptr<SurfaceArray::ISurfaceGridLookup>
0371   makeSurfaceGridLookup2D(F1 globalToLocal, F2 localToGlobal, ProtoAxis pAxisA,
0372                           ProtoAxis pAxisB) {
0373     using ISGL = SurfaceArray::ISurfaceGridLookup;
0374     std::unique_ptr<ISGL> ptr;
0375 
0376     // this becomes completely unreadable otherwise
0377     // clang-format off
0378     if (pAxisA.bType == equidistant && pAxisB.bType == equidistant) {
0379 
0380       Axis<AxisType::Equidistant, bdtA> axisA(pAxisA.min, pAxisA.max, pAxisA.nBins);
0381       Axis<AxisType::Equidistant, bdtB> axisB(pAxisB.min, pAxisB.max, pAxisB.nBins);
0382 
0383       using SGL = SurfaceArray::SurfaceGridLookup<decltype(axisA), decltype(axisB)>;
0384       ptr = std::make_unique<SGL>(
0385             globalToLocal, localToGlobal, std::pair{axisA, axisB}, std::vector{pAxisA.axisDir, pAxisB.axisDir});
0386 
0387     } else if (pAxisA.bType == equidistant && pAxisB.bType == arbitrary) {
0388 
0389       Axis<AxisType::Equidistant, bdtA> axisA(pAxisA.min, pAxisA.max, pAxisA.nBins);
0390       Axis<AxisType::Variable, bdtB> axisB(pAxisB.binEdges);
0391 
0392       using SGL = SurfaceArray::SurfaceGridLookup<decltype(axisA), decltype(axisB)>;
0393       ptr = std::make_unique<SGL>(
0394             globalToLocal, localToGlobal, std::pair{axisA, axisB}, std::vector{pAxisA.axisDir, pAxisB.axisDir});
0395 
0396     } else if (pAxisA.bType == arbitrary && pAxisB.bType == equidistant) {
0397 
0398       Axis<AxisType::Variable, bdtA> axisA(pAxisA.binEdges);
0399       Axis<AxisType::Equidistant, bdtB> axisB(pAxisB.min, pAxisB.max, pAxisB.nBins);
0400 
0401       using SGL = SurfaceArray::SurfaceGridLookup<decltype(axisA), decltype(axisB)>;
0402       ptr = std::make_unique<SGL>(
0403             globalToLocal, localToGlobal, std::pair{axisA, axisB}, std::vector{pAxisA.axisDir, pAxisB.axisDir});
0404 
0405     } else /*if (pAxisA.bType == arbitrary && pAxisB.bType == arbitrary)*/ {
0406 
0407       Axis<AxisType::Variable, bdtA> axisA(pAxisA.binEdges);
0408       Axis<AxisType::Variable, bdtB> axisB(pAxisB.binEdges);
0409 
0410       using SGL = SurfaceArray::SurfaceGridLookup<decltype(axisA), decltype(axisB)>;
0411       ptr = std::make_unique<SGL>(
0412             globalToLocal, localToGlobal, std::pair{axisA, axisB}, std::vector{pAxisA.axisDir, pAxisB.axisDir});
0413     }
0414     // clang-format on
0415 
0416     return ptr;
0417   }
0418 
0419   /// logging instance
0420   std::unique_ptr<const Logger> m_logger;
0421 
0422   /// Private helper method to complete the binning
0423   ///
0424   ///
0425   ///  given a grid point o
0426   ///    |  0  |  1 |  2  |  3 |  4  |
0427   ///    ------------------------------
0428   ///  0 |  x  |    |     |    |  x  |
0429   ///  1 |     |    |  o  |    |     |
0430   ///  2 |  x  |    |     |    |  x  |
0431   ///
0432   /// This is being called when you chose to use more bins thans surfaces
0433   /// I.e. to put a finer granularity binning onto your surface
0434   /// Neighbour bins are then filled to contain pointers as well
0435   /// This method delegates to SurfaceGridLookup itself.
0436   /// @param [in] gctx the geometry context for this call
0437   /// @param sl The @c SurfaceGridLookup
0438   /// @param surfaces the surfaces
0439   void completeBinning(const GeometryContext& gctx,
0440                        SurfaceArray::ISurfaceGridLookup& sl,
0441                        const std::vector<const Surface*>& surfaces) const {
0442     ACTS_VERBOSE(
0443         "Complete binning by filling closest neighbour surfaces into "
0444         "empty bins.");
0445 
0446     std::size_t binCompleted = sl.completeBinning(gctx, surfaces);
0447 
0448     ACTS_VERBOSE("       filled  : " << binCompleted
0449                                      << " (includes under/overflow)");
0450   }
0451 
0452   /// Private helper method to transform the  vertices of surface bounds into
0453   /// global coordinates
0454   /// @param [in] gctx the geometry context for this call
0455   /// @param surface the surface associated with the given vertices
0456   /// @param locVertices a vector of the vertices in local coordinates
0457   /// @return a vector of the vertices in global coordinates
0458   std::vector<Acts::Vector3> makeGlobalVertices(
0459       const GeometryContext& gctx, const Acts::Surface& surface,
0460       const std::vector<Acts::Vector2>& locVertices) const;
0461 };
0462 
0463 }  // namespace Acts