LArSoft  v10_04_05
Liquid Argon Software toolkit - https://larsoft.org/
KalmanFilterAlg.cxx
Go to the documentation of this file.
1 
13 #include "boost/numeric/ublas/matrix_proxy.hpp"
14 #include "boost/numeric/ublas/vector_proxy.hpp"
15 #include "cetlib_except/exception.h"
24 
25 #include "Rtypes.h"
26 #include "TCanvas.h"
27 #include "TGaxis.h"
28 #include "TLegend.h"
29 #include "TLegendEntry.h"
30 #include "TMarker.h"
31 #include "TObject.h"
32 #include "TPad.h"
33 #include "TPaveText.h"
34 #include "TText.h"
35 #include "TVirtualPad.h"
36 
37 // Local functions.
38 
39 namespace {
40 
41  std::pair<double, double> hit_position(const trkf::KHitBase& hit)
42  {
43  // Map hit -> (z,x) for plotting.
45 
46  double z = 0.;
47  double x = 0.;
48 
49  // Cast this hit to KHit<1>. Only know how to handle 1D hits.
50 
51  if (auto phit1 = dynamic_cast<const trkf::KHit<1>*>(&hit)) {
52  const std::shared_ptr<const trkf::Surface>& psurf = hit.getMeasSurface();
53 
54  // Handle SurfYZPlane.
55 
56  if (auto pyz = dynamic_cast<const trkf::SurfYZPlane*>(psurf.get())) {
57 
58  // Now finished doing casts.
59  // We have a kind of hit and measurement surface that we know how to handle.
60 
61  // Get x coordinate from hit and surface.
62 
63  x = pyz->x0() + phit1->getMeasVector()(0);
64 
65  // Get z position from surface parameters.
66  // The "z" position is actually calculated as the perpendicular distance
67  // from a corner, which is a proxy for wire number.
68 
69  double z0 = pyz->z0();
70  double y0 = pyz->y0();
71  double phi = pyz->phi();
72  double ymax = geom->TPC().HalfWidth();
73  if (phi > 0.)
74  z = z0 * std::cos(phi) + (ymax - y0) * std::sin(phi);
75  else
76  z = z0 * std::cos(phi) - (ymax + y0) * std::sin(phi);
77  }
78  else if (auto pyz = dynamic_cast<const trkf::SurfYZLine*>(psurf.get())) {
79 
80  // Now finished doing casts.
81  // We have a kind of hit and measurement surface that we know how to handle.
82 
83  // Get x coordinate from surface.
84 
85  x = pyz->x0();
86 
87  // Get z position from surface parameters.
88  // The "z" position is actually calculated as the perpendicular distance
89  // from a corner, which is a proxy for wire number.
90 
91  double z0 = pyz->z0();
92  double y0 = pyz->y0();
93  double phi = pyz->phi();
94  double ymax = geom->TPC().HalfWidth();
95  if (phi > 0.)
96  z = z0 * std::cos(phi) + (ymax - y0) * std::sin(phi);
97  else
98  z = z0 * std::cos(phi) - (ymax + y0) * std::sin(phi);
99  }
100  }
101  return {z, x};
102  }
103 
104  void update_momentum(const trkf::KVector<2>::type& defl,
105  const trkf::KSymMatrix<2>::type& errc,
106  const trkf::KSymMatrix<2>::type& errn,
107  double mass,
108  double& invp,
109  double& var_invp)
110  // Momentum updater.
111  //
112  // Arguments: defl - Deflection (2D slope residual between two
113  // sampled points on track).
114  // errc - Error matrix of deflection residual
115  // exclusive of multiple scattering.
116  // errn - Part of deflection noise error matrix
117  // proportional to 1/(beta*momentum).
118  // mass - Mass of particle.
119  // invp - Modified 1/momentum track parameter.
120  // var_invp - Modified 1/momentum variance.
121  //
122  // Returns: True if success.
123  //
124  // The total deflection residual error matrix is
125  //
126  // err = errc + [1/(beta*momentum)] * errn.
127  //
128  // The inverse momentum and error are updated using using log-likelihood
129  //
130  // log(L) = -0.5 * log(det(err)) - 0.5 * defl^T * err^(-1) * defl.
131  // log(L) = -0.5 * tr(log(err)) - 0.5 * defl^T * err^(-1) * defl.
132  //
133  {
134  // Calculate original k = 1./(beta*p), and original variance of k.
135 
136  double invp2 = invp * invp;
137  double invp3 = invp * invp2;
138  double invp4 = invp2 * invp2;
139  double mass2 = mass * mass;
140  double k = std::sqrt(invp2 + mass2 * invp4);
141  double dkdinvp = (invp + 2. * mass2 * invp3) / k;
142  double vark = var_invp * dkdinvp * dkdinvp;
143 
144  // First, find current inverse error matrix using momentum hypothesis.
145 
146  trkf::KSymMatrix<2>::type inverr = errc + k * errn;
147  trkf::syminvert(inverr);
148 
149  // Find the first and second derivatives of the log likelihood
150  // with respact to k.
151 
152  trkf::KMatrix<2, 2>::type temp1 = prod(inverr, errn);
153  trkf::KMatrix<2, 2>::type temp2 = prod(temp1, temp1);
154 
155  // KJK-FIXME: Ideally, the explicit type specification below
156  // ('trk::KVector<T>::type') should be replaced with the 'auto'
157  // keyword. However, Boost 1.66.0a runs into a static-assert
158  // failure with the 'complexity' of vtemp2 not being 0. I am not
159  // sure what the complexity means.
160  //
161  // If 'trkf::KVector<2>::type vtemp2 = prod(temp1, vtemp1)' is
162  // used below, then the GCC 7.3 compiler thinks there may be an
163  // uninitialized variable in vtemp2. A workaround is to use the
164  // Boost interface that fills a default-constructed vector. Note
165  // that this workaround may hide an actual problem. It is the
166  // responsibility of the maintainer of this code to determine if
167  // an actual problem exists.
168 
169  trkf::KVector<2>::type vtemp1 = prod(inverr, defl);
170  trkf::KVector<2>::type vtemp2 = prod(temp1, vtemp1);
171  trkf::KVector<2>::type vtemp3 = prod(temp1, vtemp2);
172  double derivk1 = -0.5 * trkf::trace(temp1) + 0.5 * inner_prod(defl, vtemp2);
173  double derivk2 = 0.5 * trkf::trace(temp2) - inner_prod(defl, vtemp3);
174 
175  // We expect the log-likelihood to be most nearly Gaussian
176  // with respect to variable q = k^(-1/2) = std::sqrt(beta*p).
177  // Therefore, transform the original variables and log-likelihood
178  // derivatives to q-space.
179 
180  double q = 1. / std::sqrt(k);
181  double varq = vark / (4. * k * k * k);
182  double derivq1 = (-2. * k / q) * derivk1;
183  double derivq2 = 6. * k * k * derivk1 + 4. * k * k * k * derivk2;
184 
185  if (derivq2 < 0.) {
186 
187  // Estimate the measurement in q-space.
188 
189  double q1 = q - derivq1 / derivq2;
190  double varq1 = -1. / derivq2;
191 
192  // Get updated estimated q, combining original estimate and
193  // update from the current measurement.
194 
195  double newvarq = 1. / (1. / varq + 1. / varq1);
196  double newq = newvarq * (q / varq + q1 / varq1);
197  q = newq;
198  varq = newvarq;
199 
200  // Calculate updated c = 1./p and variance.
201 
202  double q2 = q * q;
203  double q4 = q2 * q2;
204  double c2 = 2. / (q4 + q2 * std::sqrt(q4 + 4. * mass2));
205  double c = std::sqrt(c2);
206  double dcdq = -2. * (c / q) * (1. + mass2 * c2) / (1. + 2. * mass2 * c2);
207  double varc = varq * dcdq * dcdq;
208 
209  // Update result.
210 
211  invp = c;
212  var_invp = varc;
213  }
214  }
215 }
216 
218 
220  : fTrace(false)
221  , fMaxPErr(0.)
222  , fGoodPErr(0.)
223  , fMaxIncChisq(0.)
224  , fMaxSeedIncChisq(0.)
225  , fMaxSmoothIncChisq(0.)
226  , fMaxEndChisq(0.)
227  , fMinLHits(0)
228  , fMaxLDist(0.)
229  , fMaxPredDist(0.)
230  , fMaxSeedPredDist(0.)
231  , fMaxPropDist(0.)
232  , fMinSortDist(0.)
233  , fMaxSortDist(0.)
234  , fMaxSamePlane(0)
235  , fGapDist(0.)
236  , fMaxNoiseHits(0)
237  , fMinSampleDist(0.)
238  , fFitMomRange(false)
239  , fFitMomMS(false)
240  , fGTrace(false)
241  , fGTraceWW(0)
242  , fGTraceWH(0)
243  , fPlane(-1)
244  , fInfoPad(0)
245  , fMessages(0)
246 {
247  mf::LogInfo("KalmanFilterAlg") << "KalmanFilterAlg instantiated.";
248 
249  fTrace = pset.get<bool>("Trace");
250  fMaxPErr = pset.get<double>("MaxPErr");
251  fGoodPErr = pset.get<double>("GoodPErr");
252  fMaxIncChisq = pset.get<double>("MaxIncChisq");
253  fMaxSeedIncChisq = pset.get<double>("MaxSeedIncChisq");
254  fMaxSmoothIncChisq = pset.get<double>("MaxSmoothIncChisq");
255  fMaxEndChisq = pset.get<double>("MaxEndChisq");
256  fMinLHits = pset.get<int>("MinLHits");
257  fMaxLDist = pset.get<double>("MaxLDist");
258  fMaxPredDist = pset.get<double>("MaxPredDist");
259  fMaxSeedPredDist = pset.get<double>("MaxSeedPredDist");
260  fMaxPropDist = pset.get<double>("MaxPropDist");
261  fMinSortDist = pset.get<double>("MinSortDist");
262  fMaxSortDist = pset.get<double>("MaxSortDist");
263  fMaxSamePlane = pset.get<int>("MaxSamePlane");
264  fGapDist = pset.get<double>("GapDist");
265  fMaxNoiseHits = pset.get<double>("MaxNoiseHits");
266  fMinSampleDist = pset.get<double>("MinSampleDist");
267  fFitMomRange = pset.get<bool>("FitMomRange");
268  fFitMomMS = pset.get<bool>("FitMomMS");
269  fGTrace = pset.get<bool>("GTrace");
270  if (fGTrace) {
271  fGTraceWW = pset.get<double>("GTraceWW");
272  fGTraceWH = pset.get<double>("GTraceWH");
273  fGTraceXMin = pset.get<double>("GTraceXMin");
274  fGTraceXMax = pset.get<double>("GTraceXMax");
275  fGTraceZMin = pset.get<std::vector<double>>("GTraceZMin");
276  fGTraceZMax = pset.get<std::vector<double>>("GTraceZMax");
277  }
278 }
279 
303  KGTrack& trg,
304  const Propagator& prop,
307  bool linear) const
308 {
309  // Direction must be forward or backward (unknown is not allowed).
310 
311  if (dir != Propagator::FORWARD && dir != Propagator::BACKWARD)
312  throw cet::exception("KalmanFilterAlg") << "No direction for Kalman fit.\n";
313 
314  // Set up canvas for graphical trace.
315 
316  if (fGTrace) {
317 
318  // Make a new canvas with a unique name.
319 
320  static int cnum = 0;
321  ++cnum;
322  std::ostringstream ostr;
323  ostr << "khit" << cnum;
324  fCanvases.emplace_back(
325  new TCanvas(ostr.str().c_str(), ostr.str().c_str(), fGTraceWW, fGTraceWH));
326  fPads.clear();
327  fMarkerMap.clear();
328  int nview = 3;
329  fCanvases.back()->Divide(2, nview / 2 + 1);
330 
331  // Make subpad for each view.
332 
333  for (int iview = 0; iview < nview; ++iview) {
334 
335  std::ostringstream ostr;
336  ostr << "Plane " << iview;
337 
338  fCanvases.back()->cd(iview + 1);
339  double zmin = 0.06;
340  double zmax = 0.94;
341  double xmin = 0.04;
342  double xmax = 0.95;
343  TPad* p = new TPad(ostr.str().c_str(), ostr.str().c_str(), zmin, xmin, zmax, xmax);
344  p->SetBit(kCanDelete); // Give away ownership.
345  p->Range(fGTraceZMin[iview], fGTraceXMin, fGTraceZMax[iview], fGTraceXMax);
346  p->SetFillStyle(4000); // Transparent.
347  p->Draw();
348  fPads.push_back(p);
349 
350  // Draw label.
351 
352  TText* t = new TText(zmax - 0.02, xmax - 0.03, ostr.str().c_str());
353  t->SetBit(kCanDelete); // Give away ownership.
354  t->SetTextAlign(33);
355  t->SetTextFont(42);
356  t->SetTextSize(0.04);
357  t->Draw();
358 
359  // Draw axes.
360 
361  TGaxis* pz1 =
362  new TGaxis(zmin, xmin, zmax, xmin, fGTraceZMin[iview], fGTraceZMax[iview], 510, "");
363  pz1->SetBit(kCanDelete); // Give away ownership.
364  pz1->Draw();
365 
366  TGaxis* px1 = new TGaxis(zmin, xmin, zmin, xmax, fGTraceXMin, fGTraceXMax, 510, "");
367  px1->SetBit(kCanDelete); // Give away ownership.
368  px1->Draw();
369 
370  TGaxis* pz2 =
371  new TGaxis(zmin, xmax, zmax, xmax, fGTraceZMin[iview], fGTraceZMax[iview], 510, "-");
372  pz2->SetBit(kCanDelete); // Give away ownership.
373  pz2->Draw();
374 
375  TGaxis* px2 = new TGaxis(zmax, xmin, zmax, xmax, fGTraceXMin, fGTraceXMax, 510, "L+");
376  px2->SetBit(kCanDelete); // Give away ownership.
377  px2->Draw();
378  }
379 
380  // Draw legend.
381 
382  fCanvases.back()->cd(nview + 1);
383  fInfoPad = gPad;
384  TLegend* leg = new TLegend(0.6, 0.5, 0.99, 0.99);
385  leg->SetBit(kCanDelete); // Give away ownership.
386 
387  TLegendEntry* entry = 0;
388  TMarker* m = 0;
389 
390  m = new TMarker(0., 0., 20);
391  m->SetBit(kCanDelete);
392  m->SetMarkerSize(1.2);
393  m->SetMarkerColor(kRed);
394  entry = leg->AddEntry(m, "Hits on Track", "P");
395  entry->SetBit(kCanDelete);
396 
397  m = new TMarker(0., 0., 20);
398  m->SetBit(kCanDelete);
399  m->SetMarkerSize(1.2);
400  m->SetMarkerColor(kOrange);
401  entry = leg->AddEntry(m, "Smoothed Hits on Track", "P");
402  entry->SetBit(kCanDelete);
403 
404  m = new TMarker(0., 0., 20);
405  m->SetBit(kCanDelete);
406  m->SetMarkerSize(1.2);
407  m->SetMarkerColor(kBlack);
408  entry = leg->AddEntry(m, "Available Hits", "P");
409  entry->SetBit(kCanDelete);
410 
411  m = new TMarker(0., 0., 20);
412  m->SetBit(kCanDelete);
413  m->SetMarkerSize(1.2);
414  m->SetMarkerColor(kBlue);
415  entry = leg->AddEntry(m, "Rejected Hits", "P");
416  entry->SetBit(kCanDelete);
417 
418  m = new TMarker(0., 0., 20);
419  m->SetBit(kCanDelete);
420  m->SetMarkerSize(1.2);
421  m->SetMarkerColor(kGreen);
422  entry = leg->AddEntry(m, "Removed Hits", "P");
423  entry->SetBit(kCanDelete);
424 
425  m = new TMarker(0., 0., 20);
426  m->SetBit(kCanDelete);
427  m->SetMarkerSize(1.2);
428  m->SetMarkerColor(kMagenta);
429  entry = leg->AddEntry(m, "Seed Hits", "P");
430  entry->SetBit(kCanDelete);
431 
432  m = new TMarker(0., 0., 20);
433  m->SetBit(kCanDelete);
434  m->SetMarkerSize(1.2);
435  m->SetMarkerColor(kCyan);
436  entry = leg->AddEntry(m, "Unreachable Seed Hits", "P");
437  entry->SetBit(kCanDelete);
438 
439  leg->Draw();
440 
441  // Draw message box.
442 
443  fMessages = new TPaveText(0.01, 0.01, 0.55, 0.99, "");
444  fMessages->SetBit(kCanDelete);
445  fMessages->SetTextFont(42);
446  fMessages->SetTextSize(0.04);
447  fMessages->SetTextAlign(12);
448  fMessages->Draw();
449  TText* t = fMessages->AddText("Enter buildTrack");
450  t->SetBit(kCanDelete);
451 
452  fCanvases.back()->Update();
453  }
454 
455  // Sort container using this seed track.
456 
457  hits.sort(trk, true, prop, Propagator::UNKNOWN);
458 
459  // Draw hits and populate hit->marker map.
460 
461  if (fGTrace && fCanvases.size() > 0) {
462 
463  // Loop over sorted KHitGroups.
464  // Paint sorted seed hits magenta.
465 
466  const std::list<KHitGroup>& groups = hits.getSorted();
467  for (auto const& gr : groups) {
468 
469  // Loop over hits in this group.
470 
471  const std::vector<std::shared_ptr<const KHitBase>>& phits = gr.getHits();
472  for (auto const& phit : phits) {
473  const KHitBase& hit = *phit;
474  int pl = hit.getMeasPlane();
475  if (pl >= 0 && pl < int(fPads.size())) {
476  auto const [z, x] = hit_position(hit);
477  TMarker* marker = new TMarker(z, x, 20);
478  fMarkerMap[hit.getID()] = marker;
479  fPads[pl]->cd();
480  marker->SetBit(kCanDelete); // Give away ownership.
481  marker->SetMarkerSize(0.5);
482  marker->SetMarkerColor(kMagenta);
483  marker->Draw();
484  }
485  }
486  }
487 
488  // Loop over unsorted KHitGroups.
489  // Paint unsorted seed hits cyan.
490  // There should be few, if any, unsorted seed hits.
491 
492  const std::list<KHitGroup>& ugroups = hits.getUnsorted();
493  for (auto const& gr : ugroups) {
494 
495  // Loop over hits in this group.
496 
497  const std::vector<std::shared_ptr<const KHitBase>>& phits = gr.getHits();
498  for (auto const& phit : phits) {
499  const KHitBase& hit = *phit;
500  int pl = hit.getMeasPlane();
501  if (pl >= 0 && pl < int(fPads.size())) {
502  auto const [z, x] = hit_position(hit);
503  TMarker* marker = new TMarker(z, x, 20);
504  fMarkerMap[hit.getID()] = marker;
505  fPads[pl]->cd();
506  marker->SetBit(kCanDelete); // Give away ownership.
507  marker->SetMarkerSize(0.5);
508  marker->SetMarkerColor(kCyan);
509  marker->Draw();
510  }
511  }
512  }
513  fCanvases.back()->Update();
514  }
515 
516  // Loop over measurements (KHitGroup) from sorted list.
517 
518  double tchisq = 0.; // Cumulative chisquare.
519  double path = 0.; // Cumulative path distance.
520  int step = 0; // Step count.
521  int nsame = 0; // Number of consecutive measurements in same plane.
522  int last_plane = -1; // Last plane.
523  bool has_pref_plane = false; // Set to true when first preferred plane hit is added to track.
524 
525  // Make a copy of the starting track, in the form of a KFitTrack,
526  // which we will update as we go.
527 
528  TrackError err;
529  trk.getSurface()->getStartingError(err);
530  KETrack tre(trk, err);
531  KFitTrack trf(tre, path, tchisq);
532 
533  // Also use the starting track as the reference track for linearized
534  // propagation, until the track is established with reasonably small
535  // errors.
536 
537  KTrack ref(trk);
538  KTrack* pref = &ref;
539 
540  mf::LogInfo log("KalmanFilterAlg");
541 
542  // Loop over measurement groups (KHitGroups).
543 
544  while (hits.getSorted().size() > 0) {
545  ++step;
546  if (fTrace) {
547  log << "Build Step " << step << "\n";
548  log << "KGTrack has " << trg.numHits() << " hits.\n";
549  log << trf;
550  }
551 
552  // Get an iterator for the next KHitGroup.
553 
555  if (dir == Propagator::FORWARD)
556  it = hits.getSorted().begin();
557  else {
558  it = hits.getSorted().end();
559  --it;
560  }
561  const KHitGroup& gr = *it;
562 
563  if (fTrace) {
564  double path_est = gr.getPath();
565  log << "Next surface: " << *(gr.getSurface()) << "\n";
566  log << " Estimated path length of hit group = " << path_est << "\n";
567  }
568 
569  // Get the next prediction surface. If this KHitGroup is on the
570  // preferred plane, use that as the prediction surface.
571  // Otherwise, use the current track surface as the prediction
572  // surface.
573 
574  std::shared_ptr<const Surface> psurf = trf.getSurface();
575  if (gr.getPlane() < 0) throw cet::exception("KalmanFilterAlg") << "negative plane?\n";
576  if (fPlane < 0 || gr.getPlane() < 0 || fPlane == gr.getPlane()) psurf = gr.getSurface();
577 
578  // Propagate track to the prediction surface.
579 
580  std::optional<double> dist = prop.noise_prop(trf, psurf, Propagator::UNKNOWN, true, pref);
581  if (dist && std::abs(*dist) > fMaxPropDist) dist = std::nullopt;
582  double ds = 0.;
583 
584  if (dist) {
585 
586  // Propagation succeeded.
587  // Update cumulative path distance and track status.
588 
589  ds = *dist;
590  path += ds;
591  trf.setPath(path);
592  if (dir == Propagator::FORWARD)
594  else {
596  }
597  if (fTrace) {
598  log << "After propagation\n";
599  log << " Incremental propagation distance = " << ds << "\n";
600  log << " Path length of prediction surface = " << path << "\n";
601  log << "KGTrack has " << trg.numHits() << " hits.\n";
602  log << trf;
603  }
604 
605  // Loop over measurements in this group.
606 
607  const std::vector<std::shared_ptr<const KHitBase>>& hits = gr.getHits();
608  double best_chisq = 0.;
609  std::shared_ptr<const KHitBase> best_hit;
610  for (std::vector<std::shared_ptr<const KHitBase>>::const_iterator ihit = hits.begin();
611  ihit != hits.end();
612  ++ihit) {
613  const KHitBase& hit = **ihit;
614 
615  // Turn this hit blue.
616 
617  if (fGTrace && fCanvases.size() > 0) {
618  auto marker_it = fMarkerMap.find(hit.getID());
619  if (marker_it != fMarkerMap.end()) {
620  TMarker* marker = marker_it->second;
621  marker->SetMarkerColor(kBlue);
622  }
623  }
624 
625  // Update predction using current track hypothesis and get
626  // incremental chisquare.
627 
628  if (hit.predict(trf, prop)) {
629  double chisq = hit.getChisq();
630  double preddist = hit.getPredDistance();
631  if (fTrace) {
632  log << "Trying Hit.\n"
633  << hit << "\nchisq = " << chisq << "\n"
634  << "prediction distance = " << preddist << "\n";
635  }
636  if ((!has_pref_plane || abs(preddist) < fMaxSeedPredDist) &&
637  (best_hit.get() == 0 || chisq < best_chisq)) {
638  best_chisq = chisq;
639  if (chisq < fMaxSeedIncChisq) best_hit = *ihit;
640  }
641  }
642  }
643  if (fTrace) {
644  log << "Best hit incremental chisquare = " << best_chisq << "\n";
645  if (best_hit.get() != 0) {
646  log << "Hit after prediction\n";
647  log << *best_hit;
648  }
649  else
650  log << "No hit passed chisquare cut.\n";
651  }
652  if (fGTrace && fCanvases.size() > 0) fCanvases.back()->Update();
653 
654  // If we found a best measurement, and if the incremental
655  // chisquare passes the cut, add it to the track and update
656  // fit information.
657 
658  bool update_ok = false;
659  if (best_hit.get() != 0) {
660  KFitTrack trf0(trf);
661  best_hit->update(trf);
662  update_ok = trf.isValid();
663  if (!update_ok) trf = trf0;
664  }
665  if (update_ok) {
666  ds += best_hit->getPredDistance();
667  tchisq += best_chisq;
668  trf.setChisq(tchisq);
669  if (dir == Propagator::FORWARD)
671  else {
673  }
674 
675  // If the pointing error got too large, and there is no
676  // reference track, quit.
677 
678  if (pref == 0 && trf.PointingError() > fMaxPErr) {
679  if (fTrace) log << "Quitting because pointing error got too large.\n";
680  break;
681  }
682 
683  // Test number of consecutive measurements in the same plane.
684 
685  if (gr.getPlane() >= 0) {
686  if (gr.getPlane() == last_plane)
687  ++nsame;
688  else {
689  nsame = 1;
690  last_plane = gr.getPlane();
691  }
692  }
693  else {
694  nsame = 0;
695  last_plane = -1;
696  }
697  if (nsame <= fMaxSamePlane) {
698 
699  // Turn best hit red.
700 
701  if (fGTrace && fCanvases.size() > 0) {
702  int pl = best_hit->getMeasPlane();
703  if (pl >= 0 && pl < int(fPads.size())) {
704  auto marker_it = fMarkerMap.find(best_hit->getID());
705  if (marker_it != fMarkerMap.end()) {
706  TMarker* marker = marker_it->second;
707  marker->SetMarkerColor(kRed);
708 
709  // Redraw marker so that it will be on top.
710 
711  fPads[pl]->cd();
712  marker->Draw();
713  }
714  }
715  fCanvases.back()->Update();
716  }
717 
718  // Make a KHitTrack and add it to the KGTrack.
719 
720  KHitTrack trh(trf, best_hit);
721  trg.addTrack(trh);
722  if (fPlane == gr.getPlane()) has_pref_plane = true;
723 
724  // Decide if we want to kill the reference track.
725 
726  if (!linear && pref != 0 && int(trg.numHits()) >= fMinLHits &&
727  (trf.PointingError() < fGoodPErr || path > fMaxLDist)) {
728  pref = 0;
729  if (fTrace) log << "Killing reference track.\n";
730  }
731 
732  if (fTrace) {
733  log << "After update\n";
734  log << "KGTrack has " << trg.numHits() << " hits.\n";
735  log << trf;
736  }
737  }
738  }
739  }
740 
741  // The current KHitGroup is now resolved.
742  // Move it to unused list.
743 
744  hits.getUnused().splice(hits.getUnused().end(), hits.getSorted(), it);
745 
746  // If the propagation distance was the wrong direction, resort the measurements.
747 
748  if (pref == 0 && dist &&
749  ((dir == Propagator::FORWARD && (ds < fMinSortDist || ds > fMaxSortDist)) ||
750  (dir == Propagator::BACKWARD && (-ds < fMinSortDist || -ds > fMaxSortDist)))) {
751  if (fTrace) log << "Resorting measurements.\n";
752  hits.sort(trf, true, prop, dir);
753  }
754  }
755 
756  // Clean track.
757 
758  cleanTrack(trg);
759 
760  // Set the fit status of the last added KHitTrack to optimal and get
761  // the final chisquare and path length.
762 
763  double fchisq = 0.;
764  path = 0.;
765  if (trg.isValid()) {
766  path = trg.endTrack().getPath() - trg.startTrack().getPath();
767  if (dir == Propagator::FORWARD) {
769  fchisq = trg.endTrack().getChisq();
770  }
771  else {
773  fchisq = trg.startTrack().getChisq();
774  }
775  }
776 
777  // Summary.
778 
779  log << "KalmanFilterAlg build track summary.\n"
780  << "Build direction = " << (dir == Propagator::FORWARD ? "FORWARD" : "BACKWARD") << "\n"
781  << "Track has " << trg.numHits() << " hits.\n"
782  << "Track length = " << path << "\n"
783  << "Track chisquare = " << fchisq << "\n";
784 
785  // Done. Return success if we added at least one measurement.
786 
787  bool ok = trg.numHits() > 0;
788 
789  if (fGTrace && fCanvases.size() > 0) {
790  TText* t = 0;
791  if (ok)
792  t = fMessages->AddText("Exit buildTrack, status success");
793  else
794  t = fMessages->AddText("Exit buildTrack, status fail");
795  t->SetBit(kCanDelete);
796  fInfoPad->Modified();
797  fCanvases.back()->Update();
798  }
799 
800  return ok;
801 }
802 
836 bool trkf::KalmanFilterAlg::smoothTrack(KGTrack& trg, KGTrack* trg1, const Propagator& prop) const
837 {
838  if (not trg.isValid()) {
839  // It is an error if the KGTrack is not valid.
840  return false;
841  }
842 
843  bool result = false;
844 
845  // Examine the track endpoints and figure out which end of the track
846  // to start from. The fit always starts at the optimal end. It is
847  // an error if neither end point is optimal. Do nothing and return
848  // success if both end points are optimal.
849 
850  const KHitTrack& trh0 = trg.startTrack();
851  const KHitTrack& trh1 = trg.endTrack();
852  KFitTrack::FitStatus stat0 = trh0.getStat();
853  KFitTrack::FitStatus stat1 = trh1.getStat();
854  bool dofit = false;
855 
856  // Remember starting direction, track, and distance.
857 
859  const KTrack* trk = 0;
860  double path = 0.;
861 
862  if (stat0 == KFitTrack::OPTIMAL) {
863  if (stat1 == KFitTrack::OPTIMAL) {
864 
865  // Both ends optimal (do nothing, return success).
866 
867  dofit = false;
868  result = true;
869  }
870  else {
871 
872  // Start optimal.
873 
874  dofit = true;
875  dir = Propagator::FORWARD;
876  trk = &trh0;
877  path = 0.;
878  }
879  }
880  else {
881  if (stat1 == KFitTrack::OPTIMAL) {
882 
883  // End optimal.
884 
885  dofit = true;
886  dir = Propagator::BACKWARD;
887  trk = &trh1;
888  path = trh1.getPath();
889  }
890  else {
891 
892  // Neither end optimal (do nothing, return failure).
893 
894  dofit = false;
895  result = false;
896  }
897  }
898  if (dofit) {
899  if (!trk)
900  throw cet::exception("KalmanFilterAlg") << "KalmanFilterAlg::smoothTrack(): no track!\n";
901 
902  // Cumulative chisquare.
903 
904  double tchisq = 0.;
905 
906  // Construct starting KFitTrack with track information and distance
907  // taken from the optimal end, but with "infinite" errors.
908 
909  TrackError err;
910  trk->getSurface()->getStartingError(err);
911  KETrack tre(*trk, err);
912  KFitTrack trf(tre, path, tchisq);
913 
914  // Make initial reference track to be same as initial fit track.
915 
916  KTrack ref(trf);
917 
918  // Loop over KHitTracks contained in KGTrack.
919 
922  switch (dir) {
923  case Propagator::FORWARD:
924  it = trg.getTrackMap().begin();
925  itend = trg.getTrackMap().end();
926  break;
928  it = trg.getTrackMap().end();
929  itend = trg.getTrackMap().begin();
930  break;
931  default:
932  throw cet::exception("KalmanFilterAlg")
933  << "KalmanFilterAlg::smoothTrack(): invalid direction\n";
934  } // switch
935 
936  mf::LogInfo log("KalmanFilterAlg");
937 
938  // Loop starts here.
939 
940  result = true; // Result success unless we find an error.
941  int step = 0; // Step count.
942  while (dofit && it != itend) {
943  ++step;
944  if (fTrace) {
945  log << "Smooth Step " << step << "\n";
946  log << "Reverse fit track:\n";
947  log << trf;
948  }
949 
950  // For backward fit, decrement iterator at start of loop.
951 
952  if (dir == Propagator::BACKWARD) --it;
953 
954  KHitTrack& trh = (*it).second;
955  if (fTrace) {
956  log << "Forward track:\n";
957  log << trh;
958  }
959 
960  // Extract measurement.
961 
962  const KHitBase& hit = *(trh.getHit());
963 
964  // Propagate KFitTrack to the next track surface.
965 
966  std::shared_ptr<const Surface> psurf = trh.getSurface();
967  std::optional<double> dist = prop.noise_prop(trf, psurf, Propagator::UNKNOWN, true, &ref);
968 
969  // Check if propagation succeeded. If propagation fails, this
970  // measurement will be dropped from the unidirectional fit
971  // track. This measurement will still be in the original
972  // track, but with a status other than optimal.
973 
974  if (dist) {
975 
976  // Propagation succeeded.
977  // Update cumulative path distance and track status.
978 
979  double ds = *dist;
980  path += ds;
981  trf.setPath(path);
982  if (dir == Propagator::FORWARD)
984  else {
986  }
987  if (fTrace) {
988  log << "Reverse fit track after propagation:\n";
989  log << " Propagation distance = " << ds << "\n";
990  log << trf;
991  }
992 
993  // See if we have the proper information to calculate an optimal track
994  // at this surface (should normally be possible).
995 
996  KFitTrack::FitStatus stat = trh.getStat();
997  KFitTrack::FitStatus newstat = trf.getStat();
998 
999  if ((newstat == KFitTrack::FORWARD_PREDICTED && stat == KFitTrack::BACKWARD) ||
1000  (newstat == KFitTrack::BACKWARD_PREDICTED && stat == KFitTrack::FORWARD)) {
1001 
1002  // Update stored KHitTrack to be optimal.
1003 
1004  bool ok = trh.combineFit(trf);
1005 
1006  // Update the stored path distance to be from the currently fitting track.
1007 
1008  trh.setPath(trf.getPath());
1009 
1010  // Update reference track.
1011 
1012  ref = trh;
1013 
1014  // If combination failed, abandon the fit and return failure.
1015 
1016  if (!ok) {
1017  dofit = false;
1018  result = false;
1019  break;
1020  }
1021  if (fTrace) {
1022  log << "Combined track:\n";
1023  log << trh;
1024  }
1025  }
1026 
1027  // Update measurement predction using current track hypothesis.
1028 
1029  if (not hit.predict(trf, prop, &ref)) {
1030 
1031  // Abandon the fit and return failure.
1032 
1033  dofit = false;
1034  result = false;
1035  break;
1036  }
1037 
1038  // Prediction succeeded. Get incremental chisquare. If this
1039  // hit fails incremental chisquare cut, this hit will be
1040  // dropped from the unidirecitonal Kalman fit track, but may
1041  // still be in the smoothed track.
1042 
1043  double chisq = hit.getChisq();
1044  if (chisq < fMaxSmoothIncChisq) {
1045 
1046  // Update the reverse fitting track using the current measurement
1047  // (both track parameters and status).
1048 
1049  KFitTrack trf0(trf);
1050  hit.update(trf);
1051  bool update_ok = trf.isValid();
1052  if (!update_ok)
1053  trf = trf0;
1054  else {
1055  tchisq += chisq;
1056  trf.setChisq(tchisq);
1057 
1058  if (dir == Propagator::FORWARD)
1060  else {
1062  }
1063  if (fTrace) {
1064  log << "Reverse fit track after update:\n";
1065  log << trf;
1066  }
1067  if (fGTrace && fCanvases.size() > 0) {
1068  auto marker_it = fMarkerMap.find(hit.getID());
1069  if (marker_it != fMarkerMap.end()) {
1070  TMarker* marker = marker_it->second;
1071  marker->SetMarkerColor(kOrange);
1072  }
1073  fCanvases.back()->Update();
1074  }
1075 
1076  // If unidirectional track pointer is not null, make a
1077  // KHitTrack and save it in the unidirectional track.
1078 
1079  if (trg1 != 0) {
1080  KHitTrack trh1(trf, trh.getHit());
1081  trg1->addTrack(trh1);
1082  }
1083  }
1084  }
1085  }
1086 
1087  // For forward fit, increment iterator at end of loop.
1088 
1089  if (dir == Propagator::FORWARD) ++it;
1090 
1091  } // Loop over KHitTracks.
1092 
1093  // If fit was successful and the unidirectional track pointer
1094  // is not null and the track is valid, set the fit status of
1095  // the last added KHitTrack to optimal.
1096 
1097  if (result && trg1 != 0 && trg1->isValid()) {
1098  if (dir == Propagator::FORWARD)
1100  else {
1102  }
1103  }
1104 
1105  // Recalibrate track map.
1106 
1107  trg.recalibrate();
1108 
1109  } // Do fit.
1110 
1111  // Get the final chisquare.
1112 
1113  double fchisq = 0.5 * (trg.startTrack().getChisq() + trg.endTrack().getChisq());
1114 
1115  // Summary.
1116 
1117  mf::LogInfo log("KalmanFilterAlg");
1118  log << "KalmanFilterAlg smooth track summary.\n"
1119  << "Smooth direction = " << (dir == Propagator::FORWARD ? "FORWARD" : "BACKWARD") << "\n"
1120  << "Track has " << trg.numHits() << " hits.\n"
1121  << "Track length = " << trg.endTrack().getPath() - trg.startTrack().getPath() << "\n"
1122  << "Track chisquare = " << fchisq << "\n";
1123 
1124  return result;
1125 }
1126 
1144  const Propagator& prop,
1145  KHitContainer& hits) const
1146 {
1147  // Default result failure.
1148 
1149  bool result = false;
1150 
1151  if (fGTrace && fCanvases.size() > 0) {
1152  TText* t = fMessages->AddText("Enter extendTrack");
1153  t->SetBit(kCanDelete);
1154  fInfoPad->Modified();
1155  fCanvases.back()->Update();
1156  }
1157 
1158  // Remember the original number of measurement.
1159 
1160  unsigned int nhits0 = trg.numHits();
1161 
1162  // It is an error if the KGTrack is not valid.
1163 
1164  if (trg.isValid()) {
1165  mf::LogInfo log("KalmanFilterAlg");
1166 
1167  // Examine the track endpoints and figure out which end of the
1168  // track to extend. The track is always extended from the optimal
1169  // end. It is an error if neither end point is optimal, or both
1170  // endoints are optimal. Reset the status of the optimal, and
1171  // make a copy of the starting track fit. Also get starting path
1172  // and chisquare.
1173 
1174  KHitTrack& trh0 = trg.startTrack();
1175  KHitTrack& trh1 = trg.endTrack();
1176  KFitTrack::FitStatus stat0 = trh0.getStat();
1177  KFitTrack::FitStatus stat1 = trh1.getStat();
1178  bool dofit = false;
1180  KFitTrack trf;
1181  double path = 0.;
1182  double tchisq = 0.;
1183 
1184  if (stat0 == KFitTrack::OPTIMAL) {
1185  if (stat1 == KFitTrack::OPTIMAL) {
1186 
1187  // Both ends optimal (do nothing, return failure).
1188 
1189  dofit = false;
1190  result = false;
1191  return result;
1192  }
1193  else {
1194 
1195  // Start optimal. Extend backward.
1196 
1197  dofit = true;
1198  dir = Propagator::BACKWARD;
1200  trf = trh0;
1201  path = trh0.getPath();
1202  tchisq = trh0.getChisq();
1203  }
1204  }
1205  else {
1206  if (stat1 == KFitTrack::OPTIMAL) {
1207 
1208  // End optimal. Extend forward.
1209 
1210  dofit = true;
1211  dir = Propagator::FORWARD;
1213  trf = trh1;
1214  path = trh1.getPath();
1215  tchisq = trh1.getChisq();
1216 
1217  // Make sure forward extend track momentum is over some
1218  // minimum value.
1219 
1220  if (trf.getVector()(4) > 5.) {
1221  trf.getVector()(4) = 5.;
1222  trf.getError()(4, 4) = 5.;
1223  }
1224  }
1225  else {
1226 
1227  // Neither end optimal (do nothing, return failure).
1228 
1229  dofit = false;
1230  result = false;
1231  return result;
1232  }
1233  }
1234  if (dofit) {
1235 
1236  // Sort hit container using starting track.
1237 
1238  hits.sort(trf, true, prop, dir);
1239 
1240  // Draw and add hits in hit->marker map that are not already there.
1241 
1242  if (fGTrace && fCanvases.size() > 0) {
1243 
1244  // Loop over sorted KHitGroups.
1245  // Paint sorted hits black.
1246 
1247  const std::list<KHitGroup>& groups = hits.getSorted();
1248  for (auto const& gr : groups) {
1249 
1250  // Loop over hits in this group.
1251 
1252  const std::vector<std::shared_ptr<const KHitBase>>& phits = gr.getHits();
1253  for (auto const& phit : phits) {
1254  const KHitBase& hit = *phit;
1255  int pl = hit.getMeasPlane();
1256  if (pl >= 0 && pl < int(fPads.size())) {
1257 
1258  // Is this hit already in map?
1259 
1260  TMarker* marker = 0;
1261  auto marker_it = fMarkerMap.find(hit.getID());
1262  if (marker_it == fMarkerMap.end()) {
1263  auto const [z, x] = hit_position(hit);
1264  marker = new TMarker(z, x, 20);
1265  fMarkerMap[hit.getID()] = marker;
1266  fPads[pl]->cd();
1267  marker->SetBit(kCanDelete); // Give away ownership.
1268  marker->SetMarkerSize(0.5);
1269  marker->Draw();
1270  }
1271  else
1272  marker = marker_it->second;
1273  marker->SetMarkerColor(kBlack);
1274  }
1275  }
1276  }
1277 
1278  // Loop over unsorted KHitGroups.
1279  // Paint unsorted hits blue.
1280 
1281  const std::list<KHitGroup>& ugroups = hits.getUnsorted();
1282  for (auto const& gr : ugroups) {
1283 
1284  // Loop over hits in this group.
1285 
1286  const std::vector<std::shared_ptr<const KHitBase>>& phits = gr.getHits();
1287  for (auto const& phit : phits) {
1288  const KHitBase& hit = *phit;
1289  int pl = hit.getMeasPlane();
1290  if (pl >= 0 && pl < int(fPads.size())) {
1291 
1292  // Is this hit already in map?
1293 
1294  TMarker* marker = 0;
1295  auto marker_it = fMarkerMap.find(hit.getID());
1296  if (marker_it == fMarkerMap.end()) {
1297  auto const [z, x] = hit_position(hit);
1298  marker = new TMarker(z, x, 20);
1299  fMarkerMap[hit.getID()] = marker;
1300  fPads[pl]->cd();
1301  marker->SetBit(kCanDelete); // Give away ownership.
1302  marker->SetMarkerSize(0.5);
1303  marker->Draw();
1304  }
1305  else
1306  marker = marker_it->second;
1307  marker->SetMarkerColor(kBlue);
1308  }
1309  }
1310  }
1311  fCanvases.back()->Update();
1312  }
1313 
1314  // Extend loop starts here.
1315 
1316  int step = 0;
1317  int nsame = 0;
1318  int last_plane = -1;
1319  while (hits.getSorted().size() > 0) {
1320  ++step;
1321  if (fTrace) {
1322  log << "Extend Step " << step << "\n";
1323  log << "KGTrack has " << trg.numHits() << " hits.\n";
1324  log << trf;
1325  }
1326 
1327  // Get an iterator for the next KHitGroup.
1328 
1330  switch (dir) {
1331  case Propagator::FORWARD: it = hits.getSorted().begin(); break;
1332  case Propagator::BACKWARD:
1333  it = hits.getSorted().end();
1334  --it;
1335  break;
1336  default:
1337  throw cet::exception("KalmanFilterAlg")
1338  << "KalmanFilterAlg::extendTrack(): invalid direction\n";
1339  } // switch
1340  const KHitGroup& gr = *it;
1341 
1342  if (fTrace) {
1343  double path_est = gr.getPath();
1344  log << "Next surface: " << *(gr.getSurface()) << "\n";
1345  log << " Estimated path length of hit group = " << path_est << "\n";
1346  }
1347 
1348  // Get the next prediction surface. If this KHitGroup is on the
1349  // preferred plane, use that as the prediction surface.
1350  // Otherwise, use the current track surface as the prediction
1351  // surface.
1352 
1353  std::shared_ptr<const Surface> psurf = trf.getSurface();
1354  if (gr.getPlane() < 0)
1355  throw cet::exception("KalmanFilterAlg")
1356  << "KalmanFilterAlg::extendTrack(): negative plane?\n";
1357  if (fPlane < 0 || gr.getPlane() < 0 || fPlane == gr.getPlane()) psurf = gr.getSurface();
1358 
1359  // Propagate track to the prediction surface.
1360 
1361  std::optional<double> dist = prop.noise_prop(trf, psurf, Propagator::UNKNOWN, true);
1362  if (dist && std::abs(*dist) > fMaxPropDist) dist = std::nullopt;
1363  double ds = 0.;
1364 
1365  if (dist) {
1366 
1367  // Propagation succeeded.
1368  // Update cumulative path distance and track status.
1369 
1370  ds = *dist;
1371  path += ds;
1372  trf.setPath(path);
1373  if (dir == Propagator::FORWARD)
1375  else {
1377  }
1378  if (fTrace) {
1379  log << "After propagation\n";
1380  log << " Incremental distance = " << ds << "\n";
1381  log << " Track path length = " << path << "\n";
1382  log << "KGTrack has " << trg.numHits() << " hits.\n";
1383  log << trf;
1384  }
1385 
1386  // Loop over measurements in this group.
1387 
1388  const std::vector<std::shared_ptr<const KHitBase>>& hits = gr.getHits();
1389  double best_chisq = 0.;
1390  std::shared_ptr<const KHitBase> best_hit;
1391  for (std::vector<std::shared_ptr<const KHitBase>>::const_iterator ihit = hits.begin();
1392  ihit != hits.end();
1393  ++ihit) {
1394  const KHitBase& hit = **ihit;
1395 
1396  // Turn this hit blue.
1397 
1398  if (fGTrace && fCanvases.size() > 0) {
1399  auto marker_it = fMarkerMap.find(hit.getID());
1400  if (marker_it != fMarkerMap.end()) {
1401  TMarker* marker = marker_it->second;
1402  marker->SetMarkerColor(kBlue);
1403  }
1404  //fCanvases.back()->Update();
1405  }
1406 
1407  // Update predction using current track hypothesis and get
1408  // incremental chisquare.
1409 
1410  bool ok = hit.predict(trf, prop);
1411  if (ok) {
1412  double chisq = hit.getChisq();
1413  double preddist = hit.getPredDistance();
1414  if (abs(preddist) < fMaxPredDist && (best_hit.get() == 0 || chisq < best_chisq)) {
1415  best_chisq = chisq;
1416  if (chisq < fMaxIncChisq) best_hit = *ihit;
1417  }
1418  }
1419  }
1420  if (fTrace) {
1421  log << "Best hit incremental chisquare = " << best_chisq << "\n";
1422  if (best_hit.get() != 0) {
1423  log << "Hit after prediction\n";
1424  log << *best_hit;
1425  }
1426  else
1427  log << "No hit passed chisquare cut.\n";
1428  }
1429  if (fGTrace && fCanvases.size() > 0) fCanvases.back()->Update();
1430 
1431  // If we found a best measurement, and if the incremental
1432  // chisquare passes the cut, add it to the track and update
1433  // fit information.
1434 
1435  bool update_ok = false;
1436  if (best_hit.get() != 0) {
1437  KFitTrack trf0(trf);
1438  best_hit->update(trf);
1439  update_ok = trf.isValid();
1440  if (!update_ok) trf = trf0;
1441  }
1442  if (update_ok) {
1443  ds += best_hit->getPredDistance();
1444  tchisq += best_chisq;
1445  trf.setChisq(tchisq);
1446  if (dir == Propagator::FORWARD)
1448  else {
1450  }
1451 
1452  // If the pointing error got too large, quit.
1453 
1454  if (trf.PointingError() > fMaxPErr) {
1455  if (fTrace) log << "Quitting because pointing error got too large.\n";
1456  break;
1457  }
1458 
1459  // Test number of consecutive measurements in the same plane.
1460 
1461  if (gr.getPlane() >= 0) {
1462  if (gr.getPlane() == last_plane)
1463  ++nsame;
1464  else {
1465  nsame = 1;
1466  last_plane = gr.getPlane();
1467  }
1468  }
1469  else {
1470  nsame = 0;
1471  last_plane = -1;
1472  }
1473  if (nsame <= fMaxSamePlane) {
1474 
1475  // Turn best hit red.
1476 
1477  if (fGTrace && fCanvases.size() > 0) {
1478  int pl = best_hit->getMeasPlane();
1479  if (pl >= 0 && pl < int(fPads.size())) {
1480  auto marker_it = fMarkerMap.find(best_hit->getID());
1481  if (marker_it != fMarkerMap.end()) {
1482  TMarker* marker = marker_it->second;
1483  marker->SetMarkerColor(kRed);
1484 
1485  // Redraw marker so that it will be on top.
1486 
1487  fPads[pl]->cd();
1488  marker->Draw();
1489  }
1490  }
1491  fCanvases.back()->Update();
1492  }
1493 
1494  // Make a KHitTrack and add it to the KGTrack.
1495 
1496  KHitTrack trh(trf, best_hit);
1497  trg.addTrack(trh);
1498 
1499  if (fTrace) {
1500  log << "After update\n";
1501  log << "KGTrack has " << trg.numHits() << " hits.\n";
1502  log << trf;
1503  }
1504  }
1505  }
1506  }
1507 
1508  // The current KHitGroup is now resolved.
1509  // Move it to unused list.
1510 
1511  hits.getUnused().splice(hits.getUnused().end(), hits.getSorted(), it);
1512 
1513  // If the propagation distance was the wrong direction, resort the measurements.
1514 
1515  if (dist && ((dir == Propagator::FORWARD && (ds < fMinSortDist || ds > fMaxSortDist)) ||
1516  (dir == Propagator::BACKWARD && (-ds < fMinSortDist || -ds > fMaxSortDist)))) {
1517  if (fTrace) log << "Resorting measurements.\n";
1518  hits.sort(trf, true, prop, dir);
1519  }
1520  }
1521  }
1522 
1523  // Clean track.
1524 
1525  cleanTrack(trg);
1526 
1527  // Set the fit status of the last added KHitTrack to optimal and
1528  // get the final chisquare and path length.
1529 
1530  double fchisq = 0.;
1531  path = 0.;
1532  if (trg.isValid()) {
1533  path = trg.endTrack().getPath() - trg.startTrack().getPath();
1534  switch (dir) {
1535  case Propagator::FORWARD:
1537  fchisq = trg.endTrack().getChisq();
1538  break;
1539  case Propagator::BACKWARD:
1541  fchisq = trg.startTrack().getChisq();
1542  break;
1543  default:
1544  throw cet::exception("KalmanFilterAlg")
1545  << "KalmanFilterAlg::extendTrack(): invalid direction [II]\n";
1546  } // switch
1547  }
1548 
1549  // Summary.
1550 
1551  log << "KalmanFilterAlg extend track summary.\n"
1552  << "Extend direction = " << (dir == Propagator::FORWARD ? "FORWARD" : "BACKWARD") << "\n"
1553  << "Track has " << trg.numHits() << " hits.\n"
1554  << "Track length = " << path << "\n"
1555  << "Track chisquare = " << fchisq << "\n";
1556  // log << trg << "\n";
1557  }
1558 
1559  // Done.
1560 
1561  result = (trg.numHits() > nhits0);
1562 
1563  if (fGTrace && fCanvases.size() > 0) {
1564  TText* t = 0;
1565  if (result)
1566  t = fMessages->AddText("Exit extendTrack, status success");
1567  else
1568  t = fMessages->AddText("Exit extendTrack, status fail");
1569  t->SetBit(kCanDelete);
1570  fInfoPad->Modified();
1571  fCanvases.back()->Update();
1572  }
1573 
1574  return result;
1575 }
1576 
1592 {
1593  if (!trg.isValid()) return false;
1594 
1595  // Extract track with lowest momentum.
1596 
1597  const KHitTrack& trh = trg.endTrack();
1598  if (trh.getStat() == KFitTrack::INVALID)
1599  throw cet::exception("KalmanFilterAlg")
1600  << "KalmanFilterAlg::fitMomentumRange(): invalid end track\n";
1601  tremom = trh;
1602 
1603  // Set track momentum to a small value.
1604 
1605  tremom.getVector()(4) = 100.;
1606  tremom.getError()(4, 0) = 0.;
1607  tremom.getError()(4, 1) = 0.;
1608  tremom.getError()(4, 2) = 0.;
1609  tremom.getError()(4, 3) = 0.;
1610  tremom.getError()(4, 4) = 10000.;
1611 
1612  // Done.
1613 
1614  return true;
1615 }
1616 
1645  const Propagator& prop,
1646  KETrack& tremom) const
1647 {
1648  // Get iterators pointing to the first and last tracks.
1649 
1650  const std::multimap<double, KHitTrack>& trackmap = trg.getTrackMap();
1651  if (trackmap.size() < 2) return false;
1653  itend[0] = trackmap.begin();
1654  itend[1] = trackmap.end();
1655  --itend[1];
1656 
1657  // Check the fit status and error matrix of the first and last
1658  // track.
1659 
1660  bool result = true;
1661 
1662  for (int i = 0; result && i < 2; ++i) {
1663  const KHitTrack& trh = itend[i]->second;
1664  KFitTrack::FitStatus stat = trh.getStat();
1665  if (stat != KFitTrack::OPTIMAL) result = false;
1666  const TrackError& err = trh.getError();
1667  for (int j = 0; j < 4; ++j) {
1668  if (err(4, j) != 0.) result = false;
1669  }
1670  }
1671  if (!result) return result;
1672 
1673  // We will periodically sample the track trajectory. At each sample
1674  // point, collect the following information.
1675  //
1676  // 1. The path distance at the sample point.
1677  // 2. The original momentum of the track at the sample point and its error.
1678  // 3. One copy of the track (KETrack) that will be propagated without noise
1679  // (infinite momentum track).
1680  // 3. A second copy of the track (KETrack) that will be propagated with the
1681  // minimum allowed momentum (range out track).
1682  // 4. A third copy of the track (KETrack) that will propagated with noise
1683  // with some intermediate momentum (noise track).
1684  //
1685  // Collect the first sample from the maximum path distance track.
1686 
1687  double s_sample = itend[1]->first;
1688  const KETrack& tre = itend[1]->second;
1689  KETrack tre_inf(tre);
1690  KTrack trk_range(tre);
1691  KETrack tre_noise(tre);
1692  tre_inf.getVector()(4) = 0.;
1693  tre_inf.getError()(4, 4) = 0.;
1694  trk_range.getVector()(4) = 100.;
1695  tre_noise.getError()(4, 4) = 0.;
1696  tre_noise.getVector()(4) = 1.;
1697  tre_noise.getError()(4, 4) = 10.;
1698  double invp0 = tre_noise.getVector()(4);
1699  double var_invp0 = tre_noise.getError()(4, 4);
1700 
1701  // Loop over fits, starting at the high path distance (low momentum)
1702  // end.
1703 
1704  for (auto it = trackmap.rbegin(); it != trackmap.rend(); ++it) {
1705  double s = it->first;
1706  const KHitTrack& trh = it->second;
1707 
1708  // Ignore non-optimal fits.
1709 
1710  KFitTrack::FitStatus stat = trh.getStat();
1711  if (stat != KFitTrack::OPTIMAL) continue;
1712 
1713  // See if this track is far enough from the previous sample to
1714  // make a new sample point.
1715 
1716  if (std::abs(s - s_sample) > fMinSampleDist) {
1717 
1718  // Propagate tracks to the current track surface.
1719 
1720  std::shared_ptr<const Surface> psurf = trh.getSurface();
1721  std::optional<double> dist_inf = prop.err_prop(tre_inf, psurf, Propagator::UNKNOWN, false);
1722  std::optional<double> dist_range =
1723  prop.vec_prop(trk_range, psurf, Propagator::UNKNOWN, false);
1724  std::optional<double> dist_noise =
1725  prop.noise_prop(tre_noise, psurf, Propagator::UNKNOWN, true);
1726 
1727  // All propagations should normally succeed. If they don't,
1728  // ignore this sample for the purpose of updating the momentum.
1729 
1730  bool momentum_updated = false;
1731  if (dist_inf && dist_range && dist_noise) {
1732 
1733  // Extract the momentum at the new sample point.
1734 
1735  double invp1 = tre_noise.getVector()(4);
1736  double var_invp1 = tre_noise.getError()(4, 4);
1737 
1738  // Get the average momentum and error for this pair of
1739  // sample points, and other data.
1740 
1741  double invp = 0.5 * (invp0 + invp1);
1742  double var_invp = 0.5 * (var_invp0 + var_invp1);
1743  double mass = tre_inf.Mass();
1744  double beta = std::sqrt(1. + mass * mass * invp * invp);
1745  double invbp = invp / beta;
1746 
1747  // Extract slope subvectors and sub-error-matrices.
1748  // We have the following variables.
1749  //
1750  // slope0 - Predicted slope vector (from infinite momentum track).
1751  // slope1 - Measured slope vector (from new sample point).
1752  // defl - Deflection (slope residual = difference between measured
1753  // and predicted slope vector).
1754  // err0 - Slope error matrix of prediction.
1755  // err1 - Slope error matrix of measurement
1756  // errc - Slope residual error matrix = err0 + err1.
1757  // errn - Noise slope error matrix divided by (1/beta*momentum).
1758 
1759  KVector<2>::type slope0 = project(tre_inf.getVector(), ublas::range(2, 4));
1760  KVector<2>::type slope1 = project(trh.getVector(), ublas::range(2, 4));
1761  KVector<2>::type defl = slope1 - slope0;
1762  KSymMatrix<2>::type err0 =
1763  project(tre_inf.getError(), ublas::range(2, 4), ublas::range(2, 4));
1764  KSymMatrix<2>::type err1 = project(trh.getError(), ublas::range(2, 4), ublas::range(2, 4));
1765  KSymMatrix<2>::type errc = err0 + err1;
1766  KSymMatrix<2>::type errn =
1767  project(tre_noise.getError(), ublas::range(2, 4), ublas::range(2, 4));
1768  errn -= err0;
1769  errn /= invbp;
1770 
1771  // Calculate updated average momentum and error.
1772 
1773  double new_invp = invp;
1774  double new_var_invp = var_invp;
1775  update_momentum(defl, errc, errn, mass, new_invp, new_var_invp);
1776 
1777  // Calculate updated momentum and error at the second sample
1778  // point.
1779 
1780  double dp = 1. / new_invp - 1. / invp;
1781  invp0 = 1. / (1. / invp1 + dp);
1782  var_invp0 = new_var_invp;
1783  momentum_updated = true;
1784 
1785  // Make sure that updated momentum is not less than minimum
1786  // allowed momentum.
1787 
1788  double invp_range = trk_range.getVector()(4);
1789  if (invp0 > invp_range) invp0 = invp_range;
1790  }
1791 
1792  // Update sample.
1793 
1794  if (momentum_updated) {
1795  s_sample = s;
1796  tre_inf = trh;
1797  tre_inf.getVector()(4) = 0.;
1798  tre_inf.getError()(4, 4) = 0.;
1799  double invp_range = trk_range.getVector()(4);
1800  trk_range = trh;
1801  trk_range.getVector()(4) = invp_range;
1802  tre_noise = trh;
1803  tre_noise.getVector()(4) = invp0;
1804  tre_noise.getError()(4, 4) = var_invp0;
1805  }
1806  }
1807  }
1808 
1809  // Propagate noise track to starting (high momentum) track surface
1810  // to get final starting momentum. This propagation should normally
1811  // always succeed, but if it doesn't, don't update the track.
1812 
1813  const KHitTrack& trh0 = itend[0]->second;
1814  std::shared_ptr<const Surface> psurf = trh0.getSurface();
1815  std::optional<double> dist_noise = prop.noise_prop(tre_noise, psurf, Propagator::UNKNOWN, true);
1816  result = dist_noise.has_value();
1817 
1818  // Update momentum-estimating track.
1819 
1820  mf::LogInfo log("KalmanFilterAlg");
1821  if (result) tremom = tre_noise;
1822 
1823  // Done.
1824 
1825  return result;
1826 }
1827 
1839  const Propagator& prop,
1840  KETrack& tremom) const
1841 {
1842  mf::LogInfo log("KalmanFilterAlg");
1843  double invp_range = 0.;
1844  double invp_ms = 0.;
1845 
1846  // Get multiple scattering momentum estimate.
1847 
1848  KETrack tremom_ms;
1849  bool ok_ms = false;
1850  if (fFitMomMS) {
1851  ok_ms = fitMomentumMS(trg, prop, tremom_ms);
1852  if (ok_ms) {
1853  KGTrack trg_ms(trg);
1854  ok_ms = updateMomentum(tremom_ms, prop, trg_ms);
1855  if (ok_ms) {
1856  invp_ms = trg_ms.startTrack().getVector()(4);
1857  double var_invp = trg_ms.startTrack().getError()(4, 4);
1858  double p = 0.;
1859  if (invp_ms != 0.) p = 1. / invp_ms;
1860  double err_p = p * p * std::sqrt(var_invp);
1861  log << "Multiple scattering momentum estimate = " << p << "+-" << err_p << "\n";
1862  }
1863  }
1864  }
1865 
1866  // Get range momentum estimate.
1867 
1868  KETrack tremom_range;
1869  bool ok_range = false;
1870  if (fFitMomRange) {
1871  ok_range = fitMomentumRange(trg, tremom_range);
1872  if (ok_range) {
1873  KGTrack trg_range(trg);
1874  ok_range = updateMomentum(tremom_range, prop, trg_range);
1875  if (ok_range) {
1876  invp_range = trg_range.startTrack().getVector()(4);
1877  double var_invp = trg_range.startTrack().getError()(4, 4);
1878  double p = 0.;
1879  if (invp_range != 0.) p = 1. / invp_range;
1880  double err_p = p * p * std::sqrt(var_invp);
1881  log << "Range momentum estimate = " << p << "+-" << err_p << "\n";
1882  }
1883  }
1884  }
1885 
1886  bool result = false;
1887  if (ok_range) {
1888  tremom = tremom_range;
1889  result = ok_range;
1890  }
1891  else if (ok_ms) {
1892  tremom = tremom_ms;
1893  result = ok_ms;
1894  }
1895  return result;
1896 }
1897 
1920  const Propagator& prop,
1921  KGTrack& trg) const
1922 {
1923  // Get modifiable track map.
1924 
1925  std::multimap<double, KHitTrack>& trackmap = trg.getTrackMap();
1926 
1927  // If track map is empty, immediately return failure.
1928 
1929  if (trackmap.size() == 0) return false;
1930 
1931  // Make trial propagations to the first and last track fit to figure
1932  // out which track fit is closer to the momentum estimating track.
1933 
1934  // Find distance to first track fit.
1935 
1936  KETrack tre0(tremom);
1937  std::optional<double> dist0 =
1938  prop.vec_prop(tre0, trackmap.begin()->second.getSurface(), Propagator::UNKNOWN, false, 0, 0);
1939  // Find distance to last track fit.
1940 
1941  KETrack tre1(tremom);
1942  std::optional<double> dist1 =
1943  prop.vec_prop(tre1, trackmap.rbegin()->second.getSurface(), Propagator::UNKNOWN, false, 0, 0);
1944 
1945  // Based on distances, make starting iterator and direction flag.
1946 
1947  bool forward = true;
1948  std::multimap<double, KHitTrack>::iterator it = trackmap.begin();
1949  if (dist0) {
1950 
1951  // Propagation to first track succeeded.
1952 
1953  if (dist1) {
1954 
1955  // Propagation to both ends succeeded. If the last track is
1956  // closer, initialize iterator and direction flag for reverse
1957  // direction.
1958 
1959  if (std::abs(*dist0) > std::abs(*dist1)) {
1960  it = trackmap.end();
1961  --it;
1962  forward = false;
1963  }
1964  }
1965  }
1966  else {
1967 
1968  // Propagation to first track failed. Initialize iterator and
1969  // direction flag for reverse direction, provided that the
1970  // propagation to the last track succeeded.
1971 
1972  if (dist1) {
1973  it = trackmap.end();
1974  --it;
1975  forward = false;
1976  }
1977  else {
1978 
1979  // Propagation to both ends failed. Return failure.
1980 
1981  return false;
1982  }
1983  }
1984 
1985  // Loop over track fits in global track.
1986 
1987  KETrack tre(tremom);
1988  bool done = false;
1989  while (!done) {
1990  KHitTrack& trh = it->second;
1991  if (trh.getStat() == KFitTrack::INVALID)
1992  throw cet::exception("KalmanFilterAlg")
1993  << "KalmanFilterAlg::updateMomentum(): invalid track\n";
1994 
1995  // Propagate momentum-estimating track to current track surface
1996  // and update momentum.
1997 
1998  std::optional<double> dist = prop.noise_prop(tre, trh.getSurface(), Propagator::UNKNOWN, true);
1999 
2000  // Copy momentum to global track.
2001 
2002  std::multimap<double, KHitTrack>::iterator erase_it = trackmap.end();
2003  if (dist) {
2004  trh.getVector()(4) = tre.getVector()(4);
2005  trh.getError()(4, 0) = 0.;
2006  trh.getError()(4, 1) = 0.;
2007  trh.getError()(4, 2) = 0.;
2008  trh.getError()(4, 3) = 0.;
2009  trh.getError()(4, 4) = tre.getError()(4, 4);
2010  }
2011  else {
2012 
2013  // If the propagation failed, remember that we are supposed to
2014  // erase this track from the global track.
2015 
2016  erase_it = it;
2017  }
2018 
2019  // Advance the iterator and set the done flag.
2020 
2021  if (forward) {
2022  ++it;
2023  done = (it == trackmap.end());
2024  }
2025  else {
2026  done = (it == trackmap.begin());
2027  if (!done) --it;
2028  }
2029 
2030  // Update momentum-estimating track from just-updated global track
2031  // fit, or erase global track fit.
2032 
2033  if (erase_it == trackmap.end())
2034  tre = trh;
2035  else
2036  trackmap.erase(erase_it);
2037  }
2038 
2039  return not empty(trackmap);
2040 }
2041 
2054 bool trkf::KalmanFilterAlg::smoothTrackIter(int nsmooth, KGTrack& trg, const Propagator& prop) const
2055 {
2056  bool ok = true;
2057 
2058  // Call smoothTrack method in a loop.
2059 
2060  for (int ismooth = 0; ok && ismooth < nsmooth - 1; ++ismooth) {
2061  KGTrack trg1{trg.getPrefPlane()};
2062  ok = smoothTrack(trg, &trg1, prop);
2063  if (ok) trg = trg1;
2064  }
2065 
2066  // Do one final smooth without generating a new unidirectional
2067  // track.
2068 
2069  if (ok) ok = smoothTrack(trg, 0, prop);
2070 
2071  // Done.
2072 
2073  return ok;
2074 }
2075 
2083 {
2084  // Get hold of a modifiable track map from trg.
2085 
2086  std::multimap<double, KHitTrack>& trackmap = trg.getTrackMap();
2087 
2088  // Do an indefinite loop until we no longer find any dirt.
2089 
2090  bool done = false;
2091  while (!done) {
2092 
2093  // If track map has fewer than fMaxNoiseHits tracks, then this is a
2094  // noise track. Clear the map, making the whole track invalid.
2095 
2096  int ntrack = trackmap.size();
2097  if (ntrack <= fMaxNoiseHits) {
2098  for (auto const& trackmap_entry : trackmap) {
2099  const KHitTrack& trh = trackmap_entry.second;
2100  const KHitBase& hit = *(trh.getHit());
2101  auto marker_it = fMarkerMap.find(hit.getID());
2102  if (marker_it != fMarkerMap.end()) {
2103  TMarker* marker = marker_it->second;
2104  marker->SetMarkerColor(kGreen);
2105  }
2106  }
2107  trackmap.clear();
2108  done = true;
2109  break;
2110  }
2111 
2112  // Make sure the first two and last two tracks belong to different
2113  // views. If not, remove the first or last track.
2114 
2115  if (ntrack >= 2) {
2116 
2117  // Check start.
2118 
2119  std::multimap<double, KHitTrack>::iterator it = trackmap.begin();
2120  const KHitTrack& trh1a = (*it).second;
2121  const KHitBase& hit1a = *(trh1a.getHit());
2122  int plane1 = hit1a.getMeasPlane();
2123  double chisq1 = hit1a.getChisq();
2124  ++it;
2125  const KHitTrack& trh2a = (*it).second;
2126  const KHitBase& hit2a = *(trh2a.getHit());
2127  int plane2 = hit2a.getMeasPlane();
2128  double chisq2 = hit2a.getChisq();
2129  if ((plane1 >= 0 && plane2 >= 0 && plane1 == plane2) || chisq1 > fMaxEndChisq ||
2130  chisq2 > fMaxEndChisq) {
2131  auto marker_it = fMarkerMap.find(hit1a.getID());
2132  if (marker_it != fMarkerMap.end()) {
2133  TMarker* marker = marker_it->second;
2134  marker->SetMarkerColor(kGreen);
2135  }
2136  trackmap.erase(trackmap.begin(), it);
2137  done = false;
2138  continue;
2139  }
2140 
2141  // Check end.
2142 
2143  it = trackmap.end();
2144  --it;
2145  const KHitTrack& trh1b = (*it).second;
2146  const KHitBase& hit1b = *(trh1b.getHit());
2147  plane1 = hit1b.getMeasPlane();
2148  chisq1 = hit1b.getChisq();
2149  --it;
2150  const KHitTrack& trh2b = (*it).second;
2151  const KHitBase& hit2b = *(trh2b.getHit());
2152  plane2 = hit2b.getMeasPlane();
2153  chisq2 = hit2b.getChisq();
2154  if ((plane1 >= 0 && plane2 >= 0 && plane1 == plane2) || chisq1 > fMaxEndChisq ||
2155  chisq2 > fMaxEndChisq) {
2156  ++it;
2157  auto marker_it = fMarkerMap.find(hit1b.getID());
2158  if (marker_it != fMarkerMap.end()) {
2159  TMarker* marker = marker_it->second;
2160  marker->SetMarkerColor(kGreen);
2161  }
2162  trackmap.erase(it, trackmap.end());
2163  done = false;
2164  continue;
2165  }
2166  }
2167 
2168  // Loop over successive pairs of elements of track map. Look for
2169  // adjacent elements with distance separation greater than
2170  // fGapDist.
2171 
2172  std::multimap<double, KHitTrack>::iterator it = trackmap.begin();
2173  std::multimap<double, KHitTrack>::iterator jt = trackmap.end();
2174  int nb = 0; // Number of elements from begin to jt.
2175  int ne = ntrack; // Number of elements it to end.
2176  bool found_noise = false;
2177  for (; it != trackmap.end(); ++it, ++nb, --ne) {
2178  if (jt == trackmap.end())
2179  jt = trackmap.begin();
2180  else {
2181  if (nb < 1)
2182  throw cet::exception("KalmanFilterAlg")
2183  << "KalmanFilterAlg::cleanTrack(): nb not positive\n";
2184  if (ne < 1)
2185  throw cet::exception("KalmanFilterAlg")
2186  << "KalmanFilterAlg::cleanTrack(): ne not positive\n";
2187 
2188  double disti = (*it).first;
2189  double distj = (*jt).first;
2190  double sep = disti - distj;
2191  if (sep < 0.)
2192  throw cet::exception("KalmanFilterAlg")
2193  << "KalmanFilterAlg::fitMomentumRange(): negative separation\n";
2194 
2195  if (sep > fGapDist) {
2196 
2197  // Found a gap. See if we want to trim track.
2198 
2199  if (nb <= fMaxNoiseHits) {
2200 
2201  // Trim front.
2202 
2203  found_noise = true;
2204  for (auto jt = trackmap.begin(); jt != it; ++jt) {
2205  const KHitTrack& trh = jt->second;
2206  const KHitBase& hit = *(trh.getHit());
2207  auto marker_it = fMarkerMap.find(hit.getID());
2208  if (marker_it != fMarkerMap.end()) {
2209  TMarker* marker = marker_it->second;
2210  marker->SetMarkerColor(kGreen);
2211  }
2212  }
2213  trackmap.erase(trackmap.begin(), it);
2214  break;
2215  }
2216  if (ne <= fMaxNoiseHits) {
2217 
2218  // Trim back.
2219 
2220  found_noise = true;
2221  for (auto jt = it; jt != trackmap.end(); ++jt) {
2222  const KHitTrack& trh = jt->second;
2223  const KHitBase& hit = *(trh.getHit());
2224  auto marker_it = fMarkerMap.find(hit.getID());
2225  if (marker_it != fMarkerMap.end()) {
2226  TMarker* marker = marker_it->second;
2227  marker->SetMarkerColor(kGreen);
2228  }
2229  }
2230  trackmap.erase(it, trackmap.end());
2231  break;
2232  }
2233  }
2234  ++jt;
2235  }
2236  }
2237 
2238  // Decide if we are done.
2239 
2240  done = !found_noise;
2241  }
2242  if (fGTrace && fCanvases.size() > 0) fCanvases.back()->Update();
2243 }
Float_t x
Definition: compare.C:6
bool fGTrace
Graphical trace flag.
intermediate_table::iterator iterator
int getPrefPlane() const
Definition: KGTrack.h:51
const TrackError & getError() const
Track error matrix.
Definition: KETrack.h:52
const std::shared_ptr< const KHitBase > & getHit() const
Measurement.
Definition: KHitTrack.h:51
bool fFitMomRange
Fit momentum using range.
double fMaxPropDist
Maximum propagation distance to candidate surface.
double fGapDist
Minimum gap distance.
bool fFitMomMS
Fit momentum using multiple scattering.
double Mass() const
Based on pdg code.
Definition: KTrack.cxx:116
const std::shared_ptr< const Surface > & getSurface() const
Surface.
Definition: KTrack.h:53
double PointingError() const
Pointing error (radians).
Definition: KETrack.cxx:66
double getPredDistance() const
Prediction distance.
Definition: KHitBase.h:79
void recalibrate()
Recalibrate track map.
Definition: KGTrack.cxx:88
std::map< int, TMarker * > fMarkerMap
Markers in current canvas.
int getMeasPlane() const
Measurement plane index.
Definition: KHitBase.h:85
const std::list< KHitGroup > & getUnused() const
Definition: KHitContainer.h:71
bool smoothTrackIter(int niter, KGTrack &trg, const Propagator &prop) const
Iteratively smooth a track.
const KHitTrack & endTrack() const
Track at end point.
Definition: KGTrack.cxx:40
KSymMatrix< 5 >::type TrackError
Track error matrix, dimension 5x5.
const std::vector< std::shared_ptr< const KHitBase > > & getHits() const
Measurement collection accessor.
Definition: KHitGroup.h:54
double fMaxEndChisq
Maximum incremental chisquare for endpoint hit.
bool fTrace
Trace flag.
MaybeLogger_< ELseverityLevel::ELsev_info, false > LogInfo
Planar surface parallel to x-axis.
KalmanFilterAlg(const fhicl::ParameterSet &pset)
Constructor.
Double_t z
Definition: plot.C:276
A collection of KHitGroups.
constexpr auto abs(T v)
Returns the absolute value of the argument.
void cleanTrack(KGTrack &trg) const
Clean track by removing noise hits near endpoints.
void addTrack(const KHitTrack &trh)
Add track.
Definition: KGTrack.cxx:76
double getPath() const
Estimated path distance.
Definition: KHitGroup.h:60
intermediate_table::const_iterator const_iterator
FitStatus
Fit status enum.
Definition: KFitTrack.h:39
int getID() const
Unique id.
Definition: KHitBase.h:88
std::vector< double > fGTraceZMin
Graphical trace minimum z for each view.
const std::multimap< double, KHitTrack > & getTrackMap() const
KHitTrack collection, indexed by path distance.
Definition: KGTrack.h:54
std::optional< double > vec_prop(KTrack &trk, const std::shared_ptr< const Surface > &psurf, PropDirection dir, bool doDedx, TrackMatrix *prop_matrix=0, TrackError *noise_matrix=0) const
Propagate without error (long distance).
Definition: Propagator.cxx:52
double fMaxLDist
Maximum distance for linearized propagation.
double fMaxSortDist
Sort high distance threshold.
double fMaxSeedPredDist
Maximum prediciton distance to accept a hit in seed phase.
ublas::symmetric_matrix< double, ublas::lower, ublas::row_major, ublas::bounded_array< double, N *(N+1)/2 > > type
double fMaxPredDist
Maximum prediciton distance to accept a hit.
M::value_type trace(const M &m)
std::optional< double > noise_prop(KETrack &tre, const std::shared_ptr< const Surface > &psurf, PropDirection dir, bool doDedx, KTrack *ref=0) const
Propagate with error and noise.
Definition: Propagator.cxx:382
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:289
bool fitMomentumMS(const KGTrack &trg, const Propagator &prop, KETrack &tremom) const
Estimate track momentum using multiple scattering.
void hits()
Definition: readHits.C:15
void setChisq(double chisq)
Set chisquare.
Definition: KFitTrack.h:68
bool buildTrack(const KTrack &trk, KGTrack &trg, const Propagator &prop, const Propagator::PropDirection dir, KHitContainer &hits, bool linear) const
Make a new track.
TCanvas * c2
Definition: plot_hist.C:75
double getPath() const
Propagation distance.
Definition: KFitTrack.h:61
Kalman filter measurement class template.
ublas::vector< double, ublas::bounded_array< double, N > > type
bool syminvert(ublas::symmetric_matrix< T, TRI, L, A > &m)
double fGTraceWH
Window height.
TVirtualPad * fInfoPad
Information pad.
T get(std::string const &key) const
Definition: ParameterSet.h:314
bool extendTrack(KGTrack &trg, const Propagator &prop, KHitContainer &hits) const
Add hits to existing track.
std::optional< double > err_prop(KETrack &tre, const std::shared_ptr< const Surface > &psurf, PropDirection dir, bool doDedx, KTrack *ref=0, TrackMatrix *prop_matrix=0) const
Propagate with error, but without noise.
Definition: Propagator.cxx:343
std::vector< std::unique_ptr< TCanvas > > fCanvases
Graphical trace canvases.
const std::list< KHitGroup > & getUnsorted() const
Definition: KHitContainer.h:70
double fGTraceXMin
Graphical trace minimum x.
double fGoodPErr
Pointing error threshold for switching to free propagation.
TPaveText * fMessages
Message box.
void setStat(FitStatus stat)
Set fit status.
Definition: KFitTrack.h:69
double fMaxSeedIncChisq
Maximum incremental chisquare to accept a hit in seed phase.
double fMaxSmoothIncChisq
Maximum incremental chisquare to accept a hit in smooth phase.
double fMaxIncChisq
Maximum incremental chisquare to accept a hit.
bool smoothTrack(KGTrack &trg, KGTrack *trg1, const Propagator &prop) const
Smooth track.
double fMinSampleDist
Minimum sample distance (for momentum measurement).
Kalman Filter.
Detector simulation of raw signals on wires.
double fMinSortDist
Sort low distance threshold.
const TrackVector & getVector() const
Track state vector.
Definition: KTrack.h:54
virtual void update(KETrack &tre) const =0
Update track method.
virtual double getChisq() const =0
Return incremental chisquare.
double dist(const TReal *x, const TReal *y, const unsigned int dimension)
TDirectory * dir
Definition: macro.C:5
double fGTraceXMax
Graphical trace maximum x.
void sort(const KTrack &trk, bool addUnsorted, const Propagator &prop, Propagator::PropDirection dir=Propagator::UNKNOWN)
(Re)sort objects in unsorted and sorted lists.
std::vector< TVirtualPad * > fPads
View pads in current canvas.
TLegend * leg
Definition: compare.C:67
size_t numHits() const
Number of measurements in track.
Definition: KGTrack.h:57
double fGTraceWW
Window width.
ublas::matrix< double, ublas::row_major, ublas::bounded_array< double, N *M > > type
virtual bool predict(const KETrack &tre, const Propagator &prop, const KTrack *ref=0) const =0
Prediction method (return false if fail).
const std::shared_ptr< const Surface > & getSurface() const
Surface accessor.
Definition: KHitGroup.h:48
bool combineFit(const KFitTrack &trf)
Combine two tracks.
Definition: KFitTrack.cxx:65
void setPath(double path)
Set propagation distance.
Definition: KFitTrack.h:67
std::vector< double > fGTraceZMax
Graphical trace maximum z for each view.
int fMaxSamePlane
Maximum consecutive hits in same plane.
Line surface perpendicular to x-axis.
int fMaxNoiseHits
Maximum number of hits in noise cluster.
A collection of KHitTracks.
Basic Kalman filter track class, with fit information.
double fMaxPErr
Maximum pointing error for free propagation.
TPCGeo const & TPC(TPCID const &tpcid=details::tpc_zero) const
Returns the specified TPC.
Definition: GeometryCore.h:448
bool fitMomentumRange(const KGTrack &trg, KETrack &tremom) const
Estimate track momentum using range.
bool isValid() const
Validity flag.
Definition: KGTrack.h:66
bool fitMomentum(const KGTrack &trg, const Propagator &prop, KETrack &tremom) const
Estimate track momentum using either range or multiple scattering.
bool updateMomentum(const KETrack &tremom, const Propagator &prop, KGTrack &trg) const
Set track momentum at each track surface.
double getChisq() const
Fit chisquare.
Definition: KFitTrack.h:62
const KHitTrack & startTrack() const
Track at start point.
Definition: KGTrack.cxx:30
art framework interface to geometry description
double HalfWidth() const
Width is associated with x coordinate [cm].
Definition: TPCGeo.h:102
const std::shared_ptr< const Surface > & getMeasSurface() const
Measurement surface.
Definition: KHitBase.h:82
int fMinLHits
Minimum number of hits to turn off linearized propagation.
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
decltype(auto) constexpr empty(T &&obj)
ADL-aware version of std::empty.
Definition: StdUtils.h:109
bool isValid() const
Test if track is valid.
Definition: KTrack.cxx:81
PropDirection
Propagation direction enum.
Definition: Propagator.h:94
FitStatus getStat() const
Fit status.
Definition: KFitTrack.h:63
int getPlane() const
Plane index.
Definition: KHitGroup.h:51
int fPlane
Preferred view plane.
const std::list< KHitGroup > & getSorted() const
Definition: KHitContainer.h:69