Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-10-22 07:53:29

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