Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2026-07-16 08:07:36

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/Seeding/CompositeSpacePointLineSeeder.hpp"
0012 
0013 #include "Acts/Definitions/Tolerance.hpp"
0014 #include "Acts/Surfaces/detail/LineHelper.hpp"
0015 #include "Acts/Utilities/Helpers.hpp"
0016 
0017 #include <algorithm>
0018 #include <cassert>
0019 
0020 namespace Acts::Experimental {
0021 
0022 template <CompositeSpacePoint Sp_t>
0023 CompositeSpacePointLineSeeder::TwoCircleTangentPars
0024 CompositeSpacePointLineSeeder::constructTangentLine(const Sp_t& topHit,
0025                                                     const Sp_t& bottomHit,
0026                                                     const TangentAmbi ambi) {
0027   using namespace Acts::UnitLiterals;
0028   using namespace Acts::detail;
0029   TwoCircleTangentPars result{};
0030   result.ambi = ambi;
0031   const auto& [signTop, signBot] = s_signCombo[toUnderlying(ambi)];
0032 
0033   const Vector& bottomPos{bottomHit.localPosition()};
0034   const Vector& topPos{topHit.localPosition()};
0035   const Vector& eY{bottomHit.toNextSensor()};
0036   const Vector& eZ{bottomHit.planeNormal()};
0037   const Vector D = topPos - bottomPos;
0038 
0039   assert(Acts::abs(eY.dot(eZ)) < s_epsilon);
0040   assert(Acts::abs(bottomHit.sensorDirection().dot(eY)) < s_epsilon);
0041   assert(Acts::abs(bottomHit.sensorDirection().dot(eZ)) < s_epsilon);
0042   assert(topHit.isStraw() && bottomHit.isStraw());
0043 
0044   const double dY = D.dot(eY);
0045   const double dZ = D.dot(eZ);
0046 
0047   const double thetaTubes = std::atan2(dY, dZ);
0048   const double distTubes = fastHypot(dY, dZ);
0049   assert(distTubes > 1._mm);
0050   constexpr auto covIdx = toUnderlying(CovIdx::bending);
0051   const double combDriftUncert{topHit.covariance()[covIdx] +
0052                                bottomHit.covariance()[covIdx]};
0053   const double R =
0054       -signBot * bottomHit.driftRadius() + signTop * topHit.driftRadius();
0055   result.theta = thetaTubes - std::asin(std::clamp(R / distTubes, -1., 1.));
0056 
0057   const double cosTheta = std::cos(result.theta);
0058   const double sinTheta = std::sin(result.theta);
0059 
0060   result.y0 = bottomPos.dot(eY) * cosTheta - bottomPos.dot(eZ) * sinTheta -
0061               signBot * bottomHit.driftRadius();
0062   assert(Acts::abs(topPos.dot(eY) * cosTheta - topPos.dot(eZ) * sinTheta -
0063                    signTop * topHit.driftRadius() - result.y0) <
0064          std::numeric_limits<float>::epsilon());
0065   result.y0 /= cosTheta;
0066   const double denomSquare = 1. - Acts::pow(R / distTubes, 2);
0067   if (denomSquare < s_epsilon) {
0068     return result;
0069   }
0070   result.dTheta = combDriftUncert / std::sqrt(denomSquare) / distTubes;
0071   result.dY0 =
0072       Acts::fastHypot(
0073           bottomPos.dot(eY) * sinTheta + bottomPos.dot(eZ) * cosTheta, 1.) *
0074       result.dTheta;
0075 
0076   return result;
0077 }
0078 
0079 template <CompositeSpacePoint Sp_t>
0080 CompositeSpacePointLineSeeder::Vector
0081 CompositeSpacePointLineSeeder::makeDirection(const Sp_t& refHit,
0082                                              const double tanAngle) {
0083   const Vector& eY{refHit.toNextSensor()};
0084   const Vector& eZ{refHit.planeNormal()};
0085   const double cosTheta = std::cos(tanAngle);
0086   const double sinTheta = std::sin(tanAngle);
0087   return copySign<Vector, double>(sinTheta * eY + cosTheta * eZ, sinTheta);
0088 }
0089 
0090 template <CompositeSpacePointContainer Cont_t>
0091 std::size_t CompositeSpacePointLineSeeder::countHits(
0092     const Cont_t& container, const Selector_t<Cont_t>& selector) const {
0093   if (m_cfg.busyLimitCountGood) {
0094     return std::ranges::count_if(
0095         container, [&](const auto& hit) { return selector(*hit); });
0096   }
0097   return container.size();
0098 }
0099 
0100 /// ##########################################################################
0101 ///                CompositeSpacePointLineSeeder::SeedSolution
0102 /// ##########################################################################
0103 template <CompositeSpacePointContainer UnCalibCont_t,
0104           detail::CompositeSpacePointSorter<UnCalibCont_t> Splitter_t>
0105 CompositeSpacePointLineSeeder::SeedSolution<UnCalibCont_t,
0106                                             Splitter_t>::SpacePoint_t
0107 CompositeSpacePointLineSeeder::SeedSolution<UnCalibCont_t, Splitter_t>::getHit(
0108     const std::size_t idx) const {
0109   const auto& [layIdx, hitIdx] = m_seedHits.at(idx);
0110   const UnCalibCont_t& strawLayer = m_splitter.strawHits().at(layIdx);
0111   return *strawLayer.at(hitIdx);
0112 }
0113 template <CompositeSpacePointContainer UnCalibCont_t,
0114           detail::CompositeSpacePointSorter<UnCalibCont_t> Splitter_t>
0115 std::vector<int> CompositeSpacePointLineSeeder::SeedSolution<
0116     UnCalibCont_t, Splitter_t>::leftRightAmbiguity(const Vector& seedPos,
0117                                                    const Vector& seedDir)
0118     const {
0119   std::vector<int> result{};
0120   result.reserve(size());
0121   std::ranges::transform(
0122       m_seedHits, std::back_inserter(result),
0123       [&](const std::pair<std::size_t, std::size_t>& indices) {
0124         const auto& [layIdx, hitIdx] = indices;
0125         const UnCalibCont_t& strawLayer = m_splitter.strawHits().at(layIdx);
0126         return detail::CompSpacePointAuxiliaries::strawSign(
0127             seedPos, seedDir, *strawLayer.at(hitIdx));
0128       });
0129   return result;
0130 }
0131 template <CompositeSpacePointContainer UnCalibCont_t,
0132           detail::CompositeSpacePointSorter<UnCalibCont_t> Splitter_t>
0133 void CompositeSpacePointLineSeeder::SeedSolution<
0134     UnCalibCont_t, Splitter_t>::append(const std::size_t layIdx,
0135                                        const std::size_t hitIdx) {
0136   assert(!rangeContainsValue(m_seedHits, std::make_pair(layIdx, hitIdx)));
0137   m_seedHits.emplace_back(layIdx, hitIdx);
0138 }
0139 template <CompositeSpacePointContainer UnCalibCont_t,
0140           detail::CompositeSpacePointSorter<UnCalibCont_t> Splitter_t>
0141 void CompositeSpacePointLineSeeder::SeedSolution<
0142     UnCalibCont_t, Splitter_t>::print(std::ostream& ostr) const {
0143   TwoCircleTangentPars::print(ostr);
0144   const std::size_t N = size();
0145   ostr << ", associated hits: " << N << std::endl;
0146   for (std::size_t h = 0; h < N; ++h) {
0147     ostr << "    **** " << (h + 1ul) << ") " << Acts::toString(getHit(h))
0148          << std::endl;
0149   }
0150 }
0151 
0152 /// ##########################################################################
0153 ///                CompositeSpacePointLineSeeder::SeedingState
0154 /// ##########################################################################
0155 template <
0156     CompositeSpacePointContainer UncalibCont_t,
0157     CompositeSpacePointContainer CalibCont_t,
0158     detail::CompSpacePointSeederDelegate<UncalibCont_t, CalibCont_t> Delegate_t>
0159 void CompositeSpacePointLineSeeder::SeedingState<
0160     UncalibCont_t, CalibCont_t, Delegate_t>::print(std::ostream& ostr) const {
0161   const std::size_t nStraw = this->strawHits().size();
0162 
0163   ostr << "Seed state:\n";
0164   ostr << "N strawLayers: " << nStraw
0165        << " N strip layers: " << this->stripHits().size() << "\n";
0166   ostr << "upperLayer " << m_upperLayer.value_or(nStraw - 1ul) << " lowerLayer "
0167        << m_lowerLayer.value_or(0u) << " upperHitIndex " << m_upperHitIndex
0168        << " lower layer hit index " << m_lowerHitIndex << " sign combo index "
0169        << toString(encodeAmbiguity(s_signCombo[m_signComboIndex][0],
0170                                    s_signCombo[m_signComboIndex][1]))
0171        << "\n";
0172   ostr << "Number of seeds " << nGenSeeds() << " nStrawCut " << m_nStrawCut
0173        << "\n";
0174   if (nGenSeeds() > 0ul) {
0175     for (const auto& seen : m_seenSolutions) {
0176       ostr << "################################################" << std::endl;
0177       ostr << seen << std::endl;
0178       ostr << "################################################" << std::endl;
0179     }
0180   }
0181 }
0182 
0183 template <CompositeSpacePointContainer UnCalibCont_t>
0184 bool CompositeSpacePointLineSeeder::moveToNextHit(
0185     const UnCalibCont_t& hitVec, const Selector_t<UnCalibCont_t>& selector,
0186     std::size_t& hitIdx) const {
0187   ACTS_VERBOSE(__func__ << "() " << __LINE__
0188                         << " - Moving to next good hit from index " << hitIdx
0189                         << " in straw layer with " << hitVec.size()
0190                         << " hits.");
0191   while (++hitIdx < hitVec.size()) {
0192     if (selector(*hitVec[hitIdx])) {
0193       ACTS_VERBOSE(__func__ << "() " << __LINE__ << " - Moved towards index "
0194                             << hitIdx);
0195       return true;
0196     }
0197   }
0198   ACTS_VERBOSE(__func__ << "() " << __LINE__ << " - No good hit found.");
0199   return false;
0200 }
0201 
0202 template <CompositeSpacePointContainer UnCalibCont_t>
0203 bool CompositeSpacePointLineSeeder::firstGoodHit(
0204     const UnCalibCont_t& hitVec, const Selector_t<UnCalibCont_t>& selector,
0205     std::size_t& hitIdx) const {
0206   hitIdx = 0;
0207   if (hitVec.empty()) {
0208     ACTS_VERBOSE(__func__ << "() " << __LINE__ << " - Layer is empty.");
0209     return false;
0210   }
0211   return selector(*hitVec[hitIdx]) || moveToNextHit(hitVec, selector, hitIdx);
0212 }
0213 template <CompositeSpacePointContainer UnCalibCont_t>
0214 bool CompositeSpacePointLineSeeder::nextLayer(
0215     const StrawLayers_t<UnCalibCont_t>& strawLayers,
0216     const Selector_t<UnCalibCont_t>& selector, const std::size_t boundary,
0217     std::optional<std::size_t>& layerIndex, std::size_t& hitIdx,
0218     bool moveForward) const {
0219   if (strawLayers.empty()) {
0220     ACTS_VERBOSE(__func__ << "() " << __LINE__ << " - No straw layers.");
0221     return false;
0222   }
0223   /// The layer index is not yet instantiated.
0224   if (!layerIndex.has_value()) {
0225     layerIndex = moveForward ? 0u : strawLayers.size() - 1u;
0226     const UnCalibCont_t& hitVec{strawLayers.at(layerIndex.value())};
0227     if (hitVec.size() <= m_cfg.busyLayerLimit &&
0228         firstGoodHit(hitVec, selector, hitIdx)) {
0229       ACTS_VERBOSE(__func__ << "() " << __LINE__ << " - Instantiated "
0230                             << (moveForward ? "lower" : "upper") << " layer to "
0231                             << layerIndex.value() << ".");
0232       return true;
0233     }
0234   }
0235   ACTS_VERBOSE(__func__ << "() " << __LINE__ << " - Move "
0236                         << (moveForward ? "lower" : "upper") << " layer "
0237                         << layerIndex.value() << " to next value.");
0238   /// Increment or decrement the layer index
0239   while ((moveForward ? (++layerIndex.value()) : (--layerIndex.value())) <
0240          strawLayers.size()) {
0241     const UnCalibCont_t& hitVec{strawLayers.at(layerIndex.value())};
0242     /// Check whether the layer index is still witihin the allow boundaries
0243     if ((moveForward && layerIndex.value() >= boundary) ||
0244         (!moveForward && layerIndex.value() <= boundary)) {
0245       ACTS_VERBOSE(__func__ << "() " << __LINE__ << " - The "
0246                             << (moveForward ? "lower" : "upper") << " index "
0247                             << layerIndex.value()
0248                             << " exceeds the boundary: " << boundary << ".");
0249       return false;
0250     }
0251 
0252     if (const std::size_t nHits = countHits(hitVec, selector);
0253         nHits > m_cfg.busyLayerLimit) {
0254       ACTS_VERBOSE(__func__ << "() " << __LINE__ << " - The layer "
0255                             << layerIndex.value()
0256                             << " is too busy for seeding: " << nHits
0257                             << ". Limit: " << m_cfg.busyLayerLimit << ".");
0258       continue;
0259     }
0260 
0261     /// Check whether a good hit can be detected inside the layer
0262     if (firstGoodHit(strawLayers.at(layerIndex.value()), selector, hitIdx)) {
0263       ACTS_VERBOSE(__func__
0264                    << "() " << __LINE__ << " - Loop over all hits in the  "
0265                    << layerIndex.value() << (moveForward ? "lower" : "upper")
0266                    << " layer.");
0267       return true;
0268     }
0269   }
0270   return false;
0271 }
0272 
0273 template <
0274     CompositeSpacePointContainer UncalibCont_t,
0275     CompositeSpacePointContainer CalibCont_t,
0276     detail::CompSpacePointSeederDelegate<UncalibCont_t, CalibCont_t> Delegate_t>
0277 std::optional<CompositeSpacePointLineSeeder::SegmentSeed<CalibCont_t>>
0278 CompositeSpacePointLineSeeder::nextSeed(
0279     const CalibrationContext& cctx,
0280     SeedingState<UncalibCont_t, CalibCont_t, Delegate_t>& state) const {
0281   const StrawLayers_t<UncalibCont_t>& strawLayers{state.strawHits()};
0282 
0283   Selector_t<UncalibCont_t> selector{};
0284   selector.template connect<&Delegate_t::goodCandidate>(&state);
0285   /// The layer hast not yet been initialized
0286   if (!state.m_upperLayer || !state.m_lowerLayer) {
0287     state.m_nStrawCut = m_cfg.nStrawHitCut;
0288     /// Check whether the seeding can start with the external pattern
0289     /// parameters
0290     if (!state.m_patternSeedProduced &&
0291         (!m_cfg.startWithPattern ||
0292          std::ranges::any_of(strawLayers, [&](const UncalibCont_t& layerHits) {
0293            return countHits(layerHits, selector) > m_cfg.busyLayerLimit;
0294          }))) {
0295       state.m_patternSeedProduced = true;
0296     }
0297     if (!state.m_patternSeedProduced) {
0298       SegmentSeed<CalibCont_t> patternSeed{state.initialParameters(),
0299                                            state.newContainer(cctx)};
0300 
0301       const auto [pos, dir] = makeLine(state.initialParameters());
0302       const double t0 = patternSeed.parameters[toUnderlying(ParIdx::t0)];
0303 
0304       auto append = [&](const StrawLayers_t<UncalibCont_t>& hitLayers) {
0305         for (const auto& layer : hitLayers) {
0306           for (const auto& hit : layer) {
0307             state.append(cctx, pos, dir, t0, *hit, patternSeed.hits);
0308           }
0309         }
0310       };
0311       append(strawLayers);
0312       append(state.stripHits());
0313       state.m_patternSeedProduced = true;
0314       return patternSeed;
0315     }
0316     ACTS_DEBUG(__func__ << "() " << __LINE__ << " - Instantiate layers. ");
0317     /// No valid seed can be found
0318     if (!nextLayer(strawLayers, selector, strawLayers.size(),
0319                    state.m_lowerLayer, state.m_lowerHitIndex, true) ||
0320         !nextLayer(strawLayers, selector, state.m_lowerLayer.value(),
0321                    state.m_upperLayer, state.m_upperHitIndex, false)) {
0322       ACTS_DEBUG(__func__ << "() " << __LINE__
0323                           << " - No valid seed can be constructed. ");
0324       return std::nullopt;
0325     }
0326   }
0327 
0328   while (state.m_lowerLayer.value() < state.m_upperLayer.value()) {
0329     auto seed = buildSeed(cctx, selector, state);
0330     moveToNextCandidate(selector, state);
0331     if (seed) {
0332       return seed;
0333     }
0334   }
0335 
0336   return std::nullopt;
0337 }
0338 template <
0339     CompositeSpacePointContainer UncalibCont_t,
0340     CompositeSpacePointContainer CalibCont_t,
0341     detail::CompSpacePointSeederDelegate<UncalibCont_t, CalibCont_t> Delegate_t>
0342 void CompositeSpacePointLineSeeder::moveToNextCandidate(
0343     const Selector_t<UncalibCont_t>& selector,
0344     SeedingState<UncalibCont_t, CalibCont_t, Delegate_t>& state) const {
0345   // Vary the left right solutions
0346   ++state.m_signComboIndex;
0347   if (state.m_signComboIndex < s_signCombo.size()) {
0348     return;
0349   }
0350   // All sign combos tested. Let's reset the signs combo and move on
0351   /// to the next hit inside the layer
0352   state.m_signComboIndex = 0;
0353 
0354   const StrawLayers_t<UncalibCont_t>& strawLayers{state.strawHits()};
0355 
0356   const UncalibCont_t& lower = strawLayers[state.m_lowerLayer.value()];
0357   const UncalibCont_t& upper = strawLayers[state.m_upperLayer.value()];
0358 
0359   /// Next good hit in the lower layer found
0360   ACTS_VERBOSE(__func__ << "() " << __LINE__ << " - Move to next lower hit.");
0361   if (moveToNextHit(lower, selector, state.m_lowerHitIndex)) {
0362     return;
0363   }
0364 
0365   /// Reset the hit in the lower layer && move to the next hit
0366   /// in the upper layer
0367   ACTS_VERBOSE(__func__ << "() " << __LINE__
0368                         << " - All lower hits were tried increment upper hit.");
0369   if (firstGoodHit(lower, selector, state.m_lowerHitIndex) &&
0370       moveToNextHit(upper, selector, state.m_upperHitIndex)) {
0371     return;
0372   }
0373   /// All hit combinations in the two layers are tried -> move to next layer
0374   auto& layerToStay{state.m_moveUpLayer ? state.m_lowerLayer
0375                                         : state.m_upperLayer};
0376   auto& layerToMove{state.m_moveUpLayer ? state.m_upperLayer
0377                                         : state.m_lowerLayer};
0378   auto& hitToStay{state.m_moveUpLayer ? state.m_lowerHitIndex
0379                                       : state.m_upperHitIndex};
0380   auto& hitToMove{state.m_moveUpLayer ? state.m_upperHitIndex
0381                                       : state.m_lowerHitIndex};
0382   ACTS_VERBOSE(__func__ << "() " << __LINE__ << " - Move towards the next "
0383                         << (state.m_moveUpLayer ? "upper" : "lower")
0384                         << " layer. Current state: " << layerToMove.value());
0385   /// Reset the hits in the layer that remains and go to the next layer on the
0386   /// other side. Next time the otherside will stay and the former will move.
0387   if (firstGoodHit(strawLayers[layerToStay.value()], selector, hitToStay) &&
0388       nextLayer(strawLayers, selector, layerToStay.value(), layerToMove,
0389                 hitToMove, !state.m_moveUpLayer)) {
0390     state.m_moveUpLayer = !state.m_moveUpLayer;
0391     if (state.stopSeeding(state.m_lowerLayer.value(),
0392                           state.m_upperLayer.value())) {
0393       state.m_lowerLayer.value() = state.m_upperLayer.value() + 1ul;
0394     }
0395     return;
0396   }
0397 }
0398 
0399 template <
0400     CompositeSpacePointContainer UncalibCont_t,
0401     CompositeSpacePointContainer CalibCont_t,
0402     detail::CompSpacePointSeederDelegate<UncalibCont_t, CalibCont_t> Delegate_t>
0403 bool CompositeSpacePointLineSeeder::passSeedCuts(
0404     const Line_t& tangentSeed,
0405     SeedSolution<UncalibCont_t, Delegate_t>& newSolution,
0406     SeedingState<UncalibCont_t, CalibCont_t, Delegate_t>& state) const {
0407   // check if we collected enough straw hits
0408   const auto& [seedPos, seedDir] = tangentSeed;
0409   const double hitCut =
0410       std::max(1.0 * state.m_nStrawCut,
0411                m_cfg.nStrawLayHitCut * state.strawHits().size());
0412   ACTS_VERBOSE(__func__ << "() " << __LINE__ << " - Found "
0413                         << newSolution.nStrawHits
0414                         << " compatible straw hits. Hit cut is " << hitCut);
0415   if (newSolution.nStrawHits < hitCut) {
0416     return false;
0417   }
0418   if (!m_cfg.overlapCorridor) {
0419     return true;
0420   }
0421   newSolution.solutionSigns = newSolution.leftRightAmbiguity(seedPos, seedDir);
0422   for (std::size_t a = 0ul; a < state.nGenSeeds(); ++a) {
0423     const auto& acceptedSol = state.m_seenSolutions[a];
0424     std::size_t nOverlap{0};
0425     const std::vector<int> corridor =
0426         acceptedSol.leftRightAmbiguity(seedPos, seedDir);
0427     for (std::size_t l = 0; l < acceptedSol.size(); ++l) {
0428       nOverlap += (corridor[l] == acceptedSol.solutionSigns[l]);
0429     }
0430     if (nOverlap == corridor.size() &&
0431         acceptedSol.size() >= newSolution.size()) {
0432       return false;
0433     }
0434   }
0435   if (m_cfg.tightenHitCut) {
0436     state.m_nStrawCut = std::max(state.m_nStrawCut, newSolution.nStrawHits);
0437   }
0438   return true;
0439 }
0440 
0441 template <
0442     CompositeSpacePointContainer UncalibCont_t,
0443     CompositeSpacePointContainer CalibCont_t,
0444     detail::CompSpacePointSeederDelegate<UncalibCont_t, CalibCont_t> Delegate_t>
0445 std::optional<CompositeSpacePointLineSeeder::SegmentSeed<CalibCont_t>>
0446 CompositeSpacePointLineSeeder::buildSeed(
0447     const CalibrationContext& cctx, const Selector_t<UncalibCont_t>& selector,
0448     SeedingState<UncalibCont_t, CalibCont_t, Delegate_t>& state) const {
0449   const StrawLayers_t<UncalibCont_t>& strawLayers{state.strawHits()};
0450 
0451   ACTS_DEBUG(__func__ << "() " << __LINE__ << " - Try to draw new seed from \n"
0452                       << state << ".");
0453   const auto& upperHit =
0454       *strawLayers.at(state.m_upperLayer.value()).at(state.m_upperHitIndex);
0455   const auto& lowerHit =
0456       *strawLayers.at(state.m_lowerLayer.value()).at(state.m_lowerHitIndex);
0457   const auto ambi{static_cast<TangentAmbi>(state.m_signComboIndex)};
0458   ACTS_VERBOSE(__func__ << "() " << __LINE__ << " - " << toString(ambi)
0459                         << "\n   Top seed hit: " << Acts::toString(upperHit)
0460                         << "\n   Bottom seed hit:" << Acts::toString(lowerHit)
0461                         << ".");
0462 
0463   const TwoCircleTangentPars seedPars =
0464       constructTangentLine(lowerHit, upperHit, ambi);
0465   ACTS_VERBOSE(__func__ << "() " << __LINE__
0466                         << " - Tangential parameters: " << seedPars);
0467   if (!isValidLine(seedPars)) {
0468     ACTS_VERBOSE(__func__ << "() " << __LINE__ << " - Reject seed.");
0469     return std::nullopt;
0470   }
0471   // check if we have already seen this solution
0472   if (std::ranges::any_of(state.m_seenSolutions, [&seedPars](const auto& seen) {
0473         const double deltaY = Acts::abs(seen.y0 - seedPars.y0);
0474         const double limitY = Acts::fastHypot(seen.dY0, seedPars.dY0);
0475         const double deltaTheta = Acts::abs(seen.theta - seedPars.theta);
0476         const double limitTheta = Acts::fastHypot(seen.dTheta, seedPars.dTheta);
0477         return deltaY < limitY && deltaTheta < limitTheta;
0478       })) {
0479     ACTS_VERBOSE(__func__ << "() " << __LINE__ << " - Reject seed.");
0480     return std::nullopt;
0481   }
0482   /// Continue to construct a new solution
0483   const double t0 = state.initialParameters()[toUnderlying(ParIdx::t0)];
0484   SeedSolution<UncalibCont_t, Delegate_t> newSolution{seedPars, state};
0485 
0486   ACTS_DEBUG(__func__ << "() " << __LINE__
0487                       << " - Start looking for compatible hits");
0488 
0489   const Line_t tangentSeed{seedPars.y0 * Vector3::UnitY(),
0490                            makeDirection(lowerHit, seedPars.theta)};
0491   const auto& [seedPos, seedDir] = tangentSeed;
0492   const double maxPullSq{Acts::square(m_cfg.hitPullCut)};
0493   constexpr auto covIdx = Acts::toUnderlying(CovIdx::bending);
0494 
0495   for (const auto& [layerNr, hitsInLayer] :
0496        Acts::enumerate(state.strawHits())) {
0497     bool hadGoodHit{false};
0498     for (const auto& [hitNr, testMe] : Acts::enumerate(hitsInLayer)) {
0499       using namespace Acts::detail::LineHelper;
0500       const double distance = Acts::abs(
0501           signedDistance(testMe->localPosition(), testMe->sensorDirection(),
0502                          seedPos, seedDir));
0503       // the hits are ordered in the layer so we assume that once we found good
0504       // hits we are moving away from the seed line so we can abort the hit
0505       // association
0506       const double rMax = state.strawRadius(*testMe);
0507       assert(rMax > Acts::s_epsilon);
0508       assert(testMe->covariance()[covIdx] > Acts::s_epsilon);
0509       if (distance < rMax) {
0510         const double pullSq =
0511             m_cfg.useSimpleStrawPull
0512                 ? Acts::square(distance - Acts::abs(testMe->driftRadius())) /
0513                       testMe->covariance()[covIdx]
0514                 : state.candidateChi2(cctx, seedPos, seedDir, t0, *testMe);
0515 
0516         if (pullSq < maxPullSq) {
0517           ACTS_VERBOSE(__func__
0518                        << "() " << __LINE__ << " - layer,hit = (" << layerNr
0519                        << "," << hitNr
0520                        << ") -> spacePoint: " << Acts::toString(*testMe)
0521                        << " is close enough: " << distance
0522                        << ", pull: " << std::sqrt(pullSq));
0523           hadGoodHit = true;
0524           newSolution.append(layerNr, hitNr);
0525           newSolution.nStrawHits += selector(*testMe);
0526           continue;
0527         }
0528       }
0529       if (hadGoodHit) {
0530         break;
0531       }
0532     }
0533   }
0534   if (!passSeedCuts(tangentSeed, newSolution, state)) {
0535     return std::nullopt;
0536   }
0537   return consructSegmentSeed(cctx, tangentSeed, state, std::move(newSolution));
0538 }
0539 
0540 template <
0541     CompositeSpacePointContainer UncalibCont_t,
0542     CompositeSpacePointContainer CalibCont_t,
0543     detail::CompSpacePointSeederDelegate<UncalibCont_t, CalibCont_t> Delegate_t>
0544 CompositeSpacePointLineSeeder::SegmentSeed<CalibCont_t>
0545 CompositeSpacePointLineSeeder::consructSegmentSeed(
0546     const CalibrationContext& cctx, const Line_t& tangentSeed,
0547     SeedingState<UncalibCont_t, CalibCont_t, Delegate_t>& state,
0548     SeedSolution<UncalibCont_t, Delegate_t>&& newSolution) const {
0549   ACTS_DEBUG(__func__ << "() " << __LINE__ << " Construct new seed from \n"
0550                       << newSolution);
0551   SegmentSeed<CalibCont_t> finalSeed{
0552       combineWithPattern(tangentSeed, state.initialParameters()),
0553       state.newContainer(cctx)};
0554   const auto [seedPos, seedDir] = makeLine(finalSeed.parameters);
0555   /// Append the collected straws to the seed
0556   const double t0 = finalSeed.parameters[toUnderlying(ParIdx::t0)];
0557   for (std::size_t s = 0; s < newSolution.size(); ++s) {
0558     state.append(cctx, seedPos, seedDir, t0, newSolution.getHit(s),
0559                  finalSeed.hits);
0560   }
0561   /// The solution object is no longer needed for this seed.
0562   state.m_seenSolutions.push_back(std::move(newSolution));
0563 
0564   ACTS_DEBUG(__func__ << "() " << __LINE__
0565                       << " - Associate the strip hits to the seed");
0566   for (const auto& stripLayerHits : state.stripHits()) {
0567     double bestChi2Loc0{m_cfg.hitPullCut};
0568     double bestChi2Loc1{m_cfg.hitPullCut};
0569     std::size_t bestIdxLoc0{stripLayerHits.size()};
0570     std::size_t bestIdxLoc1{stripLayerHits.size()};
0571     /// Find the hit with the lowest pull
0572     for (const auto& [hitIdx, testMe] : Acts::enumerate(stripLayerHits)) {
0573       const double chi2 =
0574           state.candidateChi2(cctx, seedPos, seedDir, t0, *testMe);
0575       if (testMe->measuresLoc0() && chi2 < bestChi2Loc0) {
0576         bestChi2Loc0 = chi2;
0577         bestIdxLoc0 = hitIdx;
0578       }
0579       if (testMe->measuresLoc1() && chi2 < bestChi2Loc1) {
0580         bestChi2Loc1 = chi2;
0581         bestIdxLoc1 = hitIdx;
0582       }
0583     }
0584     if (bestIdxLoc0 < stripLayerHits.size()) {
0585       ACTS_VERBOSE(__func__ << "() " << __LINE__ << " - Append loc0 strip hit "
0586                             << Acts::toString(*stripLayerHits.at(bestIdxLoc0))
0587                             << ".");
0588       state.append(cctx, seedPos, seedDir, t0, *stripLayerHits.at(bestIdxLoc0),
0589                    finalSeed.hits);
0590     }
0591     if (bestIdxLoc1 != bestIdxLoc0 && bestIdxLoc1 < stripLayerHits.size()) {
0592       ACTS_VERBOSE(__func__ << "() " << __LINE__ << " - Append loc1 strip hit "
0593                             << Acts::toString(*stripLayerHits.at(bestIdxLoc1))
0594                             << ".");
0595       state.append(cctx, seedPos, seedDir, t0, *stripLayerHits.at(bestIdxLoc1),
0596                    finalSeed.hits);
0597     }
0598   }
0599   return finalSeed;
0600 }
0601 }  // namespace Acts::Experimental