Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 09:23:18

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include "Acts/Seeding2/DoubletSeedFinder.hpp"
0010 
0011 #include "Acts/EventData/SpacePointContainer2.hpp"
0012 #include "Acts/Utilities/MathHelpers.hpp"
0013 
0014 #include <stdexcept>
0015 
0016 #include <boost/mp11.hpp>
0017 #include <boost/mp11/algorithm.hpp>
0018 
0019 namespace Acts {
0020 
0021 namespace {
0022 
0023 template <bool isBottomCandidate, bool interactionPointCut, bool sortedByR,
0024           bool experimentCuts>
0025 class Impl final : public DoubletSeedFinder {
0026  public:
0027   explicit Impl(const DerivedConfig& config) : m_cfg(config) {}
0028 
0029   const DerivedConfig& config() const override { return m_cfg; }
0030 
0031   /// Iterates over dublets and tests the compatibility by applying a series of
0032   /// cuts that can be tested with only two SPs.
0033   ///
0034   /// @param config Doublet cuts that define the compatibility of space points
0035   /// @param middleSp Space point candidate to be used as middle SP in a seed
0036   /// @param middleSpInfo Information about the middle space point
0037   /// @param candidateSps Range or subet of space points to be used as candidates
0038   ///   for middle SP in a seed. In case of `sortedByR` - an offset will be
0039   ///   applied based on the middle SP radius.
0040   /// @param compatibleDoublets Output container for compatible doublets
0041   template <typename CandidateSps>
0042   void createDoubletsImpl(const ConstSpacePointProxy2& middleSp,
0043                           const MiddleSpInfo& middleSpInfo,
0044                           CandidateSps& candidateSps,
0045                           DoubletsForMiddleSp& compatibleDoublets) const {
0046     const float impactMax =
0047         isBottomCandidate ? -m_cfg.impactMax : m_cfg.impactMax;
0048 
0049     const float xM = middleSp.xy()[0];
0050     const float yM = middleSp.xy()[1];
0051     const float zM = middleSp.zr()[0];
0052     const float rM = middleSp.zr()[1];
0053     const float varianceZM = middleSp.varianceZ();
0054     const float varianceRM = middleSp.varianceR();
0055 
0056     // equivalent to impactMax / (rM * rM);
0057     const float vIPAbs = impactMax * middleSpInfo.uIP2;
0058 
0059     const auto outsideRangeCheck = [](const float value, const float min,
0060                                       const float max) {
0061       // intentionally using `|` after profiling. faster due to better branch
0062       // prediction
0063       return static_cast<bool>(static_cast<int>(value < min) |
0064                                static_cast<int>(value > max));
0065     };
0066 
0067     const auto calculateError = [&](float varianceZO, float varianceRO,
0068                                     float iDeltaR2, float cotTheta) {
0069       return iDeltaR2 * ((varianceZM + varianceZO) +
0070                          (cotTheta * cotTheta) * (varianceRM + varianceRO));
0071     };
0072 
0073     if constexpr (sortedByR) {
0074       // find the first SP inside the radius region of interest and update
0075       // the iterator so we don't need to look at the other SPs again
0076       std::uint32_t offset = 0;
0077       for (ConstSpacePointProxy2 otherSp : candidateSps) {
0078         if constexpr (isBottomCandidate) {
0079           // if r-distance is too big, try next SP in bin
0080           if (rM - otherSp.zr()[1] <= m_cfg.deltaRMax) {
0081             break;
0082           }
0083         } else {
0084           // if r-distance is too small, try next SP in bin
0085           if (otherSp.zr()[1] - rM >= m_cfg.deltaRMin) {
0086             break;
0087           }
0088         }
0089 
0090         ++offset;
0091       }
0092       candidateSps = candidateSps.subrange(offset);
0093     }
0094 
0095     const SpacePointContainer2& container = candidateSps.container();
0096     for (auto [indexO, xyO, zrO, varianceZO, varianceRO] : candidateSps.zip(
0097              container.xyColumn(), container.zrColumn(),
0098              container.varianceZColumn(), container.varianceRColumn())) {
0099       const float xO = xyO[0];
0100       const float yO = xyO[1];
0101       const float zO = zrO[0];
0102       const float rO = zrO[1];
0103 
0104       float deltaR = 0;
0105       if constexpr (isBottomCandidate) {
0106         deltaR = rM - rO;
0107 
0108         if constexpr (sortedByR) {
0109           // if r-distance is too small we are done
0110           if (deltaR < m_cfg.deltaRMin) {
0111             break;
0112           }
0113         }
0114       } else {
0115         deltaR = rO - rM;
0116 
0117         if constexpr (sortedByR) {
0118           // if r-distance is too big we are done
0119           if (deltaR > m_cfg.deltaRMax) {
0120             break;
0121           }
0122         }
0123       }
0124 
0125       if constexpr (!sortedByR) {
0126         if (outsideRangeCheck(deltaR, m_cfg.deltaRMin, m_cfg.deltaRMax)) {
0127           continue;
0128         }
0129       }
0130 
0131       float deltaZ = 0;
0132       if constexpr (isBottomCandidate) {
0133         deltaZ = zM - zO;
0134       } else {
0135         deltaZ = zO - zM;
0136       }
0137 
0138       if (outsideRangeCheck(deltaZ, m_cfg.deltaZMin, m_cfg.deltaZMax)) {
0139         continue;
0140       }
0141 
0142       // the longitudinal impact parameter zOrigin is defined as (zM - rM *
0143       // cotTheta) where cotTheta is the ratio Z/R (forward angle) of space
0144       // point duplet but instead we calculate (zOrigin * deltaR) and multiply
0145       // collisionRegion by deltaR to avoid divisions
0146       const float zOriginTimesDeltaR = zM * deltaR - rM * deltaZ;
0147       // check if duplet origin on z axis within collision region
0148       if (outsideRangeCheck(zOriginTimesDeltaR,
0149                             m_cfg.collisionRegionMin * deltaR,
0150                             m_cfg.collisionRegionMax * deltaR)) {
0151         continue;
0152       }
0153 
0154       // if interactionPointCut is false we apply z cuts before coordinate
0155       // transformation to avoid unnecessary calculations. If
0156       // interactionPointCut is true we apply the curvature cut first because it
0157       // is more frequent but requires the coordinate transformation
0158       if constexpr (!interactionPointCut) {
0159         // check if duplet cotTheta is within the region of interest
0160         // cotTheta is defined as (deltaZ / deltaR) but instead we multiply
0161         // cotThetaMax by deltaR to avoid division
0162         if (outsideRangeCheck(deltaZ, -m_cfg.cotThetaMax * deltaR,
0163                               m_cfg.cotThetaMax * deltaR)) {
0164           continue;
0165         }
0166 
0167         // transform SP coordinates to the u-v reference frame
0168         const float deltaX = xO - xM;
0169         const float deltaY = yO - yM;
0170 
0171         const float xNewFrame =
0172             deltaX * middleSpInfo.cosPhiM + deltaY * middleSpInfo.sinPhiM;
0173         const float yNewFrame =
0174             deltaY * middleSpInfo.cosPhiM - deltaX * middleSpInfo.sinPhiM;
0175 
0176         const float deltaR2 = deltaX * deltaX + deltaY * deltaY;
0177         const float iDeltaR2 = 1 / deltaR2;
0178 
0179         const float uT = xNewFrame * iDeltaR2;
0180         const float vT = yNewFrame * iDeltaR2;
0181 
0182         const float iDeltaR = std::sqrt(iDeltaR2);
0183         const float cotTheta = deltaZ * iDeltaR;
0184 
0185         const float er =
0186             calculateError(varianceZO, varianceRO, iDeltaR2, cotTheta);
0187 
0188         // fill output vectors
0189         compatibleDoublets.emplace_back(indexO, cotTheta, iDeltaR, er, uT, vT,
0190                                         xNewFrame, yNewFrame);
0191         continue;
0192       }
0193 
0194       // transform SP coordinates to the u-v reference frame
0195       const float deltaX = xO - xM;
0196       const float deltaY = yO - yM;
0197 
0198       const float xNewFrame =
0199           deltaX * middleSpInfo.cosPhiM + deltaY * middleSpInfo.sinPhiM;
0200       const float yNewFrame =
0201           deltaY * middleSpInfo.cosPhiM - deltaX * middleSpInfo.sinPhiM;
0202 
0203       const float deltaR2 = deltaX * deltaX + deltaY * deltaY;
0204       const float iDeltaR2 = 1 / deltaR2;
0205 
0206       const float uT = xNewFrame * iDeltaR2;
0207       const float vT = yNewFrame * iDeltaR2;
0208 
0209       // We check the interaction point by evaluating the minimal distance
0210       // between the origin and the straight line connecting the two points in
0211       // the doublets. Using a geometric similarity, the Im is given by
0212       // yNewFrame * rM / deltaR > config.impactMax
0213       // However, we make here an approximation of the impact parameter
0214       // which is valid under the assumption yNewFrame / xNewFrame is small
0215       // The correct computation would be:
0216       // yNewFrame * yNewFrame * rM * rM > config.impactMax *
0217       // config.impactMax * deltaR2
0218       if (std::abs(rM * yNewFrame) > impactMax * xNewFrame) {
0219         // in the rotated frame the interaction point is positioned at x = -rM
0220         // and y ~= impactParam
0221         const float vIP = (yNewFrame > 0) ? -vIPAbs : vIPAbs;
0222 
0223         // we can obtain aCoef as the slope dv/du of the linear function,
0224         // estimated using du and dv between the two SP bCoef is obtained by
0225         // inserting aCoef into the linear equation
0226         const float aCoef = (vT - vIP) / (uT - middleSpInfo.uIP);
0227         const float bCoef = vIP - aCoef * middleSpInfo.uIP;
0228         // the distance of the straight line from the origin (radius of the
0229         // circle) is related to aCoef and bCoef by d^2 = bCoef^2 / (1 +
0230         // aCoef^2) = 1 / (radius^2) and we can apply the cut on the curvature
0231         if ((bCoef * bCoef) * m_cfg.minHelixDiameter2 > 1 + aCoef * aCoef) {
0232           continue;
0233         }
0234       }
0235 
0236       // check if duplet cotTheta is within the region of interest
0237       // cotTheta is defined as (deltaZ / deltaR) but instead we multiply
0238       // cotThetaMax by deltaR to avoid division
0239       if (outsideRangeCheck(deltaZ, -m_cfg.cotThetaMax * deltaR,
0240                             m_cfg.cotThetaMax * deltaR)) {
0241         continue;
0242       }
0243 
0244       const float iDeltaR = std::sqrt(iDeltaR2);
0245       const float cotTheta = deltaZ * iDeltaR;
0246 
0247       // discard doublets based on experiment specific cuts
0248       if constexpr (experimentCuts) {
0249         if (!m_cfg.experimentCuts(middleSp, container[indexO], cotTheta,
0250                                   isBottomCandidate)) {
0251           continue;
0252         }
0253       }
0254 
0255       const float er =
0256           calculateError(varianceZO, varianceRO, iDeltaR2, cotTheta);
0257 
0258       // fill output vectors
0259       compatibleDoublets.emplace_back(indexO, cotTheta, iDeltaR, er, uT, vT,
0260                                       xNewFrame, yNewFrame);
0261     }
0262   }
0263 
0264   void createDoublets(const ConstSpacePointProxy2& middleSp,
0265                       const MiddleSpInfo& middleSpInfo,
0266                       SpacePointContainer2::ConstSubset& candidateSps,
0267                       DoubletsForMiddleSp& compatibleDoublets) const override {
0268     createDoubletsImpl(middleSp, middleSpInfo, candidateSps,
0269                        compatibleDoublets);
0270   }
0271 
0272   void createDoublets(const ConstSpacePointProxy2& middleSp,
0273                       const MiddleSpInfo& middleSpInfo,
0274                       SpacePointContainer2::ConstRange& candidateSps,
0275                       DoubletsForMiddleSp& compatibleDoublets) const override {
0276     createDoubletsImpl(middleSp, middleSpInfo, candidateSps,
0277                        compatibleDoublets);
0278   }
0279 
0280  private:
0281   DerivedConfig m_cfg;
0282 };
0283 
0284 }  // namespace
0285 
0286 std::unique_ptr<DoubletSeedFinder> DoubletSeedFinder::create(
0287     const DerivedConfig& config) {
0288   using BooleanOptions =
0289       boost::mp11::mp_list<std::bool_constant<false>, std::bool_constant<true>>;
0290 
0291   using IsBottomCandidateOptions = BooleanOptions;
0292   using InteractionPointCutOptions = BooleanOptions;
0293   using SortedByROptions = BooleanOptions;
0294   using ExperimentCutsOptions = BooleanOptions;
0295 
0296   using DoubletOptions =
0297       boost::mp11::mp_product<boost::mp11::mp_list, IsBottomCandidateOptions,
0298                               InteractionPointCutOptions, SortedByROptions,
0299                               ExperimentCutsOptions>;
0300 
0301   std::unique_ptr<DoubletSeedFinder> result;
0302   boost::mp11::mp_for_each<DoubletOptions>([&](auto option) {
0303     using OptionType = decltype(option);
0304 
0305     using IsBottomCandidate = boost::mp11::mp_at_c<OptionType, 0>;
0306     using InteractionPointCut = boost::mp11::mp_at_c<OptionType, 1>;
0307     using SortedByR = boost::mp11::mp_at_c<OptionType, 2>;
0308     using ExperimentCuts = boost::mp11::mp_at_c<OptionType, 3>;
0309 
0310     const bool configIsBottomCandidate =
0311         config.candidateDirection == Direction::Backward();
0312 
0313     if (configIsBottomCandidate != IsBottomCandidate::value ||
0314         config.interactionPointCut != InteractionPointCut::value ||
0315         config.spacePointsSortedByRadius != SortedByR::value ||
0316         config.experimentCuts.connected() != ExperimentCuts::value) {
0317       return;  // skip if the configuration does not match
0318     }
0319 
0320     // check if we already have an implementation for this configuration
0321     if (result != nullptr) {
0322       throw std::runtime_error(
0323           "DoubletSeedFinder: Multiple implementations found for one "
0324           "configuration");
0325     }
0326 
0327     // create the implementation for the given configuration
0328     result = std::make_unique<
0329         Impl<IsBottomCandidate::value, InteractionPointCut::value,
0330              SortedByR::value, ExperimentCuts::value>>(config);
0331   });
0332   if (result == nullptr) {
0333     throw std::runtime_error(
0334         "DoubletSeedFinder: No implementation found for the given "
0335         "configuration");
0336   }
0337   return result;
0338 }
0339 
0340 DoubletSeedFinder::DerivedConfig::DerivedConfig(const Config& config,
0341                                                 float bFieldInZ_)
0342     : Config(config), bFieldInZ(bFieldInZ_) {
0343   // bFieldInZ is in (pT/radius) natively, no need for conversion
0344   const float pTPerHelixRadius = bFieldInZ;
0345   minHelixDiameter2 = square(minPt * 2 / pTPerHelixRadius) * helixCutTolerance;
0346 }
0347 
0348 MiddleSpInfo DoubletSeedFinder::computeMiddleSpInfo(
0349     const ConstSpacePointProxy2& spM) {
0350   const float rM = spM.zr()[1];
0351   const float uIP = -1 / rM;
0352   const float cosPhiM = -spM.xy()[0] * uIP;
0353   const float sinPhiM = -spM.xy()[1] * uIP;
0354   const float uIP2 = uIP * uIP;
0355 
0356   return {uIP, uIP2, cosPhiM, sinPhiM};
0357 }
0358 
0359 }  // namespace Acts