File indexing completed on 2025-01-18 09:11:05
0001
0002
0003
0004
0005
0006
0007
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
0028
0029
0030 class TrackSelector {
0031 static constexpr double inf = std::numeric_limits<double>::infinity();
0032
0033 public:
0034 struct MeasurementCounter {
0035
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
0055
0056 struct Config {
0057
0058 double loc0Min = -inf;
0059 double loc0Max = inf;
0060 double loc1Min = -inf;
0061 double loc1Max = inf;
0062
0063 double timeMin = -inf;
0064 double timeMax = inf;
0065
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
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
0084
0085 bool requireReferenceSurface = true;
0086
0087
0088 MeasurementCounter measurementCounter;
0089
0090
0091
0092
0093
0094
0095
0096
0097 Config& loc0(double min, double max);
0098
0099
0100
0101
0102
0103 Config& loc1(double min, double max);
0104
0105
0106
0107
0108
0109 Config& time(double min, double max);
0110
0111
0112
0113
0114
0115 Config& phi(double min, double max);
0116
0117
0118
0119
0120
0121 Config& eta(double min, double max);
0122
0123
0124
0125
0126
0127 Config& absEta(double min, double max);
0128
0129
0130
0131
0132
0133 Config& pt(double min, double max);
0134
0135
0136
0137
0138
0139 friend std::ostream& operator<<(std::ostream& os, const Config& cuts);
0140 };
0141
0142
0143
0144 struct EtaBinnedConfig {
0145
0146 std::vector<Config> cutSets = {};
0147
0148
0149 std::vector<double> absEtaEdges = {0, inf};
0150
0151
0152
0153 std::size_t nEtaBins() const { return absEtaEdges.size() - 1; }
0154
0155
0156
0157 EtaBinnedConfig() : cutSets{{}} {};
0158
0159
0160
0161
0162 EtaBinnedConfig(double etaMin) : cutSets{}, absEtaEdges{etaMin} {}
0163
0164
0165
0166
0167
0168 EtaBinnedConfig(std::vector<double> absEtaEdgesIn)
0169 : absEtaEdges{std::move(absEtaEdgesIn)} {
0170 cutSets.resize(nEtaBins());
0171 }
0172
0173
0174
0175 EtaBinnedConfig(Config cutSet) : cutSets{std::move(cutSet)} {}
0176
0177
0178
0179
0180
0181 EtaBinnedConfig& addCuts(double etaMax,
0182 const std::function<void(Config&)>& callback = {});
0183
0184
0185
0186
0187 EtaBinnedConfig& addCuts(const std::function<void(Config&)>& callback = {});
0188
0189
0190
0191
0192
0193 friend std::ostream& operator<<(std::ostream& os,
0194 const EtaBinnedConfig& cfg);
0195
0196
0197
0198
0199 bool hasCuts(double eta) const;
0200
0201
0202
0203
0204
0205 std::size_t binIndex(double eta) const;
0206
0207
0208
0209
0210 std::size_t binIndexNoCheck(double eta) const;
0211
0212
0213
0214
0215 const Config& getCuts(double eta) const;
0216 };
0217
0218
0219
0220 TrackSelector(const Config& config);
0221
0222
0223
0224 TrackSelector(const EtaBinnedConfig& config);
0225
0226
0227
0228
0229
0230
0231
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
0237
0238
0239
0240 template <TrackProxyConcept track_proxy_t>
0241 bool isValidTrack(const track_proxy_t& track) const;
0242
0243
0244
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
0312 auto printMinMax = [&](const char* name, const auto& min, const auto& max) {
0313 os << " - " << min << " <= " << name << " < " << max << "\n";
0314 };
0315
0316 auto printMin = [&](const char* name, const auto& min) {
0317 os << " - " << min << " <= " << name << "\n";
0318 };
0319
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;
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
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;
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
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 }