Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
TrackSelector.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file TrackSelector.hpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2023 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #pragma once
10 
11 #include <cmath>
12 #include <functional>
13 #include <limits>
14 #include <ostream>
15 #include <vector>
16 
17 namespace Acts {
18 
23  static constexpr double inf = std::numeric_limits<double>::infinity();
24 
25  public:
28  struct Config {
29  // Minimum/maximum local positions.
30  double loc0Min = -inf;
31  double loc0Max = inf;
32  double loc1Min = -inf;
33  double loc1Max = inf;
34  // Minimum/maximum track time.
35  double timeMin = -inf;
36  double timeMax = inf;
37  // Direction cuts.
38  double phiMin = -inf;
39  double phiMax = inf;
40  double etaMin = -inf;
41  double etaMax = inf;
42  double absEtaMin = 0.0;
43  double absEtaMax = inf;
44  // Momentum cuts.
45  double ptMin = 0.0;
46  double ptMax = inf;
47 
48  std::size_t minMeasurements = 0;
49 
50  // Helper factory functions to produce a populated config object more
51  // conveniently
52 
57  Config& loc0(double min, double max);
58 
63  Config& loc1(double min, double max);
64 
69  Config& time(double min, double max);
70 
75  Config& phi(double min, double max);
76 
81  Config& eta(double min, double max);
82 
87  Config& absEta(double min, double max);
88 
93  Config& pt(double min, double max);
94 
99  friend std::ostream& operator<<(std::ostream& os, const Config& cuts);
100  };
101 
106  std::vector<Config> cutSets = {};
107 
109  std::vector<double> absEtaEdges = {};
110 
113  std::size_t nEtaBins() const { return absEtaEdges.size() - 1; }
114 
118 
122  EtaBinnedConfig(double etaMin) : cutSets{}, absEtaEdges{etaMin} {}
123 
128  EtaBinnedConfig(std::vector<double> absEtaEdgesIn)
129  : absEtaEdges{std::move(absEtaEdgesIn)} {
130  cutSets.resize(absEtaEdges.size() - 1);
131  }
132 
135  EtaBinnedConfig(Config cutSet) : cutSets{cutSet}, absEtaEdges{{0, inf}} {}
136 
141  EtaBinnedConfig& addCuts(double etaMax,
142  const std::function<void(Config&)>& callback = {});
143 
147  EtaBinnedConfig& addCuts(const std::function<void(Config&)>& callback = {});
148 
153  friend std::ostream& operator<<(std::ostream& os,
154  const EtaBinnedConfig& cfg);
155 
159  std::size_t binIndex(double eta) const;
160 
164  const Config& getCuts(double eta) const;
165  };
166 
169  TrackSelector(const Config& config);
170 
173  TrackSelector(const EtaBinnedConfig& config);
174 
181  template <typename input_tracks_t, typename output_tracks_t>
182  void selectTracks(const input_tracks_t& inputTracks,
183  output_tracks_t& outputTracks) const;
184 
189  template <typename track_proxy_t>
190  bool isValidTrack(const track_proxy_t& track) const;
191 
194  const EtaBinnedConfig& config() const { return m_cfg; }
195 
196  private:
200 };
201 
203  double max) {
204  loc0Min = min;
205  loc0Max = max;
206  return *this;
207 }
208 
210  double max) {
211  loc1Min = min;
212  loc1Max = max;
213  return *this;
214 }
215 
217  double max) {
218  timeMin = min;
219  timeMax = max;
220  return *this;
221 }
222 
224  double max) {
225  phiMin = min;
226  phiMax = max;
227  return *this;
228 }
229 
231  double max) {
232  if (absEtaMin != 0.0 || absEtaMax != inf) {
233  throw std::invalid_argument(
234  "Cannot set both eta and absEta cuts in the same cut set");
235  }
236  etaMin = min;
237  etaMax = max;
238  return *this;
239 }
240 
242  double max) {
243  if (etaMin != -inf || etaMax != inf) {
244  throw std::invalid_argument(
245  "Cannot set both eta and absEta cuts in the same cut set");
246  }
247  absEtaMin = min;
248  absEtaMax = max;
249  return *this;
250 }
251 
253  double max) {
254  ptMin = min;
255  ptMax = max;
256  return *this;
257 }
258 
259 inline std::ostream& operator<<(std::ostream& os,
260  const TrackSelector::Config& cuts) {
261  auto print = [&](const char* name, const auto& min, const auto& max) {
262  os << " - " << min << " <= " << name << " < " << max << "\n";
263  };
264 
265  print("loc0", cuts.loc0Min, cuts.loc0Max);
266  print("loc1", cuts.loc1Min, cuts.loc1Max);
267  print("time", cuts.timeMin, cuts.timeMax);
268  print("phi", cuts.phiMin, cuts.phiMax);
269  print("eta", cuts.etaMin, cuts.etaMax);
270  print("absEta", cuts.absEtaMin, cuts.absEtaMax);
271  print("pt", cuts.ptMin, cuts.ptMax);
272  os << " - " << cuts.minMeasurements << " <= nMeasurements\n";
273 
274  return os;
275 }
276 
278  double etaMax, const std::function<void(Config&)>& callback) {
279  if (etaMax <= absEtaEdges.back()) {
280  throw std::invalid_argument{
281  "Abs Eta bin edges must be in increasing order"};
282  }
283 
284  if (etaMax < 0.0) {
285  throw std::invalid_argument{"Abs Eta bin edges must be positive"};
286  }
287 
288  absEtaEdges.push_back(etaMax);
289  cutSets.emplace_back();
290  if (callback) {
291  callback(cutSets.back());
292  }
293  return *this;
294 }
295 
297  const std::function<void(Config&)>& callback) {
298  return addCuts(inf, callback);
299 }
300 
301 inline std::size_t TrackSelector::EtaBinnedConfig::binIndex(double eta) const {
302  if (std::abs(eta) >= absEtaEdges.back()) {
303  throw std::invalid_argument{"Eta is outside the abs eta bin edges"};
304  }
305 
306  auto binIt =
307  std::upper_bound(absEtaEdges.begin(), absEtaEdges.end(), std::abs(eta));
308  std::size_t index = std::distance(absEtaEdges.begin(), binIt) - 1;
309  return index;
310 }
311 
313  double eta) const {
314  return nEtaBins() == 1 ? cutSets[0] : cutSets[binIndex(eta)];
315 }
316 
317 inline std::ostream& operator<<(std::ostream& os,
319  os << "TrackSelector::EtaBinnedConfig:\n";
320 
321  for (std::size_t i = 1; i < cfg.absEtaEdges.size(); i++) {
322  os << cfg.absEtaEdges[i - 1] << " <= eta < " << cfg.absEtaEdges[i] << "\n";
323  os << cfg.cutSets[i - 1];
324  }
325 
326  return os;
327 }
328 
329 template <typename input_tracks_t, typename output_tracks_t>
330 void TrackSelector::selectTracks(const input_tracks_t& inputTracks,
331  output_tracks_t& outputTracks) const {
332  for (auto track : inputTracks) {
333  if (!isValidTrack(track)) {
334  continue;
335  }
336  auto destProxy = outputTracks.getTrack(outputTracks.addTrack());
337  destProxy.copyFrom(track, false);
338  destProxy.tipIndex() = track.tipIndex();
339  }
340 }
341 
342 template <typename track_proxy_t>
343 bool TrackSelector::isValidTrack(const track_proxy_t& track) const {
344  auto checkMin = [](auto x, auto min) { return min <= x; };
345  auto within = [](double x, double min, double max) {
346  return (min <= x) and (x < max);
347  };
348 
349  const auto theta = track.theta();
350 
351  constexpr double kUnset = -std::numeric_limits<double>::infinity();
352 
353  double _eta = kUnset;
354  double _absEta = kUnset;
355 
356  auto absEta = [&]() {
357  if (_absEta == kUnset) {
358  _eta = -std::log(std::tan(theta / 2));
359  _absEta = std::abs(_eta);
360  }
361  return _absEta;
362  };
363 
364  const Config* cutsPtr{nullptr};
365  if (!m_isUnbinned) {
366  if (absEta() < m_cfg.absEtaEdges.front() ||
367  _absEta >= m_cfg.absEtaEdges.back()) {
368  return false;
369  }
370  cutsPtr = &m_cfg.getCuts(_eta);
371  } else {
372  cutsPtr = &m_cfg.cutSets.front();
373  }
374 
375  const Config& cuts = *cutsPtr;
376 
377  return within(track.transverseMomentum(), cuts.ptMin, cuts.ptMax) and
378  (m_noEtaCuts || (within(absEta(), cuts.absEtaMin, cuts.absEtaMax) and
379  within(_eta, cuts.etaMin, cuts.etaMax))) and
380  within(track.phi(), cuts.phiMin, cuts.phiMax) and
381  within(track.loc0(), cuts.loc0Min, cuts.loc0Max) and
382  within(track.loc1(), cuts.loc1Min, cuts.loc1Max) and
383  within(track.time(), cuts.timeMin, cuts.timeMax) and
384  checkMin(track.nMeasurements(), cuts.minMeasurements);
385 }
386 
389  : m_cfg(config) {
390  if (m_cfg.cutSets.size() != m_cfg.nEtaBins()) {
391  throw std::invalid_argument{
392  "TrackSelector cut / eta bin configuration is inconsistent"};
393  }
394 
395  m_isUnbinned = false;
396  if (m_cfg.nEtaBins() == 1) {
397  static const std::vector<double> infVec = {0, inf};
398  bool limitEta = m_cfg.absEtaEdges != infVec;
399  m_isUnbinned = !limitEta; // single bin, no eta edges given
400 
401  const Config& cuts = m_cfg.cutSets[0];
402 
403  if (limitEta && (cuts.etaMin != -inf || cuts.etaMax != inf ||
404  cuts.absEtaMin != 0.0 || cuts.absEtaMax != inf)) {
405  throw std::invalid_argument{
406  "Explicit eta cuts are only valid for single eta bin"};
407  }
408  }
409 
411  for (const auto& cuts : m_cfg.cutSets) {
412  if (cuts.etaMin != -inf || cuts.etaMax != inf || cuts.absEtaMin != 0.0 ||
413  cuts.absEtaMax != inf) {
414  m_noEtaCuts = false;
415  }
416  }
417 }
418 
420  : TrackSelector{EtaBinnedConfig{config}} {}
421 
422 } // namespace Acts