CMS 3D CMS Logo

KFFittingSmoother.h
Go to the documentation of this file.
1 
12 
15 
20 
21 namespace {
22 
23  struct KFFittingSmootherParam {
24  explicit KFFittingSmootherParam(const edm::ParameterSet& conf)
25  : theEstimateCut(conf.getParameter<double>("EstimateCut")),
26  theMaxFractionOutliers(conf.getParameter<double>("MaxFractionOutliers")),
27  theMaxNumberOfOutliers(conf.getParameter<int>("MaxNumberOfOutliers")),
28  theNoOutliersBeginEnd(conf.getParameter<bool>("NoOutliersBeginEnd")),
29  theMinDof(conf.getParameter<int>("MinDof")),
30  theMinNumberOfHits(conf.getParameter<int>("MinNumberOfHits")),
31  rejectTracksFlag(conf.getParameter<bool>("RejectTracks")),
32  breakTrajWith2ConsecutiveMissing(conf.getParameter<bool>("BreakTrajWith2ConsecutiveMissing")),
33  noInvalidHitsBeginEnd(conf.getParameter<bool>("NoInvalidHitsBeginEnd")) {}
34 
35  double theEstimateCut;
36 
37  float theMaxFractionOutliers;
38  int theMaxNumberOfOutliers;
39  bool theNoOutliersBeginEnd;
40  int theMinDof;
41 
42  int theMinNumberOfHits;
43  bool rejectTracksFlag;
44  bool breakTrajWith2ConsecutiveMissing;
45  bool noInvalidHitsBeginEnd;
46  };
47 
48  class KFFittingSmoother final : public TrajectoryFitter, private KFFittingSmootherParam {
49  public:
50  ~KFFittingSmoother() override {}
51 
52  KFFittingSmoother(const TrajectoryFitter& aFitter,
53  const TrajectorySmoother& aSmoother,
54  const edm::ParameterSet& conf)
55  : KFFittingSmootherParam(conf), theFitter(aFitter.clone()), theSmoother(aSmoother.clone()) {}
56 
57  private:
59  desc.add<double>("EstimateCut", -1);
60  desc.add<double>("MaxFractionOutliers", 0.3);
61  desc.add<int>("MaxNumberOfOutliers", 3);
62  desc.add<int>("MinDof", 2);
63  desc.add<bool>("NoOutliersBeginEnd", false);
64  desc.add<int>("MinNumberOfHits", 5);
65  desc.add<bool>("RejectTracks", true);
66  desc.add<bool>("BreakTrajWith2ConsecutiveMissing", true);
67  desc.add<bool>("NoInvalidHitsBeginEnd", true);
68  desc.add<double>("LogPixelProbabilityCut", 0);
69  }
70 
71  Trajectory fitOne(const Trajectory& t, fitType type) const override;
72  Trajectory fitOne(const TrajectorySeed& aSeed,
73  const RecHitContainer& hits,
74  const TrajectoryStateOnSurface& firstPredTsos,
75  fitType type) const override;
76  Trajectory fitOne(const TrajectorySeed& aSeed, const RecHitContainer& hits, fitType type) const override;
77 
78  const TrajectoryFitter* fitter() const { return theFitter.get(); }
79  const TrajectorySmoother* smoother() const { return theSmoother.get(); }
80 
81  std::unique_ptr<TrajectoryFitter> clone() const override {
82  return std::unique_ptr<TrajectoryFitter>(new KFFittingSmoother(*theFitter, *theSmoother, *this));
83  }
84 
85  void setHitCloner(TkCloner const* hc) override {
86  theFitter->setHitCloner(hc);
87  theSmoother->setHitCloner(hc);
88  }
89 
90  KFFittingSmoother(const TrajectoryFitter& aFitter,
91  const TrajectorySmoother& aSmoother,
92  KFFittingSmootherParam const& other)
93  : KFFittingSmootherParam(other), theFitter(aFitter.clone()), theSmoother(aSmoother.clone()) {}
94 
95  Trajectory smoothingStep(Trajectory&& fitted) const {
96  if (theEstimateCut > 0) {
97  // remove "outlier" at the end of Traj
98  while (
99  !fitted.empty() && fitted.foundHits() >= theMinNumberOfHits &&
100  (!fitted.lastMeasurement().recHitR().isValid() || (fitted.lastMeasurement().recHitR().det() != nullptr &&
101  fitted.lastMeasurement().estimate() > theEstimateCut)))
102  fitted.pop();
103  if (fitted.foundHits() < theMinNumberOfHits)
104  return Trajectory();
105  }
106  return theSmoother->trajectory(fitted);
107  }
108 
109  private:
110  const std::unique_ptr<TrajectoryFitter> theFitter;
111  const std::unique_ptr<TrajectorySmoother> theSmoother;
112 
114  static bool checkForNans(const Trajectory& theTraj);
115 
116  friend class KFFittingSmootherESProducer;
117  };
118 
119  // #define VI_DEBUG
120 
121 #ifdef VI_DEBUG
122 #define DPRINT(x) std::cout << x << ": "
123 #define PRINT std::cout
124 #else
125 #define DPRINT(x) LogTrace(x)
126 #define PRINT LogTrace("")
127 #endif
128 
129  Trajectory KFFittingSmoother::fitOne(const Trajectory& t, fitType type) const {
130  if (!t.isValid())
131  return Trajectory();
132  return smoothingStep(theFitter->fitOne(t, type));
133  }
134 
135  bool KFFittingSmoother::checkForNans(const Trajectory& theTraj) {
136  if (edm::isNotFinite(theTraj.chiSquared()))
137  return false;
138  auto const& vtm = theTraj.measurements();
139  for (auto const& tm : vtm) {
140  if (edm::isNotFinite(tm.estimate()))
141  return false;
142  auto const& v = tm.updatedState().localParameters().vector();
143  for (int i = 0; i < 5; ++i)
144  if (edm::isNotFinite(v[i]))
145  return false;
146  if (!tm.updatedState().curvilinearError().posDef())
147  return false; //not a NaN by itself, but will lead to one
148  auto const& m = tm.updatedState().curvilinearError().matrix();
149  for (int i = 0; i < 5; ++i)
150  for (int j = 0; j < (i + 1); ++j)
151  if (edm::isNotFinite(m(i, j)))
152  return false;
153  }
154  return true;
155  }
156 
157  namespace {
158  inline void print(const std::string& header, const TrajectoryStateOnSurface& tsos) {
159  DPRINT("TrackFitters") << header << tsos.globalPosition().perp() << ' ' << tsos.globalPosition().z() << ' '
160  << 1. / tsos.signedInverseMomentum() << ' ' << 1. / tsos.transverseCurvature() << ' '
161  << tsos.globalMomentum().eta() << std::endl;
162  }
163  } // namespace
164 
166  const RecHitContainer& hits,
167  const TrajectoryStateOnSurface& firstPredTsos,
168  fitType type) const {
169  LogDebug("TrackFitters") << "In KFFittingSmoother::fit";
170 
171  print("firstPred ", firstPredTsos);
172 
173  if (hits.empty())
174  return Trajectory();
175 
176  RecHitContainer myHits = hits;
177  Trajectory tmp_first;
178 
179  //call the fitter
180  Trajectory smoothed = smoothingStep(theFitter->fitOne(aSeed, myHits, firstPredTsos));
181 
182  do {
183 #ifdef EDM_ML_DEBUG
184  //if no outliers the fit is done only once
185  for (unsigned int j = 0; j < myHits.size(); j++) {
186  if (myHits[j]->det())
187  LogTrace("TrackFitters") << "hit #:" << j + 1 << " rawId=" << myHits[j]->det()->geographicalId().rawId()
188  << " validity=" << myHits[j]->isValid();
189  else
190  LogTrace("TrackFitters") << "hit #:" << j + 1 << " Hit with no Det information";
191  }
192 #endif
193 
194 #if defined(VI_DEBUG) || defined(EDM_ML_DEBUG)
195  if (smoothed.isValid()) {
196  print("first state ", smoothed.firstMeasurement().updatedState());
197  print("last state ", smoothed.lastMeasurement().updatedState());
198  }
199 #endif
200 
201  bool hasNaN = false;
202  if (!smoothed.isValid() || (hasNaN = !checkForNans(smoothed)) || (smoothed.foundHits() < theMinNumberOfHits)) {
203  if (hasNaN)
204  edm::LogWarning("TrackNaN") << "Track has NaN or the cov is not pos-definite";
205  if (smoothed.foundHits() < theMinNumberOfHits)
206  LogTrace("TrackFitters") << "smoothed.foundHits()<theMinNumberOfHits";
207  DPRINT("TrackFitters") << "smoothed invalid => trajectory rejected with nhits/chi2 " << smoothed.foundHits()
208  << '/' << smoothed.chiSquared() << "\n";
209  if (rejectTracksFlag) {
210  return Trajectory();
211  } else {
212  std::swap(smoothed, tmp_first); // if first attempt, tmp_first would be invalid anyway
213  DPRINT("TrackFitters") << "smoothed invalid => returning orignal trajectory with nhits/chi2 "
214  << smoothed.foundHits() << '/' << smoothed.chiSquared() << "\n";
215  }
216  break;
217  }
218 #ifdef EDM_ML_DEBUG
219  else {
220  LogTrace("TrackFitters") << "dump hits after smoothing";
221  Trajectory::DataContainer meas = smoothed.measurements();
222  for (Trajectory::DataContainer::iterator it = meas.begin(); it != meas.end(); ++it) {
223  LogTrace("TrackFitters") << "hit #" << meas.end() - it - 1 << " validity=" << it->recHit()->isValid()
224  << " det=" << it->recHit()->geographicalId().rawId();
225  }
226  }
227 #endif
228 
229  if (myHits.size() != smoothed.measurements().size())
230  DPRINT("TrackFitters") << "lost hits. before/after: " << myHits.size() << '/' << smoothed.measurements().size()
231  << "\n";
232 
233  if (theEstimateCut <= 0)
234  break;
235 
236  // Check if there are outliers
237 
238  auto msize = smoothed.measurements().size();
239  declareDynArray(unsigned int, msize, bad);
240  unsigned int nbad = 0;
241  unsigned int ind = 0;
242  unsigned int lastValid = smoothed.measurements().size();
243  for (auto const& tm : smoothed.measurements()) {
244  if (tm.estimate() > theEstimateCut &&
245  tm.recHitR().det() != nullptr // do not consider outliers constraints and other special "hits"
246  )
247  bad[nbad++] = ind;
248  if (ind < lastValid && tm.recHitR().det() != nullptr && tm.recHitR().isValid())
249  lastValid = ind;
250  ++ind;
251  }
252 
253  if (0 == nbad)
254  break;
255 
256  DPRINT("TrackFitters") << "size/found/outliers list " << smoothed.measurements().size() << '/'
257  << smoothed.foundHits() << ' ' << nbad << ": ";
258  for (auto i = 0U; i < nbad; ++i)
259  PRINT << bad[i] << ',';
260  PRINT << std::endl;
261 
262  if (
263  // (smoothed.foundHits() == theMinNumberOfHits) ||
264  int(nbad) > theMaxNumberOfOutliers || float(nbad) > theMaxFractionOutliers * float(smoothed.foundHits())) {
265  DPRINT("TrackFitters") << "smoothed low quality => trajectory with nhits/chi2 " << smoothed.foundHits() << '/'
266  << smoothed.chiSquared() << "\n";
267  PRINT << "try to remove " << lastValid << std::endl;
268  nbad = 0; // try to short the traj... (below lastValid will be added)
269 
270  // do not perform outliers rejection if track is already low quality
271  /*
272  if ( rejectTracksFlag && (smoothed.chiSquared() > theEstimateCut*smoothed.ndof()) ) {
273  DPRINT("TrackFitters") << "smoothed low quality => trajectory rejected with nhits/chi2 " << smoothed.foundHits() << '/' << smoothed.chiSquared() << "\n";
274  return Trajectory();
275  } else {
276  DPRINT("TrackFitters") << "smoothed low quality => return original trajectory with nhits/chi2 " << smoothed.foundHits() << '/' << smoothed.chiSquared() << "\n";
277  }
278  break;
279  */
280  }
281 
282  // always add last valid hit as outlier candidate
283  bad[nbad++] = lastValid;
284 
285  // if ( (smoothed.ndof()<theMinDof) | ) break;
286 
287  assert(smoothed.measurements().size() <= myHits.size());
288 
289  myHits.resize(smoothed.measurements().size()); // hits are only removed from the back...
290 
291  assert(smoothed.measurements().size() == myHits.size());
292 
293  declareDynArray(Trajectory, nbad, smoothedCand);
294 
295  auto NHits = myHits.size();
297 
298  auto loc = nbad;
299  for (auto i = 0U; i < nbad; ++i) {
300  auto j = NHits - bad[i] - 1;
301  assert(myHits[j]->geographicalId() == smoothed.measurements()[bad[i]].recHitR().geographicalId());
302  auto removedHit = myHits[j];
303  myHits[j] = std::make_shared<InvalidTrackingRecHit>(*removedHit->det(), TrackingRecHit::missing);
304  smoothedCand[i] = smoothingStep(theFitter->fitOne(aSeed, myHits, firstPredTsos));
305  myHits[j] = removedHit;
306  if (smoothedCand[i].isValid() && smoothedCand[i].chiSquared() < minChi2) {
307  minChi2 = smoothedCand[i].chiSquared();
308  loc = i;
309  }
310  }
311 
312  if (loc == nbad) {
313  DPRINT("TrackFitters") << "New trajectories all invalid"
314  << "\n";
315  return Trajectory();
316  }
317 
318  DPRINT("TrackFitters") << "outlier removed " << bad[loc] << '/' << minChi2 << " was " << smoothed.chiSquared()
319  << "\n";
320 
321  if (minChi2 > smoothed.chiSquared()) {
322  DPRINT("TrackFitters") << "removing outlier makes chi2 worse " << minChi2 << '/' << smoothed.chiSquared()
323  << "\nOri: ";
324  for (auto const& tm : smoothed.measurements())
325  PRINT << tm.recHitR().geographicalId() << '/' << tm.estimate() << ' ';
326  PRINT << "\nNew: ";
327  for (auto const& tm : smoothedCand[loc].measurements())
328  PRINT << tm.recHitR().geographicalId() << '/' << tm.estimate() << ' ';
329  PRINT << "\n";
330 
331  // return Trajectory();
332  // break;
333  }
334 
335  std::swap(smoothed, tmp_first);
336  myHits[NHits - bad[loc] - 1] =
337  std::make_shared<InvalidTrackingRecHit>(*myHits[NHits - bad[loc] - 1]->det(), TrackingRecHit::missing);
338  std::swap(smoothed, smoothedCand[loc]);
339  // firstTry=false;
340 
341  DPRINT("TrackFitters") << "new trajectory with nhits/chi2 " << smoothed.foundHits() << '/'
342  << smoothed.chiSquared() << "\n";
343 
344  // Look if there are two consecutive invalid hits FIXME: take into account split matched hits!!!
345  if (breakTrajWith2ConsecutiveMissing) {
346  unsigned int firstinvalid = myHits.size();
347  for (unsigned int j = 0; j < myHits.size() - 1; ++j) {
348  if (((myHits[j]->type() == TrackingRecHit::missing) && (myHits[j]->geographicalId().rawId() != 0)) &&
349  ((myHits[j + 1]->type() == TrackingRecHit::missing) && (myHits[j + 1]->geographicalId().rawId() != 0)) &&
350  ((myHits[j]->geographicalId().rawId() & (~3)) !=
351  (myHits[j + 1]->geographicalId().rawId() & (~3))) // same gluedDet
352  ) {
353  firstinvalid = j;
354  DPRINT("TrackFitters") << "Found two consecutive missing hits. First invalid: " << firstinvalid << "\n";
355  break;
356  }
357  }
358 
359  //reject all the hits after the last valid before two consecutive invalid (missing) hits
360  //hits are sorted in the same order as in the track candidate FIXME??????
361  if (firstinvalid != myHits.size()) {
362  myHits.erase(myHits.begin() + firstinvalid, myHits.end());
363  smoothed = smoothingStep(theFitter->fitOne(aSeed, myHits, firstPredTsos));
364  DPRINT("TrackFitters") << "Trajectory shortened " << smoothed.foundHits() << '/' << smoothed.chiSquared()
365  << "\n";
366  }
367  }
368 
369  } // do
370  while (true);
371 
372  if (smoothed.isValid()) {
373  if (noInvalidHitsBeginEnd && !smoothed.empty() //should we send a warning ?
374  ) {
375  // discard latest dummy measurements
376  if (!smoothed.empty() && !smoothed.lastMeasurement().recHitR().isValid())
377  LogTrace("TrackFitters") << "Last measurement is invalid";
378 
379  while (!smoothed.empty() && !smoothed.lastMeasurement().recHitR().isValid())
380  smoothed.pop();
381 
382  //remove the invalid hits at the begin of the trajectory
383  if (!smoothed.empty() && !smoothed.firstMeasurement().recHitR().isValid()) {
384  LogTrace("TrackFitters") << "First measurement is in`valid";
385  Trajectory tmpTraj(smoothed.seed(), smoothed.direction());
386  Trajectory::DataContainer& meas = smoothed.measurements();
387  auto it = meas.begin();
388  for (; it != meas.end(); ++it)
389  if (it->recHitR().isValid())
390  break;
391  //push the first valid measurement and set the same global chi2
392  tmpTraj.push(std::move(*it), smoothed.chiSquared());
393 
394  for (auto itt = it + 1; itt != meas.end(); ++itt)
395  tmpTraj.push(std::move(*itt), 0); //add all the other measurements
396 
397  std::swap(smoothed, tmpTraj);
398 
399  } // if ( !smoothed.firstMeasurement().recHit()->isValid() )
400 
401  } // if ( noInvalidHitsBeginEnd )
402 
403  LogTrace("TrackFitters") << "end: returning smoothed trajectory with chi2=" << smoothed.chiSquared();
404 
405  //LogTrace("TrackFitters") << "dump hits before return";
406  //Trajectory::DataContainer meas = smoothed.measurements();
407  //for (Trajectory::DataContainer::iterator it=meas.begin();it!=meas.end();++it) {
408  //LogTrace("TrackFitters") << "hit #" << meas.end()-it-1 << " validity=" << it->recHit()->isValid()
409  //<< " det=" << it->recHit()->geographicalId().rawId();
410  //}
411  }
412 
413  return smoothed;
414  }
415 
416  Trajectory KFFittingSmoother::fitOne(const TrajectorySeed& aSeed, const RecHitContainer& hits, fitType type) const {
417  throw cms::Exception("TrackFitters",
418  "KFFittingSmoother::fit(TrajectorySeed, <TransientTrackingRecHit>) not implemented");
419 
420  return Trajectory();
421  }
422 
423 } // namespace
ConfigurationDescriptions.h
TrajectoryStateOnSurface.h
electrons_cff.bool
bool
Definition: electrons_cff.py:393
mps_fire.i
i
Definition: mps_fire.py:428
MessageLogger.h
hfClusterShapes_cfi.hits
hits
Definition: hfClusterShapes_cfi.py:5
edm::isNotFinite
constexpr bool isNotFinite(T x)
Definition: isFinite.h:9
Trajectory::chiSquared
float chiSquared() const
Definition: Trajectory.h:241
Trajectory::direction
PropagationDirection const & direction() const
Definition: Trajectory.cc:133
TrajectoryStateOnSurface::globalPosition
GlobalPoint globalPosition() const
Definition: TrajectoryStateOnSurface.h:65
MTVHistoProducerAlgoForTrackerBlock_cfi.minChi2
minChi2
Definition: MTVHistoProducerAlgoForTrackerBlock_cfi.py:89
edm::ParameterSetDescription
Definition: ParameterSetDescription.h:52
cms::cuda::assert
assert(be >=bs)
TrajectoryMeasurement::updatedState
TrajectoryStateOnSurface const & updatedState() const
Definition: TrajectoryMeasurement.h:184
TrajectoryStateOnSurface::transverseCurvature
double transverseCurvature() const
Definition: TrajectoryStateOnSurface.h:70
Trajectory::foundHits
int foundHits() const
Definition: Trajectory.h:206
findQualityFiles.v
v
Definition: findQualityFiles.py:179
edm::LogWarning
Log< level::Warning, false > LogWarning
Definition: MessageLogger.h:122
ghostTrackVertexReco_cff.fitType
fitType
Definition: ghostTrackVertexReco_cff.py:10
TrajectoryFitter::setHitCloner
virtual void setHitCloner(TkCloner const *)=0
PV3DBase::z
T z() const
Definition: PV3DBase.h:61
TrajectorySmoother
Definition: TrajectorySmoother.h:11
TrajectoryStateOnSurface
Definition: TrajectoryStateOnSurface.h:16
TkCloner
Definition: TkCloner.h:16
std::swap
void swap(edm::DataFrameContainer &lhs, edm::DataFrameContainer &rhs)
Definition: DataFrameContainer.h:209
clone
TEveGeoShape * clone(const TEveElement *element, TEveElement *parent)
Definition: eve_macros.cc:135
Trajectory::DataContainer
std::vector< TrajectoryMeasurement > DataContainer
Definition: Trajectory.h:40
visualization-live-secondInstance_cfg.m
m
Definition: visualization-live-secondInstance_cfg.py:72
fillDescriptions
static void fillDescriptions(edm::ConfigurationDescriptions &descriptions)
declareDynArray
#define declareDynArray(T, n, x)
Definition: DynArray.h:91
TrajectoryFitter.h
trackingPlots.other
other
Definition: trackingPlots.py:1467
ParameterSetDescription.h
mitigatedMETSequence_cff.U
U
Definition: mitigatedMETSequence_cff.py:36
AlCaHLTBitMon_QueryRunRegistry.string
string
Definition: AlCaHLTBitMon_QueryRunRegistry.py:256
wplusjetsAnalysis_cfi.NHits
NHits
Definition: wplusjetsAnalysis_cfi.py:28
LogDebug
#define LogDebug(id)
Definition: MessageLogger.h:223
edm::ParameterSet
Definition: ParameterSet.h:47
print
void print(TMatrixD &m, const char *label=nullptr, bool mathematicaFormat=false)
Definition: Utilities.cc:46
SiStripPI::max
Definition: SiStripPayloadInspectorHelper.h:169
type
type
Definition: SiPixelVCal_PayloadInspector.cc:37
Trajectory::lastMeasurement
TrajectoryMeasurement const & lastMeasurement() const
Definition: Trajectory.h:150
TrackingRecHit::missing
Definition: TrackingRecHit.h:47
gainCalibHelper::gainCalibPI::type
type
Definition: SiPixelGainCalibHelper.h:39
PV3DBase::eta
T eta() const
Definition: PV3DBase.h:73
createfilelist.int
int
Definition: createfilelist.py:10
TrajectorySmoother.h
Trajectory::pop
void pop()
Definition: Trajectory.cc:30
DynArray.h
Trajectory::measurements
DataContainer const & measurements() const
Definition: Trajectory.h:178
TrajectoryStateOnSurface::globalMomentum
GlobalVector globalMomentum() const
Definition: TrajectoryStateOnSurface.h:66
TrajectoryFitter
Definition: TrajectoryFitter.h:19
submitPVResolutionJobs.desc
string desc
Definition: submitPVResolutionJobs.py:251
eostools.move
def move(src, dest)
Definition: eostools.py:511
Trajectory::firstMeasurement
TrajectoryMeasurement const & firstMeasurement() const
Definition: Trajectory.h:166
isFinite.h
DPRINT
#define DPRINT(x)
Definition: KFFittingSmoother.h:125
Trajectory
Definition: Trajectory.h:38
Exception
Definition: hltDiff.cc:246
Trajectory::seed
TrajectorySeed const & seed() const
Access to the seed used to reconstruct the Trajectory.
Definition: Trajectory.h:263
TrajectorySeed
Definition: TrajectorySeed.h:18
minChi2
Definition: JetCombinatorics.h:265
TrajectoryMeasurement::recHitR
ConstRecHitPointer::element_type const & recHitR() const
Definition: TrajectoryMeasurement.h:186
RecoTauValidation_cfi.header
header
Definition: RecoTauValidation_cfi.py:292
TrajectoryStateOnSurface::signedInverseMomentum
double signedInverseMomentum() const
Definition: TrajectoryStateOnSurface.h:69
LogTrace
#define LogTrace(id)
Definition: MessageLogger.h:224
dqmiolumiharvest.j
j
Definition: dqmiolumiharvest.py:66
TrajectoryFitter::fitOne
virtual Trajectory fitOne(const Trajectory &traj, fitType type=standard) const =0
submitPVValidationJobs.t
string t
Definition: submitPVValidationJobs.py:644
PV3DBase::perp
T perp() const
Definition: PV3DBase.h:69
PRINT
#define PRINT
Definition: KFFittingSmoother.h:126
Trajectory::isValid
bool isValid() const
Definition: Trajectory.h:257
InvalidTransientRecHit.h
Trajectory::empty
bool empty() const
True if trajectory has no measurements.
Definition: Trajectory.h:233