Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:11:05

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/EventData/TrackProxyConcept.hpp"
0012 #include "Acts/EventData/TrackStateType.hpp"
0013 #include "Acts/Geometry/GeometryHierarchyMap.hpp"
0014 #include "Acts/Geometry/GeometryIdentifier.hpp"
0015 #include "Acts/Utilities/AngleHelpers.hpp"
0016 
0017 #include <cmath>
0018 #include <functional>
0019 #include <limits>
0020 #include <ostream>
0021 #include <vector>
0022 
0023 #include <boost/container/small_vector.hpp>
0024 
0025 namespace Acts {
0026 
0027 /// Class which performs filtering of tracks. It accepts an input and an output
0028 /// track container and uses the built-in copy facility to copy tracks into the
0029 /// output container.
0030 class TrackSelector {
0031   static constexpr double inf = std::numeric_limits<double>::infinity();
0032 
0033  public:
0034   struct MeasurementCounter {
0035     // Combination of a geometry hierarchy map and a minimum hit count
0036     using CounterElement =
0037         std::pair<GeometryHierarchyMap<unsigned int>, unsigned int>;
0038 
0039     boost::container::small_vector<CounterElement, 4> counters;
0040 
0041     template <TrackProxyConcept track_proxy_t>
0042     bool isValidTrack(const track_proxy_t& track) const;
0043 
0044     void addCounter(const std::vector<GeometryIdentifier>& identifiers,
0045                     unsigned int threshold) {
0046       std::vector<GeometryHierarchyMap<unsigned int>::InputElement> elements;
0047       for (const auto& id : identifiers) {
0048         elements.emplace_back(id, 0);
0049       }
0050       counters.emplace_back(std::move(elements), threshold);
0051     }
0052   };
0053 
0054   /// Configuration of a set of cuts for a single eta bin
0055   /// Default construction yields a set of cuts that accepts everything.
0056   struct Config {
0057     // Minimum/maximum local positions.
0058     double loc0Min = -inf;
0059     double loc0Max = inf;
0060     double loc1Min = -inf;
0061     double loc1Max = inf;
0062     // Minimum/maximum track time.
0063     double timeMin = -inf;
0064     double timeMax = inf;
0065     // Direction cuts.
0066     double phiMin = -inf;
0067     double phiMax = inf;
0068     double etaMin = -inf;
0069     double etaMax = inf;
0070     double absEtaMin = 0.0;
0071     double absEtaMax = inf;
0072     // Momentum cuts.
0073     double ptMin = 0.0;
0074     double ptMax = inf;
0075 
0076     std::size_t minMeasurements = 0;
0077     std::size_t maxHoles = std::numeric_limits<std::size_t>::max();
0078     std::size_t maxOutliers = std::numeric_limits<std::size_t>::max();
0079     std::size_t maxHolesAndOutliers = std::numeric_limits<std::size_t>::max();
0080     std::size_t maxSharedHits = std::numeric_limits<std::size_t>::max();
0081     double maxChi2 = inf;
0082 
0083     /// Whether a reference surface is required for the track
0084     /// If false, the parameter cuts are not evaluated
0085     bool requireReferenceSurface = true;
0086 
0087     // Defaults to: no cut
0088     MeasurementCounter measurementCounter;
0089 
0090     // Helper factory functions to produce a populated config object more
0091     // conveniently
0092 
0093     /// Set loc0 acceptance range
0094     /// @param min Minimum value
0095     /// @param max Maximum value
0096     /// @return Reference to this object
0097     Config& loc0(double min, double max);
0098 
0099     /// Set loc1 acceptance range
0100     /// @param min Minimum value
0101     /// @param max Maximum value
0102     /// @return Reference to this object
0103     Config& loc1(double min, double max);
0104 
0105     /// Set time acceptance range
0106     /// @param min Minimum value
0107     /// @param max Maximum value
0108     /// @return Reference to this object
0109     Config& time(double min, double max);
0110 
0111     /// Set phi acceptance range
0112     /// @param min Minimum value
0113     /// @param max Maximum value
0114     /// @return Reference to this object
0115     Config& phi(double min, double max);
0116 
0117     /// Set the eta acceptance range
0118     /// @param min Minimum value
0119     /// @param max Maximum value
0120     /// @return Reference to this object
0121     Config& eta(double min, double max);
0122 
0123     /// Set the absolute eta acceptance range
0124     /// @param min Minimum value
0125     /// @param max Maximum value
0126     /// @return Reference to this object
0127     Config& absEta(double min, double max);
0128 
0129     /// Set the pt acceptance range
0130     /// @param min Minimum value
0131     /// @param max Maximum value
0132     /// @return Reference to this object
0133     Config& pt(double min, double max);
0134 
0135     /// Print this set of cuts to an output stream
0136     /// @param os Output stream
0137     /// @param cuts Cuts to print
0138     /// @return Reference to the output stream
0139     friend std::ostream& operator<<(std::ostream& os, const Config& cuts);
0140   };
0141 
0142   /// Main config object for the track selector. Combines a set of cut
0143   /// configurations and corresponding eta bins
0144   struct EtaBinnedConfig {
0145     /// Cut sets for each eta bin
0146     std::vector<Config> cutSets = {};
0147 
0148     /// Eta bin edges for varying cuts by eta
0149     std::vector<double> absEtaEdges = {0, inf};
0150 
0151     /// Get the number of eta bins
0152     /// @return Number of eta bins
0153     std::size_t nEtaBins() const { return absEtaEdges.size() - 1; }
0154 
0155     /// Construct an empty (accepts everything) configuration.
0156     /// Results in a single cut set and one abs eta bin from 0 to infinity.
0157     EtaBinnedConfig() : cutSets{{}} {};
0158 
0159     /// Constructor to create a config object that is not upper-bounded.
0160     /// This is useful to use the "fluent" API to populate the configuration.
0161     /// @param etaMin Minimum eta bin edge
0162     EtaBinnedConfig(double etaMin) : cutSets{}, absEtaEdges{etaMin} {}
0163 
0164     /// Constructor from a vector of eta bin edges. This automatically
0165     /// initializes all the cuts to be the same for all eta and be essentially
0166     /// no-op.
0167     /// @param absEtaEdgesIn is the vector of eta bin edges
0168     EtaBinnedConfig(std::vector<double> absEtaEdgesIn)
0169         : absEtaEdges{std::move(absEtaEdgesIn)} {
0170       cutSets.resize(nEtaBins());
0171     }
0172 
0173     /// Auto-converting constructor from a single cut configuration.
0174     /// Results in a single absolute eta bin from 0 to infinity.
0175     EtaBinnedConfig(Config cutSet) : cutSets{std::move(cutSet)} {}
0176 
0177     /// Add a new eta bin with the given upper bound.
0178     /// @param etaMax Upper bound of the new eta bin
0179     /// @param callback Callback to configure the cuts for this eta bin
0180     /// @return Reference to this object
0181     EtaBinnedConfig& addCuts(double etaMax,
0182                              const std::function<void(Config&)>& callback = {});
0183 
0184     /// Add a new eta bin with an upper bound of +infinity.
0185     /// @param callback Callback to configure the cuts for this eta bin
0186     /// @return Reference to this object
0187     EtaBinnedConfig& addCuts(const std::function<void(Config&)>& callback = {});
0188 
0189     /// Print this configuration to an output stream
0190     /// @param os Output stream
0191     /// @param cfg Configuration to print
0192     /// @return Reference to the output stream
0193     friend std::ostream& operator<<(std::ostream& os,
0194                                     const EtaBinnedConfig& cfg);
0195 
0196     /// Check if the configuration has a bin for a given eta
0197     /// @param eta Eta value
0198     /// @return True if the configuration has a bin for the given eta
0199     bool hasCuts(double eta) const;
0200 
0201     /// Get the index of the eta bin for a given eta.
0202     /// throws an exception if Eta is outside the abs eta bin edges.
0203     /// @param eta Eta value
0204     /// @return Index of the eta bin
0205     std::size_t binIndex(double eta) const;
0206 
0207     /// Get the index of the eta bin for a given eta
0208     /// @param eta Eta value
0209     /// @return Index of the eta bin, or >= nEtaBins() if Eta is outside the abs eta bin edges.
0210     std::size_t binIndexNoCheck(double eta) const;
0211 
0212     /// Get the cuts for a given eta
0213     /// @param eta Eta value
0214     /// @return Cuts for the given eta
0215     const Config& getCuts(double eta) const;
0216   };
0217 
0218   /// Constructor from a single cut config object
0219   /// @param config is the configuration object
0220   TrackSelector(const Config& config);
0221 
0222   /// Constructor from a multi-eta
0223   /// @param config is the configuration object
0224   TrackSelector(const EtaBinnedConfig& config);
0225 
0226   /// Select tracks from an input container and copy them into an output
0227   /// container
0228   /// @tparam input_tracks_t is the type of the input track container
0229   /// @tparam output_tracks_t is the type of the output track container
0230   /// @param inputTracks is the input track container
0231   /// @param outputTracks is the output track container
0232   template <typename input_tracks_t, typename output_tracks_t>
0233   void selectTracks(const input_tracks_t& inputTracks,
0234                     output_tracks_t& outputTracks) const;
0235 
0236   /// Helper function to check if a track is valid
0237   /// @tparam track_proxy_t is the type of the track proxy
0238   /// @param track is the track proxy
0239   /// @return true if the track is valid
0240   template <TrackProxyConcept track_proxy_t>
0241   bool isValidTrack(const track_proxy_t& track) const;
0242 
0243   /// Get readonly access to the config parameters
0244   /// @return the config object
0245   const EtaBinnedConfig& config() const { return m_cfg; }
0246 
0247  private:
0248   EtaBinnedConfig m_cfg;
0249   bool m_isUnbinned = false;
0250 };
0251 
0252 inline TrackSelector::Config& TrackSelector::Config::loc0(double min,
0253                                                           double max) {
0254   loc0Min = min;
0255   loc0Max = max;
0256   return *this;
0257 }
0258 
0259 inline TrackSelector::Config& TrackSelector::Config::loc1(double min,
0260                                                           double max) {
0261   loc1Min = min;
0262   loc1Max = max;
0263   return *this;
0264 }
0265 
0266 inline TrackSelector::Config& TrackSelector::Config::time(double min,
0267                                                           double max) {
0268   timeMin = min;
0269   timeMax = max;
0270   return *this;
0271 }
0272 
0273 inline TrackSelector::Config& TrackSelector::Config::phi(double min,
0274                                                          double max) {
0275   phiMin = min;
0276   phiMax = max;
0277   return *this;
0278 }
0279 
0280 inline TrackSelector::Config& TrackSelector::Config::eta(double min,
0281                                                          double max) {
0282   if (absEtaMin != 0.0 || absEtaMax != inf) {
0283     throw std::invalid_argument(
0284         "Cannot set both eta and absEta cuts in the same cut set");
0285   }
0286   etaMin = min;
0287   etaMax = max;
0288   return *this;
0289 }
0290 
0291 inline TrackSelector::Config& TrackSelector::Config::absEta(double min,
0292                                                             double max) {
0293   if (etaMin != -inf || etaMax != inf) {
0294     throw std::invalid_argument(
0295         "Cannot set both eta and absEta cuts in the same cut set");
0296   }
0297   absEtaMin = min;
0298   absEtaMax = max;
0299   return *this;
0300 }
0301 
0302 inline TrackSelector::Config& TrackSelector::Config::pt(double min,
0303                                                         double max) {
0304   ptMin = min;
0305   ptMax = max;
0306   return *this;
0307 }
0308 
0309 inline std::ostream& operator<<(std::ostream& os,
0310                                 const TrackSelector::Config& cuts) {
0311   // for printing cuts set up with `within`
0312   auto printMinMax = [&](const char* name, const auto& min, const auto& max) {
0313     os << " - " << min << " <= " << name << " < " << max << "\n";
0314   };
0315   // for printing cuts set up with `checkMin`
0316   auto printMin = [&](const char* name, const auto& min) {
0317     os << " - " << min << " <= " << name << "\n";
0318   };
0319   // for printing cuts set up with `checkMax`
0320   auto printMax = [&](const char* name, const auto& max) {
0321     os << " - " << name << " <= " << max << "\n";
0322   };
0323 
0324   printMinMax("loc0", cuts.loc0Min, cuts.loc0Max);
0325   printMinMax("loc1", cuts.loc1Min, cuts.loc1Max);
0326   printMinMax("time", cuts.timeMin, cuts.timeMax);
0327   printMinMax("phi", cuts.phiMin, cuts.phiMax);
0328   printMinMax("eta", cuts.etaMin, cuts.etaMax);
0329   printMinMax("absEta", cuts.absEtaMin, cuts.absEtaMax);
0330   printMinMax("pt", cuts.ptMin, cuts.ptMax);
0331   printMax("nHoles", cuts.maxHoles);
0332   printMax("nOutliers", cuts.maxOutliers);
0333   printMax("nHoles + nOutliers", cuts.maxHolesAndOutliers);
0334   printMax("nSharedHits", cuts.maxSharedHits);
0335   printMax("chi2", cuts.maxChi2);
0336   printMin("nMeasurements", cuts.minMeasurements);
0337   return os;
0338 }
0339 
0340 inline TrackSelector::EtaBinnedConfig& TrackSelector::EtaBinnedConfig::addCuts(
0341     double etaMax, const std::function<void(Config&)>& callback) {
0342   if (etaMax <= absEtaEdges.back()) {
0343     throw std::invalid_argument{
0344         "Abs Eta bin edges must be in increasing order"};
0345   }
0346 
0347   if (etaMax < 0.0) {
0348     throw std::invalid_argument{"Abs Eta bin edges must be positive"};
0349   }
0350 
0351   absEtaEdges.push_back(etaMax);
0352   cutSets.emplace_back();
0353   if (callback) {
0354     callback(cutSets.back());
0355   }
0356   return *this;
0357 }
0358 
0359 inline TrackSelector::EtaBinnedConfig& TrackSelector::EtaBinnedConfig::addCuts(
0360     const std::function<void(Config&)>& callback) {
0361   return addCuts(inf, callback);
0362 }
0363 
0364 inline bool TrackSelector::EtaBinnedConfig::hasCuts(double eta) const {
0365   return std::abs(eta) < absEtaEdges.back();
0366 }
0367 
0368 inline std::size_t TrackSelector::EtaBinnedConfig::binIndex(double eta) const {
0369   std::size_t index = binIndexNoCheck(eta);
0370   if (!(index < nEtaBins())) {
0371     throw std::invalid_argument{"Eta is outside the abs eta bin edges"};
0372   }
0373   return index;
0374 }
0375 
0376 inline std::size_t TrackSelector::EtaBinnedConfig::binIndexNoCheck(
0377     double eta) const {
0378   auto binIt =
0379       std::upper_bound(absEtaEdges.begin(), absEtaEdges.end(), std::abs(eta));
0380   std::size_t index = std::distance(absEtaEdges.begin(), binIt);
0381   if (index == 0) {
0382     index = absEtaEdges.size() + 1;  // positive value to check for underflow
0383   }
0384   return index - 1;
0385 }
0386 
0387 inline const TrackSelector::Config& TrackSelector::EtaBinnedConfig::getCuts(
0388     double eta) const {
0389   return nEtaBins() == 1 ? cutSets[0] : cutSets[binIndex(eta)];
0390 }
0391 
0392 inline std::ostream& operator<<(std::ostream& os,
0393                                 const TrackSelector::EtaBinnedConfig& cfg) {
0394   os << "TrackSelector::EtaBinnedConfig:\n";
0395 
0396   for (std::size_t i = 1; i < cfg.absEtaEdges.size(); i++) {
0397     os << cfg.absEtaEdges[i - 1] << " <= eta < " << cfg.absEtaEdges[i] << "\n";
0398     os << cfg.cutSets[i - 1];
0399   }
0400 
0401   return os;
0402 }
0403 
0404 template <typename input_tracks_t, typename output_tracks_t>
0405 void TrackSelector::selectTracks(const input_tracks_t& inputTracks,
0406                                  output_tracks_t& outputTracks) const {
0407   for (auto track : inputTracks) {
0408     if (!isValidTrack(track)) {
0409       continue;
0410     }
0411     auto destProxy = outputTracks.makeTrack();
0412     destProxy.copyFrom(track, false);
0413     destProxy.tipIndex() = track.tipIndex();
0414   }
0415 }
0416 
0417 template <TrackProxyConcept track_proxy_t>
0418 bool TrackSelector::isValidTrack(const track_proxy_t& track) const {
0419   auto checkMin = [](auto x, auto min) { return min <= x; };
0420   auto checkMax = [](auto x, auto max) { return x <= max; };
0421   auto within = [](double x, double min, double max) {
0422     return (min <= x) && (x < max);
0423   };
0424 
0425   const auto theta = track.theta();
0426 
0427   constexpr double kUnset = -std::numeric_limits<double>::infinity();
0428 
0429   double _eta = kUnset;
0430   double _absEta = kUnset;
0431 
0432   auto absEta = [&]() {
0433     if (_absEta == kUnset) {
0434       _eta = AngleHelpers::etaFromTheta(theta);
0435       _absEta = std::abs(_eta);
0436     }
0437     return _absEta;
0438   };
0439 
0440   const Config* cutsPtr{nullptr};
0441   if (!m_isUnbinned) {
0442     // return false if |eta| is outside its range, or nan.
0443     if (!(absEta() >= m_cfg.absEtaEdges.front() &&
0444           _absEta < m_cfg.absEtaEdges.back())) {
0445       return false;
0446     }
0447     cutsPtr = &m_cfg.getCuts(_eta);
0448   } else {
0449     cutsPtr = &m_cfg.cutSets.front();
0450   }
0451 
0452   const Config& cuts = *cutsPtr;
0453 
0454   auto parameterCuts = [&]() {
0455     return within(track.transverseMomentum(), cuts.ptMin, cuts.ptMax) &&
0456            (!m_isUnbinned ||
0457             (within(absEta(), cuts.absEtaMin, cuts.absEtaMax) &&
0458              within(_eta, cuts.etaMin, cuts.etaMax))) &&
0459            within(track.phi(), cuts.phiMin, cuts.phiMax) &&
0460            within(track.loc0(), cuts.loc0Min, cuts.loc0Max) &&
0461            within(track.loc1(), cuts.loc1Min, cuts.loc1Max) &&
0462            within(track.time(), cuts.timeMin, cuts.timeMax);
0463   };
0464 
0465   auto trackCuts = [&]() {
0466     return checkMin(track.nMeasurements(), cuts.minMeasurements) &&
0467            checkMax(track.nHoles(), cuts.maxHoles) &&
0468            checkMax(track.nOutliers(), cuts.maxOutliers) &&
0469            checkMax(track.nHoles() + track.nOutliers(),
0470                     cuts.maxHolesAndOutliers) &&
0471            checkMax(track.nSharedHits(), cuts.maxSharedHits) &&
0472            checkMax(track.chi2(), cuts.maxChi2) &&
0473            cuts.measurementCounter.isValidTrack(track);
0474   };
0475 
0476   if (cuts.requireReferenceSurface) {
0477     return track.hasReferenceSurface() && parameterCuts() && trackCuts();
0478   } else {
0479     return trackCuts();
0480   }
0481 }
0482 
0483 inline TrackSelector::TrackSelector(
0484     const TrackSelector::EtaBinnedConfig& config)
0485     : m_cfg(config) {
0486   if (m_cfg.cutSets.size() != m_cfg.nEtaBins()) {
0487     throw std::invalid_argument{
0488         "TrackSelector cut / eta bin configuration is inconsistent"};
0489   }
0490 
0491   if (m_cfg.nEtaBins() == 1) {
0492     static const std::vector<double> infVec = {0, inf};
0493     m_isUnbinned =
0494         m_cfg.absEtaEdges == infVec;  // single bin, no eta edges given
0495   }
0496 
0497   if (!m_isUnbinned) {
0498     for (const auto& cuts : m_cfg.cutSets) {
0499       if (cuts.etaMin != -inf || cuts.etaMax != inf || cuts.absEtaMin != 0.0 ||
0500           cuts.absEtaMax != inf) {
0501         throw std::invalid_argument{
0502             "Explicit eta cuts are only valid for single eta bin"};
0503       }
0504     }
0505   }
0506 }
0507 
0508 inline TrackSelector::TrackSelector(const Config& config)
0509     : TrackSelector{EtaBinnedConfig{config}} {}
0510 
0511 template <TrackProxyConcept track_proxy_t>
0512 bool TrackSelector::MeasurementCounter::isValidTrack(
0513     const track_proxy_t& track) const {
0514   // No hit cuts, accept everything
0515   if (counters.empty()) {
0516     return true;
0517   }
0518 
0519   boost::container::small_vector<unsigned int, 4> counterValues;
0520   counterValues.resize(counters.size(), 0);
0521 
0522   for (const auto& ts : track.trackStatesReversed()) {
0523     if (!ts.typeFlags().test(Acts::TrackStateFlag::MeasurementFlag)) {
0524       continue;
0525     }
0526 
0527     const auto geoId = ts.referenceSurface().geometryId();
0528 
0529     for (std::size_t i = 0; i < counters.size(); i++) {
0530       const auto& [counterMap, threshold] = counters[i];
0531       const auto it = counterMap.find(geoId);
0532       if (it == counterMap.end()) {
0533         continue;
0534       }
0535 
0536       counterValues[i]++;
0537     }
0538   }
0539 
0540   for (std::size_t i = 0; i < counters.size(); i++) {
0541     const auto& [counterMap, threshold] = counters[i];
0542     const unsigned int value = counterValues[i];
0543     if (value < threshold) {
0544       return false;
0545     }
0546   }
0547 
0548   return true;
0549 }
0550 }  // namespace Acts