Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:12:53

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