CMS 3D CMS Logo

KFFittingSmootherESProducer.cc
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  theMinNumberOfHitsHighEta(conf.getParameter<int>("MinNumberOfHitsHighEta")),
32  theHighEtaSwitch(conf.getParameter<double>("HighEtaSwitch")),
33  rejectTracksFlag(conf.getParameter<bool>("RejectTracks")),
34  breakTrajWith2ConsecutiveMissing(conf.getParameter<bool>("BreakTrajWith2ConsecutiveMissing")),
35  noInvalidHitsBeginEnd(conf.getParameter<bool>("NoInvalidHitsBeginEnd")) {}
36 
37  double theEstimateCut;
38 
39  float theMaxFractionOutliers;
40  int theMaxNumberOfOutliers;
41  bool theNoOutliersBeginEnd;
42  int theMinDof;
43 
44  int theMinNumberOfHits;
45  int theMinNumberOfHitsHighEta;
46  double theHighEtaSwitch;
47  bool rejectTracksFlag;
48  bool breakTrajWith2ConsecutiveMissing;
49  bool noInvalidHitsBeginEnd;
50  };
51 
52  class KFFittingSmoother final : public TrajectoryFitter, private KFFittingSmootherParam {
53  public:
54  ~KFFittingSmoother() override {}
55 
56  KFFittingSmoother(const TrajectoryFitter& aFitter,
57  const TrajectorySmoother& aSmoother,
58  const edm::ParameterSet& conf)
59  : KFFittingSmootherParam(conf), theFitter(aFitter.clone()), theSmoother(aSmoother.clone()) {}
60 
61  private:
63  desc.add<double>("EstimateCut", -1);
64  desc.add<double>("MaxFractionOutliers", 0.3);
65  desc.add<int>("MaxNumberOfOutliers", 3);
66  desc.add<int>("MinDof", 2);
67  desc.add<bool>("NoOutliersBeginEnd", false);
68  desc.add<int>("MinNumberOfHits", 5);
69  desc.add<int>("MinNumberOfHitsHighEta", 5);
70  desc.add<double>("HighEtaSwitch", 5.0);
71  desc.add<bool>("RejectTracks", true);
72  desc.add<bool>("BreakTrajWith2ConsecutiveMissing", true);
73  desc.add<bool>("NoInvalidHitsBeginEnd", true);
74  desc.add<double>("LogPixelProbabilityCut", 0);
75  }
76 
77  int getNhitCutValue(const Trajectory& t,
78  double theHighEtaSwitch,
79  int theMinNumberOfHitsHighEta,
80  int theMinNumberOfHits) const {
81  double sinhTrajEta2 = std::numeric_limits<double>::max();
82  if (!t.empty() && t.isValid()) {
83  /* in principle we can access eta() and check it w.r.t theHighEtaSwitch.
84  but eta() is expensive, so we are making use of the following relation
85  sinh(eta) = pz/pt (will square on both side to get rid of sign)
86  */
87  double pt = t.lastMeasurement().updatedState().freeTrajectoryState()->momentum().perp();
88  double pz = t.lastMeasurement().updatedState().freeTrajectoryState()->momentum().z();
89  sinhTrajEta2 = (pz * pz) / (pt * pt);
90  }
91  double myEtaSwitch = sinh(theHighEtaSwitch);
92  const auto thisHitCut =
93  sinhTrajEta2 > (myEtaSwitch * myEtaSwitch) ? theMinNumberOfHitsHighEta : theMinNumberOfHits;
94  return thisHitCut;
95  }
96 
97  Trajectory fitOne(const Trajectory& t, fitType type) const override;
98  Trajectory fitOne(const TrajectorySeed& aSeed,
99  const RecHitContainer& hits,
100  const TrajectoryStateOnSurface& firstPredTsos,
101  fitType type) const override;
102  Trajectory fitOne(const TrajectorySeed& aSeed, const RecHitContainer& hits, fitType type) const override;
103 
104  const TrajectoryFitter* fitter() const { return theFitter.get(); }
105  const TrajectorySmoother* smoother() const { return theSmoother.get(); }
106 
107  std::unique_ptr<TrajectoryFitter> clone() const override {
108  return std::unique_ptr<TrajectoryFitter>(new KFFittingSmoother(*theFitter, *theSmoother, *this));
109  }
110 
111  void setHitCloner(TkCloner const* hc) override {
112  theFitter->setHitCloner(hc);
113  theSmoother->setHitCloner(hc);
114  }
115 
116  KFFittingSmoother(const TrajectoryFitter& aFitter,
117  const TrajectorySmoother& aSmoother,
118  KFFittingSmootherParam const& other)
119  : KFFittingSmootherParam(other), theFitter(aFitter.clone()), theSmoother(aSmoother.clone()) {}
120 
121  Trajectory smoothingStep(Trajectory&& fitted) const {
122  const auto thisHitCut = getNhitCutValue(fitted, theHighEtaSwitch, theMinNumberOfHitsHighEta, theMinNumberOfHits);
123 
124  if (theEstimateCut > 0) {
125  // remove "outlier" at the end of Traj
126  while (
127  !fitted.empty() && fitted.foundHits() >= thisHitCut &&
128  (!fitted.lastMeasurement().recHitR().isValid() || (fitted.lastMeasurement().recHitR().det() != nullptr &&
129  fitted.lastMeasurement().estimate() > theEstimateCut)))
130  fitted.pop();
131  if (fitted.foundHits() < thisHitCut)
132  return Trajectory();
133  }
134  return theSmoother->trajectory(fitted);
135  }
136 
137  private:
138  const std::unique_ptr<TrajectoryFitter> theFitter;
139  const std::unique_ptr<TrajectorySmoother> theSmoother;
140 
142  static bool checkForNans(const Trajectory& theTraj);
143 
144  friend class KFFittingSmootherESProducer;
145  };
146 
147  // #define VI_DEBUG
148 
149 #ifdef VI_DEBUG
150 #define DPRINT(x) std::cout << x << ": "
151 #define PRINT std::cout
152 #else
153 #define DPRINT(x) LogTrace(x)
154 #define PRINT LogTrace("")
155 #endif
156 
157  inline Trajectory KFFittingSmoother::fitOne(const Trajectory& t, fitType type) const {
158  if (!t.isValid())
159  return Trajectory();
160  return smoothingStep(theFitter->fitOne(t, type));
161  }
162 
163  inline bool KFFittingSmoother::checkForNans(const Trajectory& theTraj) {
164  if (edm::isNotFinite(theTraj.chiSquared()))
165  return false;
166  auto const& vtm = theTraj.measurements();
167  for (auto const& tm : vtm) {
168  if (edm::isNotFinite(tm.estimate()))
169  return false;
170  auto const& v = tm.updatedState().localParameters().vector();
171  for (int i = 0; i < 5; ++i)
172  if (edm::isNotFinite(v[i]))
173  return false;
174  if (!tm.updatedState().curvilinearError().posDef())
175  return false; //not a NaN by itself, but will lead to one
176  auto const& m = tm.updatedState().curvilinearError().matrix();
177  for (int i = 0; i < 5; ++i)
178  for (int j = 0; j < (i + 1); ++j)
179  if (edm::isNotFinite(m(i, j)))
180  return false;
181  }
182  return true;
183  }
184 
185  namespace {
186  inline void print(const std::string& header, const TrajectoryStateOnSurface& tsos) {
187  DPRINT("TrackFitters") << header << tsos.globalPosition().perp() << ' ' << tsos.globalPosition().z() << ' '
188  << 1. / tsos.signedInverseMomentum() << ' ' << 1. / tsos.transverseCurvature() << ' '
189  << tsos.globalMomentum().eta() << std::endl;
190  }
191  } // namespace
192 
194  const RecHitContainer& hits,
195  const TrajectoryStateOnSurface& firstPredTsos,
196  fitType type) const {
197  LogDebug("TrackFitters") << "In KFFittingSmoother::fit";
198 
199  print("firstPred ", firstPredTsos);
200 
201  if (hits.empty())
202  return Trajectory();
203 
204  RecHitContainer myHits = hits;
205  Trajectory tmp_first;
206 
207  //call the fitter
208  Trajectory smoothed = smoothingStep(theFitter->fitOne(aSeed, myHits, firstPredTsos));
209 
210  do {
211 #ifdef EDM_ML_DEBUG
212  //if no outliers the fit is done only once
213  for (unsigned int j = 0; j < myHits.size(); j++) {
214  if (myHits[j]->det())
215  LogTrace("TrackFitters") << "hit #:" << j + 1 << " rawId=" << myHits[j]->det()->geographicalId().rawId()
216  << " validity=" << myHits[j]->isValid();
217  else
218  LogTrace("TrackFitters") << "hit #:" << j + 1 << " Hit with no Det information";
219  }
220 #endif
221 
222 #if defined(VI_DEBUG) || defined(EDM_ML_DEBUG)
223  if (smoothed.isValid()) {
224  print("first state ", smoothed.firstMeasurement().updatedState());
225  print("last state ", smoothed.lastMeasurement().updatedState());
226  }
227 #endif
228 
229  bool hasNaN = false;
230  const auto thisHitCut =
231  getNhitCutValue(smoothed, theHighEtaSwitch, theMinNumberOfHitsHighEta, theMinNumberOfHits);
232 
233  if (!smoothed.isValid() || (hasNaN = !checkForNans(smoothed)) || (smoothed.foundHits() < thisHitCut)) {
234  if (hasNaN)
235  edm::LogWarning("TrackNaN") << "Track has NaN or the cov is not pos-definite";
236  if (smoothed.foundHits() < thisHitCut)
237  LogTrace("TrackFitters") << "smoothed.foundHits()<thisHitCut";
238  DPRINT("TrackFitters") << "smoothed invalid => trajectory rejected with nhits/chi2 " << smoothed.foundHits()
239  << '/' << smoothed.chiSquared() << "\n";
240  if (rejectTracksFlag) {
241  return Trajectory();
242  } else {
243  std::swap(smoothed, tmp_first); // if first attempt, tmp_first would be invalid anyway
244  DPRINT("TrackFitters") << "smoothed invalid => returning orignal trajectory with nhits/chi2 "
245  << smoothed.foundHits() << '/' << smoothed.chiSquared() << "\n";
246  }
247  break;
248  }
249 #ifdef EDM_ML_DEBUG
250  else {
251  LogTrace("TrackFitters") << "dump hits after smoothing";
252  Trajectory::DataContainer meas = smoothed.measurements();
253  for (Trajectory::DataContainer::iterator it = meas.begin(); it != meas.end(); ++it) {
254  LogTrace("TrackFitters") << "hit #" << meas.end() - it - 1 << " validity=" << it->recHit()->isValid()
255  << " det=" << it->recHit()->geographicalId().rawId();
256  }
257  }
258 #endif
259 
260  if (myHits.size() != smoothed.measurements().size())
261  DPRINT("TrackFitters") << "lost hits. before/after: " << myHits.size() << '/' << smoothed.measurements().size()
262  << "\n";
263 
264  if (theEstimateCut <= 0)
265  break;
266 
267  // Check if there are outliers
268 
269  auto msize = smoothed.measurements().size();
270  declareDynArray(unsigned int, msize, bad);
271  unsigned int nbad = 0;
272  unsigned int ind = 0;
273  unsigned int lastValid = smoothed.measurements().size();
274  for (auto const& tm : smoothed.measurements()) {
275  if (tm.estimate() > theEstimateCut &&
276  tm.recHitR().det() != nullptr // do not consider outliers constraints and other special "hits"
277  )
278  bad[nbad++] = ind;
279  if (ind < lastValid && tm.recHitR().det() != nullptr && tm.recHitR().isValid())
280  lastValid = ind;
281  ++ind;
282  }
283 
284  if (0 == nbad)
285  break;
286 
287  DPRINT("TrackFitters") << "size/found/outliers list " << smoothed.measurements().size() << '/'
288  << smoothed.foundHits() << ' ' << nbad << ": ";
289  for (auto i = 0U; i < nbad; ++i)
290  PRINT << bad[i] << ',';
291  PRINT << std::endl;
292 
293  if (
294  // (smoothed.foundHits() == theMinNumberOfHits) ||
295  int(nbad) > theMaxNumberOfOutliers || float(nbad) > theMaxFractionOutliers * float(smoothed.foundHits())) {
296  DPRINT("TrackFitters") << "smoothed low quality => trajectory with nhits/chi2 " << smoothed.foundHits() << '/'
297  << smoothed.chiSquared() << "\n";
298  PRINT << "try to remove " << lastValid << std::endl;
299  nbad = 0; // try to short the traj... (below lastValid will be added)
300 
301  // do not perform outliers rejection if track is already low quality
302  /*
303  if ( rejectTracksFlag && (smoothed.chiSquared() > theEstimateCut*smoothed.ndof()) ) {
304  DPRINT("TrackFitters") << "smoothed low quality => trajectory rejected with nhits/chi2 " << smoothed.foundHits() << '/' << smoothed.chiSquared() << "\n";
305  return Trajectory();
306  } else {
307  DPRINT("TrackFitters") << "smoothed low quality => return original trajectory with nhits/chi2 " << smoothed.foundHits() << '/' << smoothed.chiSquared() << "\n";
308  }
309  break;
310  */
311  }
312 
313  // always add last valid hit as outlier candidate
314  bad[nbad++] = lastValid;
315 
316  // if ( (smoothed.ndof()<theMinDof) | ) break;
317 
318  assert(smoothed.measurements().size() <= myHits.size());
319 
320  myHits.resize(smoothed.measurements().size()); // hits are only removed from the back...
321 
322  assert(smoothed.measurements().size() == myHits.size());
323 
324  declareDynArray(Trajectory, nbad, smoothedCand);
325 
326  auto NHits = myHits.size();
328 
329  auto loc = nbad;
330  for (auto i = 0U; i < nbad; ++i) {
331  auto j = NHits - bad[i] - 1;
332  assert(myHits[j]->geographicalId() == smoothed.measurements()[bad[i]].recHitR().geographicalId());
333  auto removedHit = myHits[j];
334  myHits[j] = std::make_shared<InvalidTrackingRecHit>(*removedHit->det(), TrackingRecHit::missing);
335  smoothedCand[i] = smoothingStep(theFitter->fitOne(aSeed, myHits, firstPredTsos));
336  myHits[j] = removedHit;
337  if (smoothedCand[i].isValid() && smoothedCand[i].chiSquared() < minChi2) {
338  minChi2 = smoothedCand[i].chiSquared();
339  loc = i;
340  }
341  }
342 
343  if (loc == nbad) {
344  DPRINT("TrackFitters") << "New trajectories all invalid"
345  << "\n";
346  return Trajectory();
347  }
348 
349  DPRINT("TrackFitters") << "outlier removed " << bad[loc] << '/' << minChi2 << " was " << smoothed.chiSquared()
350  << "\n";
351 
352  if (minChi2 > smoothed.chiSquared()) {
353  DPRINT("TrackFitters") << "removing outlier makes chi2 worse " << minChi2 << '/' << smoothed.chiSquared()
354  << "\nOri: ";
355  for (auto const& tm : smoothed.measurements())
356  PRINT << tm.recHitR().geographicalId() << '/' << tm.estimate() << ' ';
357  PRINT << "\nNew: ";
358  for (auto const& tm : smoothedCand[loc].measurements())
359  PRINT << tm.recHitR().geographicalId() << '/' << tm.estimate() << ' ';
360  PRINT << "\n";
361 
362  // return Trajectory();
363  // break;
364  }
365 
366  std::swap(smoothed, tmp_first);
367  myHits[NHits - bad[loc] - 1] =
368  std::make_shared<InvalidTrackingRecHit>(*myHits[NHits - bad[loc] - 1]->det(), TrackingRecHit::missing);
369  std::swap(smoothed, smoothedCand[loc]);
370  // firstTry=false;
371 
372  DPRINT("TrackFitters") << "new trajectory with nhits/chi2 " << smoothed.foundHits() << '/'
373  << smoothed.chiSquared() << "\n";
374 
375  // Look if there are two consecutive invalid hits FIXME: take into account split matched hits!!!
376  if (breakTrajWith2ConsecutiveMissing) {
377  unsigned int firstinvalid = myHits.size();
378  for (unsigned int j = 0; j < myHits.size() - 1; ++j) {
379  if (((myHits[j]->type() == TrackingRecHit::missing) && (myHits[j]->geographicalId().rawId() != 0)) &&
380  ((myHits[j + 1]->type() == TrackingRecHit::missing) && (myHits[j + 1]->geographicalId().rawId() != 0)) &&
381  ((myHits[j]->geographicalId().rawId() & (~3)) !=
382  (myHits[j + 1]->geographicalId().rawId() & (~3))) // same gluedDet
383  ) {
384  firstinvalid = j;
385  DPRINT("TrackFitters") << "Found two consecutive missing hits. First invalid: " << firstinvalid << "\n";
386  break;
387  }
388  }
389 
390  //reject all the hits after the last valid before two consecutive invalid (missing) hits
391  //hits are sorted in the same order as in the track candidate FIXME??????
392  if (firstinvalid != myHits.size()) {
393  myHits.erase(myHits.begin() + firstinvalid, myHits.end());
394  smoothed = smoothingStep(theFitter->fitOne(aSeed, myHits, firstPredTsos));
395  DPRINT("TrackFitters") << "Trajectory shortened " << smoothed.foundHits() << '/' << smoothed.chiSquared()
396  << "\n";
397  }
398  }
399 
400  } // do
401  while (true);
402 
403  if (smoothed.isValid()) {
404  if (noInvalidHitsBeginEnd && !smoothed.empty() //should we send a warning ?
405  ) {
406  // discard latest dummy measurements
407  if (!smoothed.empty() && !smoothed.lastMeasurement().recHitR().isValid())
408  LogTrace("TrackFitters") << "Last measurement is invalid";
409 
410  while (!smoothed.empty() && !smoothed.lastMeasurement().recHitR().isValid())
411  smoothed.pop();
412 
413  //remove the invalid hits at the begin of the trajectory
414  if (!smoothed.empty() && !smoothed.firstMeasurement().recHitR().isValid()) {
415  LogTrace("TrackFitters") << "First measurement is in`valid";
416  Trajectory tmpTraj(smoothed.seed(), smoothed.direction());
417  Trajectory::DataContainer& meas = smoothed.measurements();
418  auto it = meas.begin();
419  for (; it != meas.end(); ++it)
420  if (it->recHitR().isValid())
421  break;
422  //push the first valid measurement and set the same global chi2
423  tmpTraj.push(std::move(*it), smoothed.chiSquared());
424 
425  for (auto itt = it + 1; itt != meas.end(); ++itt)
426  tmpTraj.push(std::move(*itt), 0); //add all the other measurements
427 
428  std::swap(smoothed, tmpTraj);
429 
430  } // if ( !smoothed.firstMeasurement().recHit()->isValid() )
431 
432  } // if ( noInvalidHitsBeginEnd )
433 
434  LogTrace("TrackFitters") << "end: returning smoothed trajectory with chi2=" << smoothed.chiSquared();
435 
436  //LogTrace("TrackFitters") << "dump hits before return";
437  //Trajectory::DataContainer meas = smoothed.measurements();
438  //for (Trajectory::DataContainer::iterator it=meas.begin();it!=meas.end();++it) {
439  //LogTrace("TrackFitters") << "hit #" << meas.end()-it-1 << " validity=" << it->recHit()->isValid()
440  //<< " det=" << it->recHit()->geographicalId().rawId();
441  //}
442  }
443 
444  return smoothed;
445  }
446 
448  const RecHitContainer& hits,
449  fitType type) const {
450  throw cms::Exception("TrackFitters",
451  "KFFittingSmoother::fit(TrajectorySeed, <TransientTrackingRecHit>) not implemented");
452 
453  return Trajectory();
454  }
455 
456 } // namespace
457 
459 
462 
463 namespace {
464 
465  class KFFittingSmootherESProducer final : public edm::ESProducer {
466  public:
467  KFFittingSmootherESProducer(const edm::ParameterSet& p) : pset_{p} {
468  std::string myname = p.getParameter<std::string>("ComponentName");
469  auto cc = setWhatProduced(this, myname);
470  fitToken_ = cc.consumes(edm::ESInputTag("", pset_.getParameter<std::string>("Fitter")));
471  smoothToken_ = cc.consumes(edm::ESInputTag("", pset_.getParameter<std::string>("Smoother")));
472  }
473 
474  static void fillDescriptions(edm::ConfigurationDescriptions& descriptions) {
476  desc.add<std::string>("ComponentName", "KFFittingSmoother");
477  desc.add<std::string>("Fitter", "KFFitter");
478  desc.add<std::string>("Smoother", "KFSmoother");
480  descriptions.add("KFFittingSmoother", desc);
481  }
482 
483  std::unique_ptr<TrajectoryFitter> produce(const TrajectoryFitterRecord& iRecord) {
484  return std::make_unique<KFFittingSmoother>(iRecord.get(fitToken_), iRecord.get(smoothToken_), pset_);
485  }
486 
487  private:
488  const edm::ParameterSet pset_;
491  };
492 } // namespace
493 
495 
496 DEFINE_FWK_EVENTSETUP_MODULE(KFFittingSmootherESProducer);
auto setWhatProduced(T *iThis, const es::Label &iLabel={})
Definition: ESProducer.h:163
ConstRecHitPointer::element_type const & recHitR() const
T perp() const
Definition: PV3DBase.h:69
bool isValid() const
Definition: Trajectory.h:257
const bool isValid(const Frame &aFrame, const FrameQuality &aQuality, const uint16_t aExpectedPos)
T z() const
Definition: PV3DBase.h:61
constexpr bool isNotFinite(T x)
Definition: isFinite.h:9
T eta() const
Definition: PV3DBase.h:73
#define DPRINT(x)
float chiSquared() const
Definition: Trajectory.h:241
int foundHits() const
Definition: Trajectory.h:206
assert(be >=bs)
TrajectoryMeasurement const & lastMeasurement() const
Definition: Trajectory.h:150
#define LogTrace(id)
DataContainer const & measurements() const
Definition: Trajectory.h:178
std::vector< TrajectoryMeasurement > DataContainer
Definition: Trajectory.h:40
GlobalPoint globalPosition() const
virtual void setHitCloner(TkCloner const *)=0
void swap(edm::DataFrameContainer &lhs, edm::DataFrameContainer &rhs)
PropagationDirection const & direction() const
Definition: Trajectory.cc:133
void print(TMatrixD &m, const char *label=nullptr, bool mathematicaFormat=false)
Definition: Utilities.cc:47
virtual Trajectory fitOne(const Trajectory &traj, fitType type=standard) const =0
static void fillDescriptions(edm::ConfigurationDescriptions &descriptions)
TrajectorySeed const & seed() const
Access to the seed used to reconstruct the Trajectory.
Definition: Trajectory.h:263
void pop()
Definition: Trajectory.cc:30
#define DEFINE_FWK_EVENTSETUP_MODULE(type)
Definition: ModuleFactory.h:60
void add(std::string const &label, ParameterSetDescription const &psetDescription)
bool empty() const
True if trajectory has no measurements.
Definition: Trajectory.h:233
GlobalVector globalMomentum() const
TEveGeoShape * clone(const TEveElement *element, TEveElement *parent)
Definition: eve_macros.cc:135
TrajectoryStateOnSurface const & updatedState() const
TrajectoryMeasurement const & firstMeasurement() const
Definition: Trajectory.h:166
#define declareDynArray(T, n, x)
Definition: DynArray.h:91
__shared__ int msize
Log< level::Warning, false > LogWarning
ProductT const & get(ESGetToken< ProductT, DepRecordT > const &iToken) const
def move(src, dest)
Definition: eostools.py:511
#define LogDebug(id)