Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 09:25:13

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 <boost/test/data/test_case.hpp>
0010 #include <boost/test/unit_test.hpp>
0011 
0012 #include "Acts/Definitions/TrackParametrization.hpp"
0013 #include "Acts/EventData/MultiTrajectory.hpp"
0014 #include "Acts/EventData/TrackContainer.hpp"
0015 #include "Acts/EventData/TrackProxyConcept.hpp"
0016 #include "Acts/EventData/TrackStateType.hpp"
0017 #include "Acts/EventData/VectorMultiTrajectory.hpp"
0018 #include "Acts/EventData/VectorTrackContainer.hpp"
0019 #include "Acts/Geometry/GeometryIdentifier.hpp"
0020 #include "Acts/Surfaces/CurvilinearSurface.hpp"
0021 #include "Acts/Surfaces/PerigeeSurface.hpp"
0022 #include "Acts/Surfaces/PlaneSurface.hpp"
0023 #include "Acts/TrackFinding/TrackSelector.hpp"
0024 #include "Acts/Utilities/AngleHelpers.hpp"
0025 
0026 #include <limits>
0027 #include <numbers>
0028 
0029 using namespace Acts;
0030 namespace bdata = boost::unit_test::data;
0031 
0032 struct MockTrack {
0033   static constexpr bool ReadOnly = true;
0034   using Container = VectorTrackContainer;
0035   using Trajectory = VectorMultiTrajectory;
0036   using IndexType = TrackIndexType;
0037 
0038   MockTrack()
0039       : m_parameterBuffer(BoundVector::Zero()),
0040         m_covarianceBuffer(BoundSquareMatrix::Identity()) {}
0041 
0042   TrackIndexType index() const { return m_index; }
0043   TrackIndexType tipIndex() const { return m_tipIndex; }
0044   TrackIndexType stemIndex() const { return m_stemIndex; }
0045 
0046   bool hasReferenceSurface() const { return true; }
0047   const Surface& referenceSurface() const {
0048     static const std::shared_ptr<PlaneSurface> srf =
0049         CurvilinearSurface(Vector3::Zero(), Vector3::UnitZ()).planeSurface();
0050     return *srf;
0051   }
0052 
0053   detail_tpc::ConstParametersMap parameters() const {
0054     syncParameterBuffer();
0055     return detail_tpc::ConstParametersMap(m_parameterBuffer.data());
0056   }
0057 
0058   detail_tpc::ConstCovarianceMap covariance() const {
0059     return detail_tpc::ConstCovarianceMap(m_covarianceBuffer.data());
0060   }
0061 
0062   ParticleHypothesis particleHypothesis() const {
0063     return ParticleHypothesis::pion();
0064   }
0065 
0066   double theta() const { return m_theta; }
0067   double phi() const { return m_phi; }
0068   double qOverP() const { return m_qOverP; }
0069   double absoluteMomentum() const { return m_absMomentum; }
0070   double transverseMomentum() const { return m_pt; }
0071   double charge() const { return std::copysign(1.0, m_qOverP); }
0072   double loc0() const { return m_loc0; }
0073   double loc1() const { return m_loc1; }
0074   double time() const { return m_time; }
0075 
0076   unsigned int nMeasurements() const { return m_nMeasurements; }
0077   unsigned int nHoles() const { return m_nHoles; }
0078   unsigned int nOutliers() const { return m_nOutliers; }
0079   unsigned int nSharedHits() const { return m_nSharedHits; }
0080   float chi2() const { return m_chi2; }
0081   unsigned int nDoF() const { return m_nDoF; }
0082 
0083   unsigned int nTrackStates() const { return 0u; }
0084 
0085   // To comply with concept, not actually used
0086   bool hasColumn(HashedString /*key*/) const {
0087     throw std::runtime_error("Not implemented");
0088   }
0089 
0090   template <typename T, HashedString>
0091   const T& component() const {
0092     throw std::runtime_error("Not implemented");
0093   }
0094 
0095   template <typename T>
0096   const T& component(HashedString /*key*/) const {
0097     throw std::runtime_error("Not implemented");
0098   }
0099 
0100   template <typename T, HashedString>
0101   T& component()
0102     requires(!ReadOnly)
0103   {
0104     throw std::runtime_error("Not implemented");
0105   }
0106 
0107   template <typename T>
0108   T& component(HashedString /*key*/)
0109     requires(!ReadOnly)
0110   {
0111     throw std::runtime_error("Not implemented");
0112   }
0113 
0114  private:
0115   struct MockTrackState {
0116     const Surface& referenceSurface() const {
0117       static const std::shared_ptr<PlaneSurface> srf =
0118           CurvilinearSurface(Vector3::Zero(), Vector3::UnitZ()).planeSurface();
0119       return *srf;
0120     }
0121 
0122     ConstTrackStateType typeFlags() const {
0123       static const ConstTrackStateType::raw_type raw{0};
0124       return ConstTrackStateType{raw};
0125     }
0126   };
0127 
0128   struct TrackStateRange {
0129     auto begin() const { return m_trackStates.begin(); }
0130     auto end() const { return m_trackStates.end(); }
0131 
0132    private:
0133     std::vector<MockTrackState> m_trackStates;
0134   };
0135 
0136  public:
0137   TrackStateRange trackStatesReversed() const { return {}; }
0138 
0139   void syncParameterBuffer() const {
0140     m_parameterBuffer[eBoundLoc0] = m_loc0;
0141     m_parameterBuffer[eBoundLoc1] = m_loc1;
0142     m_parameterBuffer[eBoundTime] = m_time;
0143     m_parameterBuffer[eBoundPhi] = m_phi;
0144     m_parameterBuffer[eBoundTheta] = m_theta;
0145     m_parameterBuffer[eBoundQOverP] = m_qOverP;
0146   }
0147 
0148   TrackIndexType m_index = 0;
0149   TrackIndexType m_tipIndex = 0;
0150   TrackIndexType m_stemIndex = 0;
0151 
0152   double m_theta = 0.;
0153   double m_phi = 0.;
0154   double m_pt = 0.;
0155   double m_loc0 = 0.;
0156   double m_loc1 = 0.;
0157   double m_time = 0.;
0158   unsigned int m_nMeasurements = 0;
0159   unsigned int m_nHoles = 0;
0160   unsigned int m_nOutliers = 0;
0161   unsigned int m_nSharedHits = 0;
0162   float m_chi2 = 0.F;
0163   unsigned int m_nDoF = 0;
0164   double m_qOverP = 1.;
0165   double m_absMomentum = 1.;
0166 
0167   mutable BoundVector m_parameterBuffer;
0168   mutable BoundSquareMatrix m_covarianceBuffer;
0169 };
0170 
0171 static_assert(TrackProxyConcept<MockTrack>);
0172 
0173 namespace ActsTests {
0174 
0175 BOOST_AUTO_TEST_SUITE(TrackFindingSuite)
0176 
0177 std::vector<double> etaValues{-5.0, -4.5, -4.0, -3.5, -3.0, -2.5, -2.0, -1.5,
0178                               -1.0, -0.5, 0.0,  0.5,  1.0,  1.5,  2.0,  2.5,
0179                               3.0,  3.5,  4.0,  4.5,  5.0,  1.0};
0180 
0181 BOOST_DATA_TEST_CASE(TestSingleBinCase, bdata::make(etaValues), eta) {
0182   TrackSelector::EtaBinnedConfig cfgBase;
0183 
0184   MockTrack baseTrack{};
0185   baseTrack.m_theta = AngleHelpers::thetaFromEta(eta);
0186   baseTrack.m_phi = 0.5;
0187   baseTrack.m_pt = 0.5;
0188   baseTrack.m_loc0 = 0.5;
0189   baseTrack.m_loc1 = 0.5;
0190   baseTrack.m_time = 0.5;
0191   baseTrack.m_nMeasurements = 1;
0192   baseTrack.m_nHoles = 0;
0193   baseTrack.m_nOutliers = 0;
0194   baseTrack.m_nSharedHits = 0;
0195   baseTrack.m_chi2 = 0.0;
0196 
0197   {
0198     TrackSelector selector{cfgBase};
0199     // should select anything
0200     BOOST_CHECK(selector.isValidTrack(baseTrack));
0201   }
0202 
0203   auto check = [&](const auto& var, const auto& minPtr, const auto& maxPtr,
0204                    const auto& propPtr) {
0205     BOOST_TEST_INFO_SCOPE("Testing " << var);
0206     MockTrack track = baseTrack;
0207 
0208     auto cfgMinOnly = cfgBase;
0209     auto cfgMaxOnly = cfgBase;
0210     auto cfgMinMax = cfgBase;
0211 
0212     cfgMinOnly.cutSets.at(0).*minPtr = -1;
0213     cfgMinMax.cutSets.at(0).*minPtr = -1;
0214     cfgMaxOnly.cutSets.at(0).*maxPtr = 1;
0215     cfgMinMax.cutSets.at(0).*maxPtr = 1;
0216 
0217     TrackSelector minOnly{cfgMinOnly};
0218     TrackSelector maxOnly{cfgMaxOnly};
0219     TrackSelector minMax{cfgMinMax};
0220 
0221     BOOST_CHECK(minOnly.isValidTrack(track));
0222     BOOST_CHECK(maxOnly.isValidTrack(track));
0223     BOOST_CHECK(minMax.isValidTrack(track));
0224 
0225     // push track outside of minimum
0226     track.*propPtr = -1.1;
0227 
0228     BOOST_CHECK(!minOnly.isValidTrack(track));
0229     BOOST_CHECK(maxOnly.isValidTrack(track));
0230     BOOST_CHECK(!minMax.isValidTrack(track));
0231 
0232     // push track outside of maximum
0233     track.*propPtr = 1.1;
0234 
0235     BOOST_CHECK(minOnly.isValidTrack(track));
0236     BOOST_CHECK(!maxOnly.isValidTrack(track));
0237     BOOST_CHECK(!minMax.isValidTrack(track));
0238   };
0239 
0240   check("loc0", &TrackSelector::Config::loc0Min,
0241         &TrackSelector::Config::loc0Max, &MockTrack::m_loc0);
0242 
0243   check("loc1", &TrackSelector::Config::loc1Min,
0244         &TrackSelector::Config::loc1Max, &MockTrack::m_loc1);
0245 
0246   check("phi", &TrackSelector::Config::phiMin, &TrackSelector::Config::phiMax,
0247         &MockTrack::m_phi);
0248 
0249   check("time", &TrackSelector::Config::timeMin,
0250         &TrackSelector::Config::timeMax, &MockTrack::m_time);
0251 
0252   {
0253     BOOST_TEST_INFO_SCOPE("pt min");
0254     auto cfg = cfgBase;
0255     cfg.cutSets.at(0).ptMin = {0.2};
0256     TrackSelector selector{cfg};
0257     MockTrack track = baseTrack;
0258     BOOST_CHECK(selector.isValidTrack(track));
0259     track.m_pt = 0.1;
0260     BOOST_CHECK(!selector.isValidTrack(track));
0261   }
0262 
0263   {
0264     BOOST_TEST_INFO_SCOPE("pt max");
0265     auto cfg = cfgBase;
0266     cfg.cutSets.at(0).ptMax = {1.0};
0267     TrackSelector selector{cfg};
0268     MockTrack track = baseTrack;
0269     BOOST_CHECK(selector.isValidTrack(track));
0270     track.m_pt = 1.1;
0271     BOOST_CHECK(!selector.isValidTrack(track));
0272   }
0273 
0274   {
0275     BOOST_TEST_INFO_SCOPE("pt min max");
0276     auto cfg = cfgBase;
0277     cfg.cutSets.at(0).ptMin = {0.2};
0278     cfg.cutSets.at(0).ptMax = {1.0};
0279     TrackSelector selector{cfg};
0280     MockTrack track = baseTrack;
0281     BOOST_CHECK(selector.isValidTrack(track));
0282     track.m_pt = 0.1;
0283     BOOST_CHECK(!selector.isValidTrack(track));
0284     track.m_pt = 1.1;
0285     BOOST_CHECK(!selector.isValidTrack(track));
0286   }
0287 
0288   {
0289     BOOST_TEST_INFO_SCOPE("eta min");
0290     auto cfg = cfgBase;
0291     cfg.cutSets.at(0).etaMin = {-1.0};
0292     TrackSelector selector{cfg};
0293     MockTrack track = baseTrack;
0294     track.m_theta = AngleHelpers::thetaFromEta(0.5);
0295     BOOST_CHECK(selector.isValidTrack(track));
0296     track.m_theta = AngleHelpers::thetaFromEta(-1.1);
0297     BOOST_CHECK(!selector.isValidTrack(track));
0298   }
0299 
0300   {
0301     BOOST_TEST_INFO_SCOPE("eta max");
0302     auto cfg = cfgBase;
0303     cfg.cutSets.at(0).etaMax = {1.0};
0304     TrackSelector selector{cfg};
0305     MockTrack track = baseTrack;
0306     track.m_theta = AngleHelpers::thetaFromEta(0.5);
0307     BOOST_CHECK(selector.isValidTrack(track));
0308     track.m_theta = AngleHelpers::thetaFromEta(1.1);
0309     BOOST_CHECK(!selector.isValidTrack(track));
0310   }
0311 
0312   {
0313     BOOST_TEST_INFO_SCOPE("eta min max");
0314     auto cfg = cfgBase;
0315     cfg.cutSets.at(0).etaMin = {-1.0};
0316     cfg.cutSets.at(0).etaMax = {1.0};
0317     TrackSelector selector{cfg};
0318     MockTrack track = baseTrack;
0319     track.m_theta = AngleHelpers::thetaFromEta(0.5);
0320     BOOST_CHECK(selector.isValidTrack(track));
0321     track.m_theta = AngleHelpers::thetaFromEta(-1.1);
0322     BOOST_CHECK(!selector.isValidTrack(track));
0323     track.m_theta = AngleHelpers::thetaFromEta(1.1);
0324     BOOST_CHECK(!selector.isValidTrack(track));
0325   }
0326 
0327   {
0328     BOOST_TEST_INFO_SCOPE("abs eta min");
0329     auto cfg = cfgBase;
0330     cfg.cutSets.at(0).absEtaMin = {0.2};
0331     TrackSelector selector{cfg};
0332     MockTrack track = baseTrack;
0333     track.m_theta = AngleHelpers::thetaFromEta(0.5);
0334     BOOST_CHECK(selector.isValidTrack(track));
0335     track.m_theta = AngleHelpers::thetaFromEta(-0.5);
0336     BOOST_CHECK(selector.isValidTrack(track));
0337 
0338     track.m_theta = AngleHelpers::thetaFromEta(0.1);
0339     BOOST_CHECK(!selector.isValidTrack(track));
0340     track.m_theta = AngleHelpers::thetaFromEta(-0.1);
0341     BOOST_CHECK(!selector.isValidTrack(track));
0342   }
0343 
0344   {
0345     BOOST_TEST_INFO_SCOPE("abs eta max");
0346     auto cfg = cfgBase;
0347     cfg.cutSets.at(0).absEtaMax = {1.0};
0348     TrackSelector selector{cfg};
0349     MockTrack track = baseTrack;
0350     track.m_theta = AngleHelpers::thetaFromEta(0.5);
0351     BOOST_CHECK(selector.isValidTrack(track));
0352     track.m_theta = AngleHelpers::thetaFromEta(-0.5);
0353     BOOST_CHECK(selector.isValidTrack(track));
0354 
0355     track.m_theta = AngleHelpers::thetaFromEta(1.1);
0356     BOOST_CHECK(!selector.isValidTrack(track));
0357     track.m_theta = AngleHelpers::thetaFromEta(-1.1);
0358     BOOST_CHECK(!selector.isValidTrack(track));
0359   }
0360 
0361   {
0362     BOOST_TEST_INFO_SCOPE("abs eta min max");
0363     auto cfg = cfgBase;
0364     cfg.cutSets.at(0).absEtaMin = {0.2};
0365     cfg.cutSets.at(0).absEtaMax = {1.0};
0366     TrackSelector selector{cfg};
0367     MockTrack track = baseTrack;
0368     track.m_theta = AngleHelpers::thetaFromEta(0.5);
0369     BOOST_CHECK(selector.isValidTrack(track));
0370     track.m_theta = AngleHelpers::thetaFromEta(-0.5);
0371     BOOST_CHECK(selector.isValidTrack(track));
0372 
0373     track.m_theta = AngleHelpers::thetaFromEta(0.1);
0374     BOOST_CHECK(!selector.isValidTrack(track));
0375     track.m_theta = AngleHelpers::thetaFromEta(-0.1);
0376     BOOST_CHECK(!selector.isValidTrack(track));
0377 
0378     track.m_theta = AngleHelpers::thetaFromEta(1.1);
0379     BOOST_CHECK(!selector.isValidTrack(track));
0380     track.m_theta = AngleHelpers::thetaFromEta(-1.1);
0381     BOOST_CHECK(!selector.isValidTrack(track));
0382   }
0383 
0384   {
0385     BOOST_TEST_INFO_SCOPE("nMeas min");
0386     auto cfg = cfgBase;
0387     cfg.cutSets.at(0).minMeasurements = {1};
0388     TrackSelector selector{cfg};
0389     MockTrack track = baseTrack;
0390     track.m_nMeasurements = {2};
0391     BOOST_CHECK(selector.isValidTrack(track));
0392     track.m_nMeasurements = {1};
0393     BOOST_CHECK(selector.isValidTrack(track));
0394     track.m_nMeasurements = {0};
0395     BOOST_CHECK(!selector.isValidTrack(track));
0396   }
0397 
0398   {
0399     BOOST_TEST_INFO_SCOPE("nHoles max");
0400     auto cfg = cfgBase;
0401     cfg.cutSets.at(0).maxHoles = {3};
0402     TrackSelector selector{cfg};
0403     MockTrack track = baseTrack;
0404     track.m_nHoles = {2};
0405     BOOST_CHECK(selector.isValidTrack(track));
0406     track.m_nHoles = {3};
0407     BOOST_CHECK(selector.isValidTrack(track));
0408     track.m_nHoles = {4};
0409     BOOST_CHECK(!selector.isValidTrack(track));
0410   }
0411 
0412   {
0413     BOOST_TEST_INFO_SCOPE("nOutliers max");
0414     auto cfg = cfgBase;
0415     cfg.cutSets.at(0).maxOutliers = {3};
0416     TrackSelector selector{cfg};
0417     MockTrack track = baseTrack;
0418     track.m_nOutliers = {2};
0419     BOOST_CHECK(selector.isValidTrack(track));
0420     track.m_nOutliers = {3};
0421     BOOST_CHECK(selector.isValidTrack(track));
0422     track.m_nOutliers = {4};
0423     BOOST_CHECK(!selector.isValidTrack(track));
0424   }
0425 
0426   {
0427     BOOST_TEST_INFO_SCOPE("nSharedHits max");
0428     auto cfg = cfgBase;
0429     cfg.cutSets.at(0).maxSharedHits = {3};
0430     TrackSelector selector{cfg};
0431     MockTrack track = baseTrack;
0432     track.m_nSharedHits = {2};
0433     BOOST_CHECK(selector.isValidTrack(track));
0434     track.m_nSharedHits = {3};
0435     BOOST_CHECK(selector.isValidTrack(track));
0436     track.m_nSharedHits = {4};
0437     BOOST_CHECK(!selector.isValidTrack(track));
0438   }
0439 
0440   {
0441     BOOST_TEST_INFO_SCOPE("nSharedHits max");
0442     auto cfg = cfgBase;
0443     cfg.cutSets.at(0).maxChi2 = {3};
0444     TrackSelector selector{cfg};
0445     MockTrack track = baseTrack;
0446     track.m_chi2 = {2};
0447     BOOST_CHECK(selector.isValidTrack(track));
0448     track.m_chi2 = {3};
0449     BOOST_CHECK(selector.isValidTrack(track));
0450     track.m_chi2 = {4};
0451     BOOST_CHECK(!selector.isValidTrack(track));
0452   }
0453 }
0454 
0455 BOOST_AUTO_TEST_CASE(TestSingleBinEtaCutByBinEdge) {
0456   TrackSelector selector{TrackSelector::EtaBinnedConfig(1.0).addCuts(2.0)};
0457 
0458   BOOST_TEST_INFO_SCOPE(selector.config());
0459 
0460   MockTrack track{};
0461   track.m_theta = AngleHelpers::thetaFromEta(0.0);
0462   BOOST_CHECK(!selector.isValidTrack(track));
0463 
0464   track.m_theta = AngleHelpers::thetaFromEta(0.5);
0465   BOOST_CHECK(!selector.isValidTrack(track));
0466 
0467   // cannot easily check on-edge behavior because of floating point arithmetic
0468   // (it won't be exactly 1.0 in selector)
0469   track.m_theta = AngleHelpers::thetaFromEta(1.01);
0470   BOOST_CHECK(selector.isValidTrack(track));
0471 
0472   track.m_theta = AngleHelpers::thetaFromEta(1.5);
0473   BOOST_CHECK(selector.isValidTrack(track));
0474 
0475   track.m_theta = AngleHelpers::thetaFromEta(2.0);
0476   BOOST_CHECK(!selector.isValidTrack(track));
0477 }
0478 
0479 BOOST_AUTO_TEST_CASE(TestMultiBinCuts) {
0480   MockTrack baseTrack{};
0481   baseTrack.m_theta = AngleHelpers::thetaFromEta(1.0);
0482   baseTrack.m_phi = 0.5;
0483   baseTrack.m_pt = 0.5;
0484   baseTrack.m_loc0 = 0.5;
0485   baseTrack.m_loc1 = 0.5;
0486   baseTrack.m_time = 0.5;
0487   baseTrack.m_nMeasurements = 1;
0488   baseTrack.m_nHoles = 0;
0489   baseTrack.m_nOutliers = 0;
0490   baseTrack.m_nSharedHits = 0;
0491   baseTrack.m_chi2 = 0.0;
0492 
0493   using Config = TrackSelector::Config;
0494 
0495   using factory_ptr_t = Config& (Config::*)(double, double);
0496   using prop_ptr_t = double MockTrack::*;
0497 
0498   auto check = [&](const char* name, const factory_ptr_t& factory,
0499                    const prop_ptr_t& prop) {
0500     BOOST_TEST_CONTEXT(name) {
0501       auto cfg = TrackSelector::EtaBinnedConfig{0.0};
0502 
0503       cfg.addCuts(2.0, [&](auto& c) { (c.*factory)(-1.0, 1.0); })
0504           .addCuts([&](auto& c) { (c.*factory)(-2.0, 2.0); });
0505 
0506       TrackSelector selector{cfg};
0507 
0508       BOOST_TEST_INFO_SCOPE(cfg);
0509 
0510       {
0511         // exactly at zero
0512         MockTrack track = baseTrack;
0513         track.m_theta = AngleHelpers::thetaFromEta(0.0);
0514 
0515         BOOST_CHECK(selector.isValidTrack(track));
0516 
0517         track.*prop = -1.1;
0518         BOOST_CHECK(!selector.isValidTrack(track));
0519 
0520         track.*prop = 1.1;
0521         BOOST_CHECK(!selector.isValidTrack(track));
0522       }
0523 
0524       {
0525         // first bin
0526         MockTrack track = baseTrack;
0527         track.m_theta = AngleHelpers::thetaFromEta(1.0);
0528 
0529         BOOST_CHECK(selector.isValidTrack(track));
0530 
0531         track.*prop = -1.1;
0532         BOOST_CHECK(!selector.isValidTrack(track));
0533 
0534         track.*prop = 1.1;
0535         BOOST_CHECK(!selector.isValidTrack(track));
0536       }
0537 
0538       {
0539         // first bin edge
0540         MockTrack track = baseTrack;
0541         track.m_theta = AngleHelpers::thetaFromEta(
0542             2.0 - std::numeric_limits<double>::epsilon());
0543 
0544         BOOST_CHECK(selector.isValidTrack(track));
0545 
0546         track.*prop = -1.1;
0547         BOOST_CHECK(!selector.isValidTrack(track));
0548 
0549         track.*prop = 1.1;
0550         BOOST_CHECK(!selector.isValidTrack(track));
0551       }
0552 
0553       {
0554         // second bin lower edge
0555         MockTrack track = baseTrack;
0556         track.m_theta = AngleHelpers::thetaFromEta(2.0);
0557 
0558         BOOST_CHECK(selector.isValidTrack(track));
0559 
0560         track.*prop = -1.1;
0561         BOOST_CHECK(selector.isValidTrack(track));
0562 
0563         track.*prop = 1.1;
0564         BOOST_CHECK(selector.isValidTrack(track));
0565 
0566         track.*prop = -2.1;
0567         BOOST_CHECK(!selector.isValidTrack(track));
0568 
0569         track.*prop = 2.1;
0570         BOOST_CHECK(!selector.isValidTrack(track));
0571       }
0572 
0573       {
0574         // second bin
0575         MockTrack track = baseTrack;
0576         track.m_theta = AngleHelpers::thetaFromEta(10.0);
0577 
0578         track.*prop = -1.1;
0579         BOOST_CHECK(selector.isValidTrack(track));
0580 
0581         track.*prop = 1.1;
0582         BOOST_CHECK(selector.isValidTrack(track));
0583 
0584         track.*prop = -2.1;
0585         BOOST_CHECK(!selector.isValidTrack(track));
0586 
0587         track.*prop = 2.1;
0588         BOOST_CHECK(!selector.isValidTrack(track));
0589       }
0590     }
0591   };
0592 
0593   check("loc0", &Config::loc0, &MockTrack::m_loc0);
0594   check("loc1", &Config::loc1, &MockTrack::m_loc1);
0595   check("time", &Config::time, &MockTrack::m_time);
0596   check("phi", &Config::phi, &MockTrack::m_phi);
0597   check("pt", &Config::pt, &MockTrack::m_pt);
0598 }
0599 
0600 BOOST_AUTO_TEST_CASE(TestBinSelection) {
0601   using EtaBinnedConfig = TrackSelector::EtaBinnedConfig;
0602   constexpr double inf = std::numeric_limits<double>::infinity();
0603 
0604   {
0605     EtaBinnedConfig cfg{std::vector<double>{0, inf}};
0606     for (int i = -1; i <= 1; i = i + 2) {
0607       BOOST_CHECK_EQUAL(cfg.binIndex(i * 0.0), 0);
0608       BOOST_CHECK_EQUAL(cfg.binIndex(i * 1.0), 0);
0609       BOOST_CHECK_EQUAL(cfg.binIndex(i * 2.0), 0);
0610       BOOST_CHECK_EQUAL(cfg.binIndex(i * 3.0), 0);
0611       BOOST_CHECK_EQUAL(cfg.binIndex(i * 10.0), 0);
0612       BOOST_CHECK_EQUAL(cfg.binIndex(i * 1000.0), 0);
0613     }
0614   }
0615 
0616   {
0617     EtaBinnedConfig cfg{std::vector<double>{0, 0.5, 1.5, 2.5, 3.0, inf}};
0618     for (int i = -1; i <= 1; i = i + 2) {
0619       BOOST_CHECK_EQUAL(cfg.binIndex(i * 0.0), 0);
0620       BOOST_CHECK_EQUAL(cfg.binIndex(i * 1.0), 1);
0621       BOOST_CHECK_EQUAL(cfg.binIndex(i * 2.0), 2);
0622       BOOST_CHECK_EQUAL(cfg.binIndex(i * 3.0), 4);
0623       BOOST_CHECK_EQUAL(cfg.binIndex(i * 10.0), 4);
0624       BOOST_CHECK_EQUAL(cfg.binIndex(i * 1000.0), 4);
0625     }
0626   }
0627 
0628   {
0629     EtaBinnedConfig cfg{std::vector<double>{0, 1, 2}};
0630     for (int i = -1; i <= 1; i = i + 2) {
0631       BOOST_CHECK_EQUAL(cfg.binIndex(i * 0.0), 0);
0632       BOOST_CHECK_EQUAL(cfg.binIndex(i * 1.0), 1);
0633       BOOST_CHECK_EQUAL(
0634           cfg.binIndex(i * (2.0 - std::numeric_limits<double>::epsilon())), 1);
0635       BOOST_CHECK_THROW(cfg.binIndex(i * 2.0), std::invalid_argument);
0636     }
0637   }
0638 }
0639 
0640 BOOST_AUTO_TEST_CASE(TestConstructor) {
0641   // valid multi bin construction
0642   {
0643     TrackSelector::EtaBinnedConfig cfg{std::vector<double>{0, 1, 4}};
0644     cfg.cutSets.at(0).ptMin = 0.9;
0645     cfg.cutSets.at(1).ptMin = 0.4;
0646     TrackSelector{cfg};
0647   }
0648 
0649   {
0650     // Track selector config with 2 eta bins
0651     TrackSelector::EtaBinnedConfig cfg{std::vector<double>{0, 1, 4}};
0652 
0653     // just the right amount!
0654     TrackSelector{cfg};
0655 
0656     // not enough cut cets
0657     cfg.cutSets.resize(1);
0658     BOOST_CHECK_THROW(TrackSelector{cfg}, std::invalid_argument);
0659 
0660     // too many cut sets
0661     cfg.cutSets.resize(3);
0662     BOOST_CHECK_THROW(TrackSelector{cfg}, std::invalid_argument);
0663   }
0664 
0665   // Constructor straight from cut config
0666   TrackSelector::Config cuts;
0667   TrackSelector selector{cuts};
0668   BOOST_CHECK_EQUAL(selector.config().absEtaEdges.size(), 2);
0669 
0670   {
0671     // Invalid sequence of chained construction
0672     auto cfg = TrackSelector::EtaBinnedConfig(0);
0673     cfg.addCuts(2.0, [](auto& c) { c.loc0(-1.0, 1.0); });
0674     BOOST_CHECK_THROW(cfg.addCuts(1.0), std::invalid_argument);
0675     BOOST_CHECK_THROW(TrackSelector::EtaBinnedConfig(0).addCuts(-2.0),
0676                       std::invalid_argument);
0677   }
0678 
0679   {
0680     auto cfg = TrackSelector::EtaBinnedConfig(1.0);
0681 
0682     cfg.addCuts(2.0, [](auto& c) { c.loc0(-1.0, 1.0); });
0683     BOOST_CHECK_EQUAL(cfg.nEtaBins(), 1);
0684     BOOST_CHECK_EQUAL(cfg.getCuts(1.5).loc0Min, -1.0);
0685     BOOST_CHECK_EQUAL(cfg.getCuts(1.5).loc0Max, 1.0);
0686 
0687     cfg.addCuts(3.0, [](auto& c) { c.loc0(-2.0, 2.0); });
0688     BOOST_CHECK_EQUAL(cfg.nEtaBins(), 2);
0689     BOOST_CHECK_EQUAL(cfg.getCuts(2.5).loc0Min, -2.0);
0690     BOOST_CHECK_EQUAL(cfg.getCuts(2.5).loc0Max, 2.0);
0691 
0692     cfg.addCuts(4.0, [](auto& c) { c.loc0(-3.0, 3.0); });
0693     BOOST_CHECK_EQUAL(cfg.nEtaBins(), 3);
0694     BOOST_CHECK_EQUAL(cfg.getCuts(3.5).loc0Min, -3.0);
0695     BOOST_CHECK_EQUAL(cfg.getCuts(3.5).loc0Max, 3.0);
0696   }
0697 }
0698 
0699 BOOST_AUTO_TEST_CASE(SubsetHitCountCut) {
0700   auto makeSurface = [](GeometryIdentifier id) {
0701     std::shared_ptr<PlaneSurface> srf =
0702         CurvilinearSurface(Vector3::Zero(), Vector3::UnitZ()).planeSurface();
0703 
0704     srf->assignGeometryId(id);
0705     return srf;
0706   };
0707 
0708   auto addTrackState = [](auto& track, const auto& surface,
0709                           TrackStateFlag flag) {
0710     auto ts = track.appendTrackState();
0711     ts.setReferenceSurface(surface);
0712     ts.typeFlags().set(flag);
0713     return ts;
0714   };
0715 
0716   auto addMeasurement = [&](auto& track, const auto& surface) {
0717     return addTrackState(track, surface, TrackStateFlag::MeasurementFlag);
0718   };
0719 
0720   auto addMaterial = [&](auto& track, const auto& surface) {
0721     return addTrackState(track, surface, TrackStateFlag::MaterialFlag);
0722   };
0723 
0724   TrackContainer tc{VectorTrackContainer{}, VectorMultiTrajectory{}};
0725 
0726   auto makeTrack = [&]() {
0727     auto track = tc.makeTrack();
0728 
0729     using namespace Acts::UnitLiterals;
0730     track.parameters() << 0, 0, std::numbers::pi / 2., std::numbers::pi / 2.,
0731         1 / 1_GeV, 0;
0732     auto perigee = Surface::makeShared<PerigeeSurface>(Vector3::Zero());
0733     track.setReferenceSurface(perigee);
0734     return track;
0735   };
0736 
0737   auto vol7_lay3_sen2 = makeSurface(
0738       GeometryIdentifier{}.withVolume(7).withLayer(3).withSensitive(2));
0739   auto vol7_lay4 = makeSurface(GeometryIdentifier{}.withVolume(7).withLayer(4));
0740   auto vol7_lay3_sen8 = makeSurface(
0741       GeometryIdentifier{}.withVolume(7).withLayer(3).withSensitive(8));
0742   auto vol7_lay5_sen11 = makeSurface(
0743       GeometryIdentifier{}.withVolume(7).withLayer(5).withSensitive(11));
0744   auto vol7_lay5_sen12 = makeSurface(
0745       GeometryIdentifier{}.withVolume(7).withLayer(5).withSensitive(12));
0746   auto vol7_lay6_sen3 = makeSurface(
0747       GeometryIdentifier{}.withVolume(7).withLayer(6).withSensitive(3));
0748 
0749   auto vol8_lay8_sen1 = makeSurface(
0750       GeometryIdentifier{}.withVolume(8).withLayer(8).withSensitive(1));
0751   auto vol8_lay8_sen2 = makeSurface(
0752       GeometryIdentifier{}.withVolume(8).withLayer(8).withSensitive(2));
0753   auto vol8_lay9_sen1 = makeSurface(
0754       GeometryIdentifier{}.withVolume(8).withLayer(9).withSensitive(1));
0755 
0756   TrackSelector::Config cfgVol7;
0757   cfgVol7.measurementCounter.addCounter({GeometryIdentifier{}.withVolume(7)},
0758                                         3);
0759   TrackSelector selectorVol7{cfgVol7};
0760 
0761   auto trackVol7 = makeTrack();
0762 
0763   BOOST_CHECK(!selectorVol7.isValidTrack(trackVol7));
0764 
0765   // 1 hit in vol7
0766   addMeasurement(trackVol7, vol7_lay3_sen2);
0767   addMaterial(trackVol7, vol7_lay4);
0768 
0769   BOOST_CHECK(!selectorVol7.isValidTrack(trackVol7));
0770   addMeasurement(trackVol7, vol7_lay5_sen11);
0771   BOOST_CHECK(!selectorVol7.isValidTrack(trackVol7));
0772 
0773   // Now we should have enough hits
0774   addMeasurement(trackVol7, vol7_lay6_sen3);
0775   BOOST_CHECK(selectorVol7.isValidTrack(trackVol7));
0776 
0777   TrackSelector::Config cfgVol8;
0778   cfgVol8.measurementCounter.addCounter({GeometryIdentifier{}.withVolume(8)},
0779                                         2);
0780   TrackSelector selectorVol8{cfgVol8};
0781 
0782   // Previous trackVol7 has no measurements in volume 8
0783   BOOST_CHECK(!selectorVol8.isValidTrack(trackVol7));
0784 
0785   auto trackVol8 = makeTrack();
0786   BOOST_CHECK(!selectorVol8.isValidTrack(trackVol8));
0787 
0788   addMeasurement(trackVol8, vol8_lay8_sen1);
0789   BOOST_CHECK(!selectorVol8.isValidTrack(trackVol8));
0790   addMeasurement(trackVol8, vol8_lay8_sen2);
0791   BOOST_CHECK(selectorVol8.isValidTrack(trackVol8));
0792   addMeasurement(trackVol8, vol8_lay9_sen1);
0793   BOOST_CHECK(selectorVol8.isValidTrack(trackVol8));
0794 
0795   TrackSelector::Config cfgVol7Lay5;
0796   cfgVol7Lay5.measurementCounter.addCounter(
0797       {GeometryIdentifier{}.withVolume(7).withLayer(5)}, 2);
0798   TrackSelector selectorVol7Lay5{cfgVol7Lay5};
0799 
0800   // Only one hit on volume 7 layer 5
0801   BOOST_CHECK(!selectorVol7Lay5.isValidTrack(trackVol7));
0802   addMeasurement(trackVol7, vol7_lay5_sen12);
0803   BOOST_CHECK(selectorVol7Lay5.isValidTrack(trackVol7));
0804 
0805   // Check requirement on volume 7 OR 8
0806   TrackSelector::Config cfgVol7Or8;
0807   cfgVol7Or8.measurementCounter.addCounter(
0808       {GeometryIdentifier{}.withVolume(7), GeometryIdentifier{}.withVolume(8)},
0809       4);
0810   TrackSelector selectorVol7Or8{cfgVol7Or8};
0811 
0812   // threshold is 4
0813   // this track has enough hits in volume 7 only
0814   BOOST_CHECK(selectorVol7Or8.isValidTrack(trackVol7));
0815   // this track does not have enough hits in volume 8 only
0816   BOOST_CHECK(!selectorVol7Or8.isValidTrack(trackVol8));
0817 
0818   // add 1 hit in volume 7 to push it over the threshold
0819   addMeasurement(trackVol8, vol7_lay3_sen8);
0820   // now it passes
0821   BOOST_CHECK(selectorVol7Or8.isValidTrack(trackVol8));
0822 
0823   TrackSelector::Config cfgVol7And8;
0824   cfgVol7And8.measurementCounter.addCounter(
0825       {GeometryIdentifier{}.withVolume(7)}, 4);
0826   cfgVol7And8.measurementCounter.addCounter(
0827       {GeometryIdentifier{}.withVolume(8)}, 2);
0828   TrackSelector selectorVol7And8{cfgVol7And8};
0829 
0830   // this track has enough hits in vol 7 but not enough in vol 8
0831   BOOST_CHECK(!selectorVol7And8.isValidTrack(trackVol7));
0832 
0833   addMeasurement(trackVol7, vol8_lay8_sen1);
0834   addMeasurement(trackVol7, vol8_lay8_sen2);
0835 
0836   BOOST_CHECK(selectorVol7And8.isValidTrack(trackVol7));
0837 }
0838 
0839 BOOST_AUTO_TEST_SUITE_END()
0840 
0841 }  // namespace ActsTests