Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-13 07:50:40

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/Definitions/Direction.hpp"
0012 #include "Acts/EventData/SpacePointContainer2.hpp"
0013 #include "Acts/Utilities/MathHelpers.hpp"
0014 
0015 #include <stdexcept>
0016 
0017 namespace Acts::Experimental {
0018 
0019 namespace {
0020 
0021 enum class SpacePointCandidateType { Bottom, Top };
0022 
0023 /// Iterates over dublets and tests the compatibility by applying a series of
0024 /// cuts that can be tested with only two SPs.
0025 ///
0026 /// @tparam candidate_type Type of space point candidate (e.g. Bottom or Top)
0027 /// @tparam interaction_point_cut Whether to apply the interaction point cut
0028 /// @tparam sorted_in_r Whether the space points are sorted in radius
0029 ///
0030 /// @param config Doublet cuts that define the compatibility of space points
0031 /// @param spacePoints Space point container to be used
0032 /// @param middleSp Space point candidate to be used as middle SP in a seed
0033 /// @param middleSpInfo Information about the middle space point
0034 /// @param candidateSps Group of space points to be used as candidates for
0035 ///                     middle SP in a seed
0036 /// @param candidateOffset Offset in the candidateSps to start from
0037 /// @param compatibleDoublets Output container for compatible doublets
0038 template <SpacePointCandidateType candidateType, bool interactionPointCut,
0039           bool sortedInR>
0040 void createDoubletsImpl(
0041     const DoubletSeedFinder::DerivedConfig& config,
0042     const SpacePointContainer2& spacePoints,
0043     const ConstSpacePointProxy2& middleSp,
0044     const DoubletSeedFinder::MiddleSpInfo& middleSpInfo,
0045     std::span<const SpacePointIndex2> candidateSps,
0046     std::size_t& candidateOffset,
0047     DoubletSeedFinder::DoubletsForMiddleSp& compatibleDoublets) {
0048   constexpr bool isBottomCandidate =
0049       candidateType == SpacePointCandidateType::Bottom;
0050 
0051   const float impactMax =
0052       isBottomCandidate ? -config.impactMax : config.impactMax;
0053 
0054   const float xM = middleSp.x();
0055   const float yM = middleSp.y();
0056   const float zM = middleSp.z();
0057   const float rM = middleSp.r();
0058   const float varianceZM = middleSp.varianceZ();
0059   const float varianceRM = middleSp.varianceR();
0060 
0061   // equivalent to impactMax / (rM * rM);
0062   const float vIPAbs = impactMax * middleSpInfo.uIP2;
0063 
0064   float deltaR = 0.;
0065   float deltaZ = 0.;
0066 
0067   const auto outsideRangeCheck = [](const float value, const float min,
0068                                     const float max) {
0069     // intentionally using `|` after profiling. faster due to better branch
0070     // prediction
0071     return static_cast<bool>(static_cast<int>(value < min) |
0072                              static_cast<int>(value > max));
0073   };
0074 
0075   const auto calculateError = [&](const ConstSpacePointProxy2& otherSp,
0076                                   float iDeltaR2, float cotTheta) {
0077     const float varianceZO = otherSp.varianceZ();
0078     const float varianceRO = otherSp.varianceR();
0079 
0080     return iDeltaR2 * ((varianceZM + varianceZO) +
0081                        (cotTheta * cotTheta) * (varianceRM + varianceRO));
0082   };
0083 
0084   if constexpr (sortedInR) {
0085     // find the first SP inside the radius region of interest and update
0086     // the iterator so we don't need to look at the other SPs again
0087     for (; candidateOffset < candidateSps.size(); ++candidateOffset) {
0088       ConstSpacePointProxy2 otherSp =
0089           spacePoints[candidateSps[candidateOffset]];
0090 
0091       if constexpr (isBottomCandidate) {
0092         // if r-distance is too big, try next SP in bin
0093         if (rM - otherSp.r() <= config.deltaRMax) {
0094           break;
0095         }
0096       } else {
0097         // if r-distance is too small, try next SP in bin
0098         if (otherSp.r() - rM >= config.deltaRMin) {
0099           break;
0100         }
0101       }
0102     }
0103   }
0104 
0105   for (SpacePointIndex2 otherSpIndex : candidateSps.subspan(candidateOffset)) {
0106     ConstSpacePointProxy2 otherSp = spacePoints[otherSpIndex];
0107 
0108     const float xO = otherSp.x();
0109     const float yO = otherSp.y();
0110     const float zO = otherSp.z();
0111     const float rO = otherSp.r();
0112 
0113     if constexpr (isBottomCandidate) {
0114       deltaR = rM - rO;
0115 
0116       if constexpr (sortedInR) {
0117         // if r-distance is too small we are done
0118         if (deltaR < config.deltaRMin) {
0119           break;
0120         }
0121       }
0122     } else {
0123       deltaR = rO - rM;
0124 
0125       if constexpr (sortedInR) {
0126         // if r-distance is too big we are done
0127         if (deltaR > config.deltaRMax) {
0128           break;
0129         }
0130       }
0131     }
0132 
0133     if constexpr (!sortedInR) {
0134       if (outsideRangeCheck(deltaR, config.deltaRMin, config.deltaRMax)) {
0135         continue;
0136       }
0137     }
0138 
0139     if constexpr (isBottomCandidate) {
0140       deltaZ = zM - zO;
0141     } else {
0142       deltaZ = zO - zM;
0143     }
0144 
0145     if (outsideRangeCheck(deltaZ, config.deltaZMin, config.deltaZMax)) {
0146       continue;
0147     }
0148 
0149     // the longitudinal impact parameter zOrigin is defined as (zM - rM *
0150     // cotTheta) where cotTheta is the ratio Z/R (forward angle) of space
0151     // point duplet but instead we calculate (zOrigin * deltaR) and multiply
0152     // collisionRegion by deltaR to avoid divisions
0153     const float zOriginTimesDeltaR = zM * deltaR - rM * deltaZ;
0154     // check if duplet origin on z axis within collision region
0155     if (outsideRangeCheck(zOriginTimesDeltaR,
0156                           config.collisionRegionMin * deltaR,
0157                           config.collisionRegionMax * deltaR)) {
0158       continue;
0159     }
0160 
0161     // if interactionPointCut is false we apply z cuts before coordinate
0162     // transformation to avoid unnecessary calculations. If
0163     // interactionPointCut is true we apply the curvature cut first because it
0164     // is more frequent but requires the coordinate transformation
0165     if constexpr (!interactionPointCut) {
0166       // check if duplet cotTheta is within the region of interest
0167       // cotTheta is defined as (deltaZ / deltaR) but instead we multiply
0168       // cotThetaMax by deltaR to avoid division
0169       if (outsideRangeCheck(deltaZ, -config.cotThetaMax * deltaR,
0170                             config.cotThetaMax * deltaR)) {
0171         continue;
0172       }
0173 
0174       // transform SP coordinates to the u-v reference frame
0175       const float deltaX = xO - xM;
0176       const float deltaY = yO - yM;
0177 
0178       const float xNewFrame =
0179           deltaX * middleSpInfo.cosPhiM + deltaY * middleSpInfo.sinPhiM;
0180       const float yNewFrame =
0181           deltaY * middleSpInfo.cosPhiM - deltaX * middleSpInfo.sinPhiM;
0182 
0183       const float deltaR2 = deltaX * deltaX + deltaY * deltaY;
0184       const float iDeltaR2 = 1 / deltaR2;
0185 
0186       const float uT = xNewFrame * iDeltaR2;
0187       const float vT = yNewFrame * iDeltaR2;
0188 
0189       const float iDeltaR = std::sqrt(iDeltaR2);
0190       const float cotTheta = deltaZ * iDeltaR;
0191 
0192       const float er = calculateError(otherSp, iDeltaR2, cotTheta);
0193 
0194       // fill output vectors
0195       compatibleDoublets.emplace_back(
0196           otherSp.index(),
0197           {cotTheta, iDeltaR, er, uT, vT, xNewFrame, yNewFrame});
0198       continue;
0199     }
0200 
0201     // transform SP coordinates to the u-v reference frame
0202     const float deltaX = xO - xM;
0203     const float deltaY = yO - yM;
0204 
0205     const float xNewFrame =
0206         deltaX * middleSpInfo.cosPhiM + deltaY * middleSpInfo.sinPhiM;
0207     const float yNewFrame =
0208         deltaY * middleSpInfo.cosPhiM - deltaX * middleSpInfo.sinPhiM;
0209 
0210     const float deltaR2 = deltaX * deltaX + deltaY * deltaY;
0211     const float iDeltaR2 = 1 / deltaR2;
0212 
0213     const float uT = xNewFrame * iDeltaR2;
0214     const float vT = yNewFrame * iDeltaR2;
0215 
0216     // We check the interaction point by evaluating the minimal distance
0217     // between the origin and the straight line connecting the two points in
0218     // the doublets. Using a geometric similarity, the Im is given by
0219     // yNewFrame * rM / deltaR <= config.impactMax
0220     // However, we make here an approximation of the impact parameter
0221     // which is valid under the assumption yNewFrame / xNewFrame is small
0222     // The correct computation would be:
0223     // yNewFrame * yNewFrame * rM * rM <= config.impactMax *
0224     // config.impactMax * deltaR2
0225     if (std::abs(rM * yNewFrame) <= impactMax * xNewFrame) {
0226       // check if duplet cotTheta is within the region of interest
0227       // cotTheta is defined as (deltaZ / deltaR) but instead we multiply
0228       // cotThetaMax by deltaR to avoid division
0229       if (outsideRangeCheck(deltaZ, -config.cotThetaMax * deltaR,
0230                             config.cotThetaMax * deltaR)) {
0231         continue;
0232       }
0233 
0234       const float iDeltaR = std::sqrt(iDeltaR2);
0235       const float cotTheta = deltaZ * iDeltaR;
0236 
0237       // discard bottom-middle doublets in a certain (r, eta) region according
0238       // to detector specific cuts
0239       if constexpr (isBottomCandidate) {
0240         if (config.experimentCuts.connected() &&
0241             !config.experimentCuts(rO, cotTheta)) {
0242           continue;
0243         }
0244       }
0245 
0246       const float er = calculateError(otherSp, iDeltaR2, cotTheta);
0247 
0248       // fill output vectors
0249       compatibleDoublets.emplace_back(
0250           otherSp.index(),
0251           {cotTheta, iDeltaR, er, uT, vT, xNewFrame, yNewFrame});
0252       continue;
0253     }
0254 
0255     // in the rotated frame the interaction point is positioned at x = -rM
0256     // and y ~= impactParam
0257     const float vIP = (yNewFrame > 0) ? -vIPAbs : vIPAbs;
0258 
0259     // we can obtain aCoef as the slope dv/du of the linear function,
0260     // estimated using du and dv between the two SP bCoef is obtained by
0261     // inserting aCoef into the linear equation
0262     const float aCoef = (vT - vIP) / (uT - middleSpInfo.uIP);
0263     const float bCoef = vIP - aCoef * middleSpInfo.uIP;
0264     // the distance of the straight line from the origin (radius of the
0265     // circle) is related to aCoef and bCoef by d^2 = bCoef^2 / (1 +
0266     // aCoef^2) = 1 / (radius^2) and we can apply the cut on the curvature
0267     if ((bCoef * bCoef) * config.minHelixDiameter2 > 1 + aCoef * aCoef) {
0268       continue;
0269     }
0270 
0271     // check if duplet cotTheta is within the region of interest
0272     // cotTheta is defined as (deltaZ / deltaR) but instead we multiply
0273     // cotThetaMax by deltaR to avoid division
0274     if (outsideRangeCheck(deltaZ, -config.cotThetaMax * deltaR,
0275                           config.cotThetaMax * deltaR)) {
0276       continue;
0277     }
0278 
0279     const float iDeltaR = std::sqrt(iDeltaR2);
0280     const float cotTheta = deltaZ * iDeltaR;
0281 
0282     // discard bottom-middle doublets in a certain (r, eta) region according
0283     // to detector specific cuts
0284     if constexpr (isBottomCandidate) {
0285       if (config.experimentCuts.connected() &&
0286           !config.experimentCuts(rO, cotTheta)) {
0287         continue;
0288       }
0289     }
0290 
0291     const float er = calculateError(otherSp, iDeltaR2, cotTheta);
0292 
0293     // fill output vectors
0294     compatibleDoublets.emplace_back(
0295         otherSp.index(), {cotTheta, iDeltaR, er, uT, vT, xNewFrame, yNewFrame});
0296   }
0297 }
0298 
0299 }  // namespace
0300 
0301 DoubletSeedFinder::DerivedConfig::DerivedConfig(const Config& cfg,
0302                                                 float bFieldInZ_)
0303     : Config(cfg), bFieldInZ(bFieldInZ_) {
0304   // bFieldInZ is in (pT/radius) natively, no need for conversion
0305   const float pTPerHelixRadius = bFieldInZ;
0306   minHelixDiameter2 = square(minPt * 2 / pTPerHelixRadius) * helixCutTolerance;
0307 }
0308 
0309 DoubletSeedFinder::MiddleSpInfo DoubletSeedFinder::computeMiddleSpInfo(
0310     const ConstSpacePointProxy2& spM) {
0311   const float rM = spM.r();
0312   const float uIP = -1 / rM;
0313   const float cosPhiM = -spM.x() * uIP;
0314   const float sinPhiM = -spM.y() * uIP;
0315   const float uIP2 = uIP * uIP;
0316 
0317   return {uIP, uIP2, cosPhiM, sinPhiM};
0318 }
0319 
0320 DoubletSeedFinder::DoubletSeedFinder(const DerivedConfig& cfg) : m_cfg(cfg) {}
0321 
0322 void DoubletSeedFinder::createDoublets(
0323     const SpacePointContainer2& spacePoints,
0324     const ConstSpacePointProxy2& middleSp, const MiddleSpInfo& middleSpInfo,
0325     std::span<const SpacePointIndex2> candidateSps,
0326     DoubletsForMiddleSp& compatibleDoublets) const {
0327   std::size_t candidateOffset = 0;
0328 
0329   if (m_cfg.candidateDirection == Direction::Backward() &&
0330       m_cfg.interactionPointCut) {
0331     return createDoubletsImpl<SpacePointCandidateType::Bottom, true, false>(
0332         config(), spacePoints, middleSp, middleSpInfo, candidateSps,
0333         candidateOffset, compatibleDoublets);
0334   }
0335 
0336   if (m_cfg.candidateDirection == Direction::Backward() &&
0337       !m_cfg.interactionPointCut) {
0338     return createDoubletsImpl<SpacePointCandidateType::Bottom, false, false>(
0339         config(), spacePoints, middleSp, middleSpInfo, candidateSps,
0340         candidateOffset, compatibleDoublets);
0341   }
0342 
0343   if (m_cfg.candidateDirection == Direction::Forward() &&
0344       m_cfg.interactionPointCut) {
0345     return createDoubletsImpl<SpacePointCandidateType::Top, true, false>(
0346         config(), spacePoints, middleSp, middleSpInfo, candidateSps,
0347         candidateOffset, compatibleDoublets);
0348   }
0349 
0350   if (m_cfg.candidateDirection == Direction::Forward() &&
0351       !m_cfg.interactionPointCut) {
0352     return createDoubletsImpl<SpacePointCandidateType::Top, false, false>(
0353         config(), spacePoints, middleSp, middleSpInfo, candidateSps,
0354         candidateOffset, compatibleDoublets);
0355   }
0356 
0357   throw std::logic_error("DoubletSeedFinder: unhandled configuration");
0358 }
0359 
0360 void DoubletSeedFinder::createSortedDoublets(
0361     const SpacePointContainer2& spacePoints,
0362     const ConstSpacePointProxy2& middleSp, const MiddleSpInfo& middleSpInfo,
0363     std::span<const SpacePointIndex2> candidateSps,
0364     std::size_t& candidateOffset,
0365     DoubletsForMiddleSp& compatibleDoublets) const {
0366   if (m_cfg.candidateDirection == Direction::Backward() &&
0367       m_cfg.interactionPointCut) {
0368     return createDoubletsImpl<SpacePointCandidateType::Bottom, true, true>(
0369         config(), spacePoints, middleSp, middleSpInfo, candidateSps,
0370         candidateOffset, compatibleDoublets);
0371   }
0372 
0373   if (m_cfg.candidateDirection == Direction::Backward() &&
0374       !m_cfg.interactionPointCut) {
0375     return createDoubletsImpl<SpacePointCandidateType::Bottom, false, true>(
0376         config(), spacePoints, middleSp, middleSpInfo, candidateSps,
0377         candidateOffset, compatibleDoublets);
0378   }
0379 
0380   if (m_cfg.candidateDirection == Direction::Forward() &&
0381       m_cfg.interactionPointCut) {
0382     return createDoubletsImpl<SpacePointCandidateType::Top, true, true>(
0383         config(), spacePoints, middleSp, middleSpInfo, candidateSps,
0384         candidateOffset, compatibleDoublets);
0385   }
0386 
0387   if (m_cfg.candidateDirection == Direction::Forward() &&
0388       !m_cfg.interactionPointCut) {
0389     return createDoubletsImpl<SpacePointCandidateType::Top, false, true>(
0390         config(), spacePoints, middleSp, middleSpInfo, candidateSps,
0391         candidateOffset, compatibleDoublets);
0392   }
0393 
0394   throw std::logic_error("DoubletSeedFinder: unhandled configuration");
0395 }
0396 
0397 }  // namespace Acts::Experimental