PmaTrack3D.cxx
Go to the documentation of this file.
1 /**
2  * @file PmaTrack3D.cxx
3  *
4  * @author D.Stefan and R.Sulej
5  *
6  * @brief Implementation of the Projection Matching Algorithm
7  *
8  * Build 3D segments and whole tracks by simultaneous matching hits in 2D projections.
9  * See PmaTrack3D.h file for details.
10  */
11 
15 
19 
22 
23 #include <array>
24 
25 #include "range/v3/algorithm.hpp"
26 #include "range/v3/view.hpp"
27 
28 pma::Track3D::Track3D() = default;
29 
31  : fMaxHitsPerSeg(src.fMaxHitsPerSeg)
32  , fPenaltyFactor(src.fPenaltyFactor)
33  , fMaxSegStopFactor(src.fMaxSegStopFactor)
34  , fSegStopValue(src.fSegStopValue)
35  , fMinSegStop(src.fMinSegStop)
36  , fMaxSegStop(src.fMaxSegStop)
37  , fSegStopFactor(src.fSegStopFactor)
38  , fPenaltyValue(src.fPenaltyValue)
39  , fEndSegWeight(src.fEndSegWeight)
40  , fHitsRadius(src.fHitsRadius)
41  , fT0(src.fT0)
42  , fT0Flag(src.fT0Flag)
43  , fTag(src.fTag)
44 {
45  fHits.reserve(src.fHits.size());
46  for (auto const* hit : src.fHits) {
47  pma::Hit3D* h3d = new pma::Hit3D(*hit);
48  h3d->fParent = this;
49  fHits.push_back(h3d);
50  }
51 
52  fNodes.reserve(src.fNodes.size());
53  for (auto const* node : src.fNodes)
54  fNodes.push_back(new pma::Node3D(*node));
55 
56  for (auto const* point : src.fAssignedPoints)
57  fAssignedPoints.push_back(new TVector3(*point));
58 
61 }
62 
64 {
65  for (size_t i = 0; i < fHits.size(); i++)
66  delete fHits[i];
67  for (size_t i = 0; i < fAssignedPoints.size(); i++)
68  delete fAssignedPoints[i];
69 
70  for (size_t i = 0; i < fSegments.size(); i++)
71  delete fSegments[i];
72  for (size_t i = 0; i < fNodes.size(); i++)
73  if (!fNodes[i]->NextCount() && !fNodes[i]->Prev()) delete fNodes[i];
74 }
75 
76 bool
78 {
79  if (!HasTwoViews(2)) {
80  mf::LogError("pma::Track3D") << "Need min. 2 hits per view, at least two views.";
81  return false;
82  }
83 
84  auto cryos = Cryos();
85  if (cryos.size() > 1) {
86  mf::LogError("pma::Track3D") << "Only one cryostat for now, please.";
87  return false;
88  }
89  int cryo = cryos.front();
90 
91  auto tpcs = TPCs();
92  if (tpcs.size() > 1) {
93  mf::LogError("pma::Track3D") << "Only one TPC, please.";
94  return false;
95  }
96  // single tpc, many tpc's are ok, but need to be handled from ProjectionMatchingAlg::buildMultiTPCTrack()
97  int tpc = tpcs.front();
98 
99  if (InitFromRefPoints(detProp, tpc, cryo))
100  mf::LogVerbatim("pma::Track3D") << "Track initialized with 3D reference points.";
101  else if (InitFromHits(detProp, tpc, cryo, initEndSegW))
102  mf::LogVerbatim("pma::Track3D") << "Track initialized with hit positions.";
103  else {
104  InitFromMiddle(detProp, tpc, cryo);
105  mf::LogVerbatim("pma::Track3D") << "Track initialized in the module center.";
106  }
107 
109  return true;
110 }
111 
112 void
114 {
115  for (size_t i = 0; i < fNodes.size(); i++)
116  delete fNodes[i];
117  fNodes.clear();
118 }
119 
120 bool
122  int tpc,
123  int cryo,
124  float initEndSegW)
125 {
127 
128  float wtmp = fEndSegWeight;
129  fEndSegWeight = initEndSegW;
130 
131  // endpoints for the first combination:
132  TVector3 v3d_1(0., 0., 0.), v3d_2(0., 0., 0.);
133 
134  assert(!fHits.empty());
135 
136  pma::Hit3D* hit0_a = fHits.front();
137  pma::Hit3D* hit1_a = fHits.front();
138 
139  float minX = detProp.ConvertTicksToX(hit0_a->PeakTime(), hit0_a->View2D(), tpc, cryo);
140  float maxX = detProp.ConvertTicksToX(hit1_a->PeakTime(), hit1_a->View2D(), tpc, cryo);
141  for (auto hit : fHits) {
142  double const x = detProp.ConvertTicksToX(hit->PeakTime(), hit->View2D(), tpc, cryo);
143  if (x < minX) {
144  minX = x;
145  hit0_a = hit;
146  }
147  if (x > maxX) {
148  maxX = x;
149  hit1_a = hit;
150  }
151  }
152 
153  pma::Hit3D* hit0_b = nullptr;
154  pma::Hit3D* hit1_b = nullptr;
155 
156  float minDiff0 = 5000, minDiff1 = 5000;
157  for (auto hit : fHits) {
158  double const x = detProp.ConvertTicksToX(hit->PeakTime(), hit->View2D(), tpc, cryo);
159  double diff = fabs(x - minX);
160  if ((diff < minDiff0) && (hit->View2D() != hit0_a->View2D())) {
161  minDiff0 = diff;
162  hit0_b = hit;
163  }
164  diff = fabs(x - maxX);
165  if ((diff < minDiff1) && (hit->View2D() != hit1_a->View2D())) {
166  minDiff1 = diff;
167  hit1_b = hit;
168  }
169  }
170 
171  if (hit0_a && hit0_b && hit1_a && hit1_b) {
172  double x = 0.5 * (detProp.ConvertTicksToX(hit0_a->PeakTime(), hit0_a->View2D(), tpc, cryo) +
173  detProp.ConvertTicksToX(hit0_b->PeakTime(), hit0_b->View2D(), tpc, cryo));
174  double y, z;
175  geom->IntersectionPoint(
176  hit0_a->Wire(), hit0_b->Wire(), hit0_a->View2D(), hit0_b->View2D(), cryo, tpc, y, z);
177  v3d_1.SetXYZ(x, y, z);
178 
179  x = 0.5 * (detProp.ConvertTicksToX(hit1_a->PeakTime(), hit1_a->View2D(), tpc, cryo) +
180  detProp.ConvertTicksToX(hit1_b->PeakTime(), hit1_b->View2D(), tpc, cryo));
181  geom->IntersectionPoint(
182  hit1_a->Wire(), hit1_b->Wire(), hit1_a->View2D(), hit1_b->View2D(), cryo, tpc, y, z);
183  v3d_2.SetXYZ(x, y, z);
184 
185  ClearNodes();
186  AddNode(detProp, v3d_1, tpc, cryo);
187  AddNode(detProp, v3d_2, tpc, cryo);
188 
189  MakeProjection();
191  Optimize(detProp, 0, 0.01F, false, true, 100);
192  SelectAllHits();
193  }
194  else {
195  mf::LogVerbatim("pma::Track3D") << "Good hits not found.";
196  fEndSegWeight = wtmp;
197  return false;
198  }
199 
200  if (pma::Dist2(fNodes.front()->Point3D(), fNodes.back()->Point3D()) < (0.3 * 0.3)) {
201  mf::LogVerbatim("pma::Track3D") << "Short initial segment.";
202  fEndSegWeight = wtmp;
203  return false;
204  }
205 
206  fEndSegWeight = wtmp;
207  return true;
208 }
209 
210 bool
212 {
213  if (fAssignedPoints.size() < 2) return false;
214 
215  ClearNodes();
216 
217  TVector3 mean(0., 0., 0.), stdev(0., 0., 0.), p(0., 0., 0.);
218  for (size_t i = 0; i < fAssignedPoints.size(); i++) {
219  p = *(fAssignedPoints[i]);
220  mean += p;
221  p.SetXYZ(p.X() * p.X(), p.Y() * p.Y(), p.Z() * p.Z());
222  stdev += p;
223  }
224  stdev *= 1.0 / fAssignedPoints.size();
225  mean *= 1.0 / fAssignedPoints.size();
226  p = mean;
227  p.SetXYZ(p.X() * p.X(), p.Y() * p.Y(), p.Z() * p.Z());
228  stdev -= p;
229 
230  double sx = stdev.X(), sy = stdev.Y(), sz = stdev.Z();
231  if (sx >= 0.0)
232  sx = sqrt(sx);
233  else
234  sx = 0.0;
235  if (sy >= 0.0)
236  sy = sqrt(sy);
237  else
238  sy = 0.0;
239  if (sz >= 0.0)
240  sz = sqrt(sz);
241  else
242  sz = 0.0;
243  stdev.SetXYZ(sx, sy, sz);
244 
245  double scale = 2.0 * stdev.Mag();
246  double iscale = 1.0 / scale;
247 
248  size_t max_index = 0;
249  double norm2, max_norm2 = 0.0;
250  std::vector<TVector3> data;
251  for (size_t i = 0; i < fAssignedPoints.size(); i++) {
252  p = *(fAssignedPoints[i]);
253  p -= mean;
254  p *= iscale;
255  norm2 = p.Mag2();
256  if (norm2 > max_norm2) {
257  max_norm2 = norm2;
258  max_index = i;
259  }
260  data.push_back(p);
261  }
262 
263  double y = 0.0, kappa = 1.0, prev_kappa, kchg = 1.0;
264  TVector3 w(data[max_index]);
265 
266  while (kchg > 0.0001)
267  for (size_t i = 0; i < data.size(); i++) {
268  y = (data[i] * w);
269  w += (y / kappa) * (data[i] - y * w);
270 
271  prev_kappa = kappa;
272  kappa += y * y;
273  kchg = fabs((kappa - prev_kappa) / prev_kappa);
274  }
275  w *= 1.0 / w.Mag();
276 
277  TVector3 v1(w), v2(w);
278  v1 *= scale;
279  v1 += mean;
280  v2 *= -scale;
281  v2 += mean;
282  std::sort(fAssignedPoints.begin(), fAssignedPoints.end(), pma::bSegmentProjLess(v1, v2));
283  for (size_t i = 0; i < fAssignedPoints.size(); i++) {
284  AddNode(detProp, *(fAssignedPoints[i]), tpc, cryo);
285  }
286 
287  RebuildSegments();
288  MakeProjection();
289 
290  if (size()) UpdateHitsRadius();
291 
292  Optimize(detProp, 0, 0.01F, false, true, 100);
293  SelectAllHits();
294 
295  return true;
296 }
297 
298 void
300 {
302 
303  const auto& tpcGeo = geom->TPC(tpc, cryo);
304 
305  double minX = tpcGeo.MinX(), maxX = tpcGeo.MaxX();
306  double minY = tpcGeo.MinY(), maxY = tpcGeo.MaxY();
307  double minZ = tpcGeo.MinZ(), maxZ = tpcGeo.MaxZ();
308 
309  TVector3 v3d_1(0.5 * (minX + maxX), 0.5 * (minY + maxY), 0.5 * (minZ + maxZ));
310  TVector3 v3d_2(v3d_1);
311 
312  TVector3 shift(5.0, 5.0, 5.0);
313  v3d_1 += shift;
314  v3d_2 -= shift;
315 
316  ClearNodes();
317  AddNode(detProp, v3d_1, tpc, cryo);
318  AddNode(detProp, v3d_2, tpc, cryo);
319 
320  MakeProjection();
322 
323  Optimize(detProp, 0, 0.01F);
324 }
325 
326 int
328 {
329  for (size_t i = 0; i < fNodes.size(); ++i)
330  if (fNodes[i] == n) return (int)i;
331  return -1;
332 }
333 
334 int
336 {
337  for (size_t i = 0; i < size(); i++)
338  if (fHits[i] == hit) return (int)i;
339  return -1;
340 }
341 
342 pma::Hit3D*
344 {
345  pma::Hit3D* h3d = fHits[index];
346  fHits.erase(fHits.begin() + index);
347  return h3d;
348 }
349 
350 bool
352  const art::Ptr<recob::Hit>& hit)
353 {
354  for (auto const& trk_hit : fHits) {
355  if (trk_hit->fHit == hit) return false;
356  }
357  pma::Hit3D* h3d = new pma::Hit3D(detProp, hit);
358  h3d->fParent = this;
359  fHits.push_back(h3d);
360  return true;
361 }
362 
363 bool
365 {
366  for (size_t i = 0; i < size(); i++) {
367  if (hit == fHits[i]->fHit) {
368  pma::Hit3D* h3d = fHits[i];
369  fHits.erase(fHits.begin() + i);
370  delete h3d;
371  return true;
372  }
373  }
374  return false;
375 }
376 
379 {
380  pma::Hit3D* h = fHits[index];
381 
382  for (auto s : fSegments) {
383  if (s->HasHit(h)) return s->GetDirection3D();
384  }
385  for (auto n : fNodes) {
386  if (n->HasHit(h)) return n->GetDirection3D();
387  }
388 
389  auto pe = GetNearestElement(h->Point2D(), h->View2D(), h->TPC());
390  if (pe) {
391  mf::LogWarning("pma::Track3D")
392  << "GetDirection3D(): had to update hit assignment to segment/node.";
393  pe->AddHit(h);
394  return pe->GetDirection3D();
395  }
396  else {
397  throw cet::exception("pma::Track3D") << "GetDirection3D(): direction of a not assigned hit "
398  << index << " (size: " << fHits.size() << ")" << std::endl;
399  }
400 }
401 
402 void
404  const std::vector<art::Ptr<recob::Hit>>& hits)
405 {
406  fHits.reserve(fHits.size() + hits.size());
407  for (auto const& hit : hits)
408  push_back(detProp, hit);
409 }
410 
411 void
413 {
414  unsigned int n = 0;
415  for (auto const& hit : hits) {
416  if (erase(hit)) n++;
417  }
418  if (n) MakeProjection();
419 }
420 
421 unsigned int
422 pma::Track3D::NHits(unsigned int view) const
423 {
424  unsigned int n = 0;
425  for (auto hit : fHits) {
426  if (hit->View2D() == view) n++;
427  }
428  return n;
429 }
430 
431 unsigned int
432 pma::Track3D::NEnabledHits(unsigned int view) const
433 {
434  unsigned int n = 0;
435  for (auto hit : fHits) {
436  if (hit->IsEnabled() && ((view == geo::kUnknown) || (view == hit->View2D()))) n++;
437  }
438  return n;
439 }
440 
441 bool
442 pma::Track3D::HasTwoViews(size_t const nmin) const
443 {
444  unsigned int nviews = 0;
445  if (NHits(geo::kU) >= nmin) nviews++;
446  if (NHits(geo::kV) >= nmin) nviews++;
447  if (NHits(geo::kZ) >= nmin) nviews++;
448  return nviews > 1;
449 }
450 
451 std::vector<unsigned int>
453 {
454  std::vector<unsigned int> tpc_idxs;
455  for (auto hit : fHits) {
456  unsigned int tpc = hit->TPC();
457 
458  bool found = false;
459  for (unsigned int const tpc_idx : tpc_idxs)
460  if (tpc_idx == tpc) {
461  found = true;
462  break;
463  }
464 
465  if (!found) tpc_idxs.push_back(tpc);
466  }
467  return tpc_idxs;
468 }
469 
470 std::vector<unsigned int>
472 {
473  std::vector<unsigned int> cryo_idxs;
474  for (auto hit : fHits) {
475  unsigned int cryo = hit->Cryo();
476 
477  bool found = false;
478  for (size_t j = 0; j < cryo_idxs.size(); j++)
479  if (cryo_idxs[j] == cryo) {
480  found = true;
481  break;
482  }
483 
484  if (!found) cryo_idxs.push_back(cryo);
485  }
486  return cryo_idxs;
487 }
488 
489 std::pair<TVector2, TVector2>
491  unsigned int view,
492  unsigned int tpc,
493  unsigned int cryo) const
494 {
495  std::pair<TVector2, TVector2> range(TVector2(0., 0.), TVector2(0., 0.));
496 
497  size_t n0 = 0;
498  while ((n0 < fNodes.size()) && (fNodes[n0]->TPC() != (int)tpc))
499  n0++;
500 
501  if (n0 < fNodes.size()) {
502  size_t n1 = n0;
503  while ((n1 < fNodes.size()) && (fNodes[n1]->TPC() == (int)tpc))
504  n1++;
505 
506  if (n0 > 0) n0--;
507  if (n1 == fNodes.size()) n1--;
508 
509  TVector2 p0 = fNodes[n0]->Projection2D(view);
510  p0 = pma::CmToWireDrift(detProp, p0.X(), p0.Y(), view, tpc, cryo);
511 
512  TVector2 p1 = fNodes[n1]->Projection2D(view);
513  p1 = pma::CmToWireDrift(detProp, p1.X(), p1.Y(), view, tpc, cryo);
514 
515  if (p0.X() > p1.X()) {
516  double tmp = p0.X();
517  p0.Set(p1.X(), p0.Y());
518  p1.Set(tmp, p1.Y());
519  }
520  if (p0.Y() > p1.Y()) {
521  double tmp = p0.Y();
522  p0.Set(p0.X(), p1.Y());
523  p1.Set(p1.X(), tmp);
524  }
525 
526  range.first = p0;
527  range.second = p1;
528  }
529  return range;
530 }
531 
532 bool
534  std::vector<pma::Track3D*>& allTracks)
535 {
536  if (fNodes.size() < 2) { return true; }
537 
538  std::vector<pma::Track3D*> toSort;
539 
540  pma::Node3D* n = fNodes.front();
541  if (n->Prev()) {
542  pma::Segment3D* s = static_cast<pma::Segment3D*>(n->Prev());
543  pma::Track3D* t = s->Parent();
544 
545  if (t->NextSegment(n)) // starts from middle of another track: need to break that one first
546  {
547  int idx = t->index_of(n);
548  if (idx >= 0) {
549  pma::Track3D* u = t->Split(detProp, idx, false); // u is in front of t
550  if (u) {
551  allTracks.push_back(u);
552  if (u->Flip(detProp, allTracks)) {
553  InternalFlip(toSort);
554  toSort.push_back(this);
555  }
556  else {
557  mf::LogWarning("pma::Track3D")
558  << "Flip(): Could not flip after split (but new track is preserved!!).";
559  return false;
560  }
561  }
562  else {
563  return false;
564  } // could not flip/break associated tracks, so give up on this one
565  }
566  else {
567  throw cet::exception("pma::Track3D") << "Node not found." << std::endl;
568  }
569  }
570  else // flip root
571  {
572  if (t->Flip(detProp, allTracks)) // all ok, so can flip this track
573  {
574  InternalFlip(toSort);
575  toSort.push_back(this);
576  }
577  else {
578  return false;
579  } // could not flip/break associated tracks, so give up on this one
580  }
581  }
582  else // simply flip
583  {
584  InternalFlip(toSort);
585  toSort.push_back(this);
586  }
587 
588  for (size_t t = 0; t < toSort.size(); t++) {
589  bool sorted = false;
590  for (size_t u = 0; u < t; u++)
591  if (toSort[u] == toSort[t]) {
592  sorted = true;
593  break;
594  }
595  if (!sorted) {
596  toSort[t]->MakeProjection();
597  toSort[t]->SortHits();
598  }
599  }
600  return true;
601 }
602 
603 void
604 pma::Track3D::InternalFlip(std::vector<pma::Track3D*>& toSort)
605 {
606  for (size_t i = 0; i < fNodes.size() - 1; i++)
607  if (fNodes[i]->NextCount() > 1) {
608  for (size_t j = 0; j < fNodes[i]->NextCount(); j++) {
609  pma::Segment3D* s = static_cast<pma::Segment3D*>(fNodes[i]->Next(j));
610  if (s->Parent() != this) toSort.push_back(s->Parent());
611  }
612  }
613 
614  if (fNodes.back()->NextCount())
615  for (size_t j = 0; j < fNodes.back()->NextCount(); j++) {
616  pma::Segment3D* s = static_cast<pma::Segment3D*>(fNodes.back()->Next(j));
617  toSort.push_back(s->Parent());
618  }
619 
620  if (fNodes.front()->Prev()) {
621  pma::Segment3D* s = static_cast<pma::Segment3D*>(fNodes.front()->Prev());
622  toSort.push_back(s->Parent());
623  s->Parent()->InternalFlip(toSort);
624  }
625 
626  std::reverse(fNodes.begin(), fNodes.end());
627  toSort.push_back(this);
628  RebuildSegments();
629 }
630 
631 void
633 {
634  std::vector<pma::Track3D*> toSort;
635  InternalFlip(toSort);
636  toSort.push_back(this);
637 
638  for (size_t t = 0; t < toSort.size(); t++) {
639  bool sorted = false;
640  for (size_t u = 0; u < t; u++)
641  if (toSort[u] == toSort[t]) {
642  sorted = true;
643  break;
644  }
645  if (!sorted) {
646  toSort[t]->MakeProjection();
647  toSort[t]->SortHits();
648  }
649  }
650 }
651 
652 bool
654 {
655  if (!fNodes.size()) { return false; }
656 
657  pma::Node3D* n = fNodes.front();
658  if (n->Prev()) {
659  pma::Segment3D* s = static_cast<pma::Segment3D*>(n->Prev());
660  pma::Track3D* t = s->Parent();
661  if (t->NextSegment(n)) { return false; } // cannot flip if starts from middle of another track
662  else {
663  return t->CanFlip();
664  } // check root
665  }
666  else {
667  return true;
668  }
669 }
670 
671 void
673 {
674  unsigned int nViews = 3;
675  pma::dedx_map dedxInViews[3];
676  for (unsigned int i = 0; i < nViews; i++) {
677  GetRawdEdxSequence(dedxInViews[i], i, 1);
678  }
679  unsigned int bestView = 2;
680  if (dedxInViews[0].size() > 2 * dedxInViews[2].size()) bestView = 0;
681  if (dedxInViews[1].size() > 2 * dedxInViews[2].size()) bestView = 1;
682 
683  std::vector<std::vector<double>> dedx;
684  for (size_t i = 0; i < size(); i++) {
685  auto it = dedxInViews[bestView].find(i);
686  if (it != dedxInViews[bestView].end()) { dedx.push_back(it->second); }
687  }
688  if (!dedx.empty()) dedx.pop_back();
689 
690  float dEdxStart = 0.0F, dEdxStop = 0.0F;
691  float dEStart = 0.0F, dxStart = 0.0F;
692  float dEStop = 0.0F, dxStop = 0.0F;
693  if (dedx.size() > 4) {
694  if (!n) // use default options
695  {
696  if (dedx.size() > 30)
697  n = 12;
698  else if (dedx.size() > 20)
699  n = 8;
700  else if (dedx.size() > 10)
701  n = 4;
702  else
703  n = 3;
704  }
705 
706  size_t k = (dedx.size() - 2) >> 1;
707  if (n > k) n = k;
708 
709  for (size_t i = 1, j = 0; j < n; i++, j++) {
710  dEStart += dedx[i][5];
711  dxStart += dedx[i][6];
712  }
713  if (dxStart > 0.0F) dEdxStart = dEStart / dxStart;
714 
715  for (size_t i = dedx.size() - 2, j = 0; j < n; i--, j++) {
716  dEStop += dedx[i][5];
717  dxStop += dedx[i][6];
718  }
719  if (dxStop > 0.0F) dEdxStop = dEStop / dxStop;
720  }
721  else if (dedx.size() == 4) {
722  dEStart = dedx[0][5] + dedx[1][5];
723  dxStart = dedx[0][6] + dedx[1][6];
724  dEStop = dedx[2][5] + dedx[3][5];
725  dxStop = dedx[2][6] + dedx[3][6];
726  if (dxStart > 0.0F) dEdxStart = dEStart / dxStart;
727  if (dxStop > 0.0F) dEdxStop = dEStop / dxStop;
728  }
729  else if (dedx.size() > 1) {
730  if (dedx.front()[2] > 0.0F) dEdxStart = dedx.front()[5] / dedx.front()[6];
731  if (dedx.back()[2] > 0.0F) dEdxStop = dedx.back()[5] / dedx.back()[6];
732  }
733  else
734  return;
735 
736  if ((dir == pma::Track3D::kForward) && ((1.0 + thr) * dEdxStop < dEdxStart)) {
737  mf::LogVerbatim("pma::Track3D")
738  << "Auto-flip fired (1), thr: " << (1.0 + thr) << ", value: " << dEdxStart / dEdxStop;
739  Flip(); // particle stops at the end of the track
740  }
741  if ((dir == pma::Track3D::kBackward) && (dEdxStop > (1.0 + thr) * dEdxStart)) {
742  mf::LogVerbatim("pma::Track3D")
743  << "Auto-flip fired (2), thr: " << (1.0 + thr) << ", value: " << dEdxStop / dEdxStart;
744  Flip(); // particle stops at the front of the track
745  }
746 }
747 
748 bool
750  std::vector<pma::Track3D*>& allTracks,
752  double thr,
753  unsigned int n)
754 {
755  unsigned int nViews = 3;
756  pma::dedx_map dedxInViews[3];
757  for (unsigned int i = 0; i < nViews; i++) {
758  GetRawdEdxSequence(dedxInViews[i], i, 1);
759  }
760  unsigned int bestView = 2;
761  if (dedxInViews[0].size() > 2 * dedxInViews[2].size()) bestView = 0;
762  if (dedxInViews[1].size() > 2 * dedxInViews[2].size()) bestView = 1;
763 
764  std::vector<std::vector<double>> dedx;
765  for (size_t i = 0; i < size(); i++) {
766  auto it = dedxInViews[bestView].find(i);
767  if (it != dedxInViews[bestView].end()) { dedx.push_back(it->second); }
768  }
769  if (!dedx.empty()) dedx.pop_back();
770 
771  float dEdxStart = 0.0F, dEdxStop = 0.0F;
772  float dEStart = 0.0F, dxStart = 0.0F;
773  float dEStop = 0.0F, dxStop = 0.0F;
774  if (dedx.size() > 4) {
775  if (!n) // use default options
776  {
777  if (dedx.size() > 30)
778  n = 12;
779  else if (dedx.size() > 20)
780  n = 8;
781  else if (dedx.size() > 10)
782  n = 4;
783  else
784  n = 3;
785  }
786 
787  size_t k = (dedx.size() - 2) >> 1;
788  if (n > k) n = k;
789 
790  for (size_t i = 1, j = 0; j < n; i++, j++) {
791  dEStart += dedx[i][5];
792  dxStart += dedx[i][6];
793  }
794  if (dxStart > 0.0F) dEdxStart = dEStart / dxStart;
795 
796  for (size_t i = dedx.size() - 2, j = 0; j < n; i--, j++) {
797  dEStop += dedx[i][5];
798  dxStop += dedx[i][6];
799  }
800  if (dxStop > 0.0F) dEdxStop = dEStop / dxStop;
801  }
802  else if (dedx.size() == 4) {
803  dEStart = dedx[0][5] + dedx[1][5];
804  dxStart = dedx[0][6] + dedx[1][6];
805  dEStop = dedx[2][5] + dedx[3][5];
806  dxStop = dedx[2][6] + dedx[3][6];
807  if (dxStart > 0.0F) dEdxStart = dEStart / dxStart;
808  if (dxStop > 0.0F) dEdxStop = dEStop / dxStop;
809  }
810  else if (dedx.size() > 1) {
811  if (dedx.front()[2] > 0.0F) dEdxStart = dedx.front()[5] / dedx.front()[6];
812  if (dedx.back()[2] > 0.0F) dEdxStop = dedx.back()[5] / dedx.back()[6];
813  }
814  else
815  return false;
816 
817  bool done = false;
818  if ((dir == pma::Track3D::kForward) && ((1.0 + thr) * dEdxStop < dEdxStart)) {
819  mf::LogVerbatim("pma::Track3D")
820  << "Auto-flip fired (1), thr: " << (1.0 + thr) << ", value: " << dEdxStart / dEdxStop;
821  done = Flip(detProp, allTracks); // particle stops at the end of the track
822  }
823  if ((dir == pma::Track3D::kBackward) && (dEdxStop > (1.0 + thr) * dEdxStart)) {
824  mf::LogVerbatim("pma::Track3D")
825  << "Auto-flip fired (2), thr: " << (1.0 + thr) << ", value: " << dEdxStop / dEdxStart;
826  done = Flip(detProp, allTracks); // particle stops at the front of the track
827  }
828  return done;
829 }
830 
831 double
833  const std::vector<art::Ptr<recob::Hit>>& hits,
834  bool normalized) const
835 {
836  if (hits.empty()) {
837  mf::LogWarning("pma::Track3D") << "TestHitsMse(): Empty cluster.";
838  return -1.0;
839  }
840 
841  double mse = 0.0;
842  for (auto const& h : hits) {
843  unsigned int cryo = h->WireID().Cryostat;
844  unsigned int tpc = h->WireID().TPC;
845  unsigned int view = h->WireID().Plane;
846  unsigned int wire = h->WireID().Wire;
847  float drift = h->PeakTime();
848 
849  mse += Dist2(pma::WireDriftToCm(detProp, wire, drift, view, tpc, cryo), view, tpc, cryo);
850  }
851  if (normalized)
852  return mse / hits.size();
853  else
854  return mse;
855 }
856 
857 unsigned int
859  const std::vector<art::Ptr<recob::Hit>>& hits,
860  double dist) const
861 {
862  if (hits.empty()) {
863  mf::LogWarning("pma::Track3D") << "TestHits(): Empty cluster.";
864  return 0;
865  }
866 
867  TVector3 p3d;
868  double tst, d2 = dist * dist;
869  unsigned int nhits = 0;
870  for (auto const& h : hits)
871  if (GetUnconstrainedProj3D(detProp, h, p3d, tst) && tst < d2) ++nhits;
872 
873  return nhits;
874 }
875 
876 double
877 pma::Track3D::Length(size_t start, size_t stop, size_t step) const
878 {
879  auto const nHits = size();
880  if (nHits < 2) return 0.0;
881 
882  if (start > stop) {
883  size_t tmp = stop;
884  stop = start;
885  start = tmp;
886  }
887  if (start >= nHits - 1) return 0.0;
888  if (stop >= nHits) stop = nHits - 1;
889 
890  double result = 0.0;
891  pma::Hit3D *h1 = nullptr, *h0 = fHits[start];
892 
893  if (!step) step = 1;
894  size_t i = start + step;
895  while (i <= stop) {
896  h1 = fHits[i];
897  result += sqrt(pma::Dist2(h1->Point3D(), h0->Point3D()));
898  h0 = h1;
899  i += step;
900  }
901  if (i - step < stop) // last step jumped beyond the stop point
902  { // so need to add the last short segment
903  result += sqrt(pma::Dist2(h0->Point3D(), back()->Point3D()));
904  }
905  return result;
906 }
907 
908 int
909 pma::Track3D::NextHit(int index, unsigned int view, bool inclDisabled) const
910 {
911  pma::Hit3D* hit = nullptr;
912  if (index < -1) index = -1;
913  while (++index < (int)size()) // look for the next index of hit from the view
914  {
915  hit = fHits[index];
916  if (hit->View2D() == view) {
917  if (inclDisabled)
918  break;
919  else if (hit->IsEnabled())
920  break;
921  }
922  }
923  return index;
924 }
925 
926 int
927 pma::Track3D::PrevHit(int index, unsigned int view, bool inclDisabled) const
928 {
929  pma::Hit3D* hit = nullptr;
930  if (index > (int)size()) index = (int)size();
931  while (--index >= 0) // look for the prev index of hit from the view
932  {
933  hit = fHits[index];
934  if (hit->View2D() == view) {
935  if (inclDisabled)
936  break;
937  else if (hit->IsEnabled())
938  break;
939  }
940  }
941  return index;
942 }
943 
944 double
946  unsigned int view,
948  bool secondDir) const
949 {
950  pma::Hit3D* nexthit = nullptr;
952 
953  if (hit->View2D() != view) {
954  mf::LogWarning("pma::Track3D") << "Function used with the hit not matching specified view.";
955  }
956 
957  double dx = 0.0; // [cm]
958  bool hitFound = false;
959  int i = index;
960  switch (dir) {
962  while (!hitFound && (++i < (int)size())) {
963  nexthit = fHits[i];
964  dx += sqrt(pma::Dist2(hit->Point3D(), nexthit->Point3D()));
965 
966  if (nexthit->View2D() == view)
967  hitFound = true;
968  else
969  hitFound = false;
970 
971  hit = nexthit;
972  }
973  if (!hitFound) {
974  if (!secondDir)
975  dx = 0.5 * HitDxByView(index, view, pma::Track3D::kBackward, true);
976  else {
977  dx = Length();
978  mf::LogWarning("pma::Track3D") << "Single hit in this view.";
979  }
980  }
981  break;
982 
984  while (!hitFound && (--i >= 0)) {
985  nexthit = fHits[i];
986  dx += sqrt(pma::Dist2(hit->Point3D(), nexthit->Point3D()));
987 
988  if (nexthit->View2D() == view)
989  hitFound = true;
990  else
991  hitFound = false;
992 
993  hit = nexthit;
994  }
995  if (!hitFound) {
996  if (!secondDir)
997  dx = 0.5 * HitDxByView(index, view, pma::Track3D::kForward, true);
998  else {
999  dx = Length();
1000  mf::LogWarning("pma::Track3D") << "Single hit in this view.";
1001  }
1002  }
1003  break;
1004 
1005  default: mf::LogError("pma::Track3D") << "Direction undefined."; break;
1006  }
1007  return dx;
1008 }
1009 
1010 double
1011 pma::Track3D::HitDxByView(size_t index, unsigned int view) const
1012 {
1013  if (index < size()) {
1014  return 0.5 * (HitDxByView(index, view, pma::Track3D::kForward) +
1015  HitDxByView(index, view, pma::Track3D::kBackward));
1016  }
1017  else {
1018  mf::LogError("pma::Track3D") << "Hit index out of range.";
1019  return 0.0;
1020  }
1021 }
1022 
1025 {
1026  pma::Segment3D* seg = nullptr;
1027  unsigned int nCount = vtx->NextCount();
1028  unsigned int k = 0;
1029  while (k < nCount) {
1030  seg = static_cast<pma::Segment3D*>(vtx->Next(k));
1031  if (seg && (seg->Parent() == this)) return seg;
1032  k++;
1033  }
1034  return 0;
1035 }
1036 
1039 {
1040  if (vtx->Prev()) {
1041  auto seg = static_cast<pma::Segment3D*>(vtx->Prev());
1042  if (seg->Parent() == this) return seg;
1043  }
1044  return nullptr;
1045 }
1046 
1047 double
1048 pma::Track3D::GetRawdEdxSequence(std::map<size_t, std::vector<double>>& dedx,
1049  unsigned int view,
1050  unsigned int skip,
1051  bool inclDisabled) const
1052 {
1053  dedx.clear();
1054 
1055  if (!size()) return 0.0;
1056 
1057  size_t step = 1;
1058 
1059  pma::Hit3D* hit = nullptr;
1060 
1061  double rv, dr, dR, dq, dEq, qSkipped = 0.0;
1062 
1063  size_t j = NextHit(-1, view, inclDisabled), s = skip;
1064  if (j >= size()) return 0.0F; // no charged hits at all
1065  while (j < size()) // look for the first hit index
1066  {
1067  hit = fHits[j];
1068  dq = hit->SummedADC();
1069  if (s) {
1070  qSkipped += dq;
1071  s--;
1072  }
1073  else
1074  break;
1075 
1076  j = NextHit(j, view, inclDisabled);
1077  }
1078 
1079  size_t jmax = PrevHit(size(), view, inclDisabled);
1080 
1081  std::vector<size_t> indexes;
1082  TVector3 p0(0., 0., 0.), p1(0., 0., 0.);
1083  TVector2 c0(0., 0.), c1(0., 0.);
1084  while (j <= jmax) {
1085  indexes.clear(); // prepare to collect hit indexes used for this dE/dx entry
1086 
1087  indexes.push_back(j);
1088  hit = fHits[j];
1089 
1090  p0 = hit->Point3D();
1091  p1 = hit->Point3D();
1092 
1093  c0.Set(hit->Wire(), hit->PeakTime());
1094  c1.Set(hit->Wire(), hit->PeakTime());
1095 
1096  dEq = hit->SummedADC(); // [now it is ADC sum]
1097 
1098  dr = HitDxByView(j,
1099  view,
1100  pma::Track3D::kForward); // protection against hits on the same position
1101  rv = HitDxByView(j, view); // dx seen by j-th hit
1102  fHits[j]->fDx = rv;
1103  dR = rv;
1104 
1105  size_t m = 1; // number of hits with charge > 0
1106  while (((m < step) || (dR < 0.1) || (dr == 0.0)) && (j <= jmax)) {
1107  j = NextHit(j, view); // just next, even if tagged as outlier
1108  if (j > jmax) break; // no more hits in this view
1109 
1110  hit = fHits[j];
1111  if (!inclDisabled && !hit->IsEnabled()) {
1112  if (dr == 0.0)
1113  continue;
1114  else
1115  break;
1116  }
1117  indexes.push_back(j);
1118 
1119  p1 = hit->Point3D();
1120 
1121  c1.Set(hit->Wire(), hit->PeakTime());
1122 
1123  dq = hit->SummedADC();
1124 
1125  dEq += dq;
1126 
1127  dr = HitDxByView(j, view, pma::Track3D::kForward);
1128  rv = HitDxByView(j, view);
1129  fHits[j]->fDx = rv;
1130  dR += rv;
1131  m++;
1132  }
1133  p0 += p1;
1134  p0 *= 0.5;
1135  c0 += c1;
1136  c0 *= 0.5;
1137 
1138  double range = Length(0, j);
1139 
1140  std::vector<double> trk_section;
1141  trk_section.push_back(c0.X());
1142  trk_section.push_back(c0.Y());
1143  trk_section.push_back(p0.X());
1144  trk_section.push_back(p0.Y());
1145  trk_section.push_back(p0.Z());
1146  trk_section.push_back(dEq);
1147  trk_section.push_back(dR);
1148  trk_section.push_back(range);
1149 
1150  for (auto const idx : indexes)
1151  dedx[idx] = trk_section;
1152 
1153  j = NextHit(j, view, inclDisabled);
1154  }
1155 
1156  return qSkipped;
1157 }
1158 
1159 std::vector<float>
1161  unsigned int wire,
1162  unsigned int view) const
1163 {
1164  std::vector<float> drifts;
1165  for (size_t i = 0; i < fNodes.size() - 1; i++) {
1166  int tpc = fNodes[i]->TPC(), cryo = fNodes[i]->Cryo();
1167  if ((tpc != fNodes[i + 1]->TPC()) || (cryo != fNodes[i + 1]->Cryo())) continue;
1168 
1169  TVector2 p0 = pma::CmToWireDrift(detProp,
1170  fNodes[i]->Projection2D(view).X(),
1171  fNodes[i]->Projection2D(view).Y(),
1172  view,
1173  fNodes[i]->TPC(),
1174  fNodes[i]->Cryo());
1175  TVector2 p1 = pma::CmToWireDrift(detProp,
1176  fNodes[i + 1]->Projection2D(view).X(),
1177  fNodes[i + 1]->Projection2D(view).Y(),
1178  view,
1179  fNodes[i + 1]->TPC(),
1180  fNodes[i + 1]->Cryo());
1181 
1182  if ((p0.X() - wire) * (p1.X() - wire) <= 0.0) {
1183  double frac_w = (wire - p0.X()) / (p1.X() - p0.X());
1184  double d = p0.Y() + frac_w * (p1.Y() - p0.Y());
1185  drifts.push_back((float)d);
1186  }
1187  }
1188  return drifts;
1189 }
1190 
1191 size_t
1193  unsigned int view)
1194 {
1195  int dPrev, dw, w, wx, wPrev, i = NextHit(-1, view);
1196 
1197  pma::Hit3D* hitPrev = nullptr;
1198  pma::Hit3D* hit = nullptr;
1199 
1200  std::vector<pma::Hit3D*> missHits;
1201 
1202  while (i < (int)size()) {
1203  hitPrev = hit;
1204  hit = fHits[i];
1205 
1206  if (hit->View2D() == view) {
1207  w = hit->Wire();
1208  if (hitPrev) {
1209  wPrev = hitPrev->Wire();
1210  dPrev = (int)hitPrev->PeakTime();
1211  if (abs(w - wPrev) > 1) {
1212  if (w > wPrev)
1213  dw = 1;
1214  else
1215  dw = -1;
1216  wx = wPrev + dw;
1217  int k = 1;
1218  while (wx != w) {
1219  int peakTime = 0;
1220  unsigned int iWire = wx;
1221  std::vector<float> drifts = DriftsOfWireIntersection(detProp, wx, view);
1222  if (drifts.size()) {
1223  peakTime = drifts[0];
1224  for (size_t d = 1; d < drifts.size(); d++)
1225  if (fabs(drifts[d] - dPrev) < fabs(peakTime - dPrev)) peakTime = drifts[d];
1226  }
1227  else {
1228  mf::LogVerbatim("pma::Track3D") << "Track does not intersect with the wire " << wx;
1229  break;
1230  }
1231  pma::Hit3D* hmiss =
1232  new pma::Hit3D(detProp, iWire, view, hit->TPC(), hit->Cryo(), peakTime, 1.0, 1.0);
1233  missHits.push_back(hmiss);
1234  wx += dw;
1235  k++;
1236  }
1237  }
1238  }
1239  }
1240  else
1241  hit = hitPrev;
1242 
1243  i = NextHit(i, view, true);
1244  while ((i < (int)size()) && (hit->TPC() != fHits[i]->TPC())) {
1245  hitPrev = hit;
1246  hit = fHits[i];
1247  i = NextHit(i, view, true);
1248  }
1249  }
1250 
1251  if (missHits.size()) {
1252  for (size_t hi = 0; hi < missHits.size(); hi++) {
1253  push_back(missHits[hi]);
1254  }
1255 
1256  MakeProjection();
1257  SortHits();
1258  }
1259 
1260  return missHits.size();
1261 }
1262 
1263 void
1265 {
1266  fNodes.push_back(node);
1267  if (fNodes.size() > 1) RebuildSegments();
1268 }
1269 
1270 bool
1272 {
1273  pma::Segment3D* seg;
1274  pma::Segment3D* maxSeg = nullptr;
1275 
1276  size_t si = 0;
1277  while (si < fSegments.size()) {
1278  if (!fSegments[si]->IsFrozen()) {
1279  maxSeg = fSegments[si];
1280  break;
1281  }
1282  else
1283  si++;
1284  }
1285  if (!maxSeg) return false;
1286 
1287  unsigned int nHitsByView[3];
1288  unsigned int nHits, maxHits = 0;
1289  unsigned int vIndex = 0, segHits, maxSegHits = 0;
1290  float segLength, maxLength = maxSeg->Length();
1291  for (unsigned int i = si + 1; i < fNodes.size(); i++) {
1292  seg = static_cast<pma::Segment3D*>(fNodes[i]->Prev());
1293  if (seg->IsFrozen()) continue;
1294 
1295  nHitsByView[0] = seg->NEnabledHits(geo::kU);
1296  nHitsByView[1] = seg->NEnabledHits(geo::kV);
1297  nHitsByView[2] = seg->NEnabledHits(geo::kZ);
1298  segHits = nHitsByView[0] + nHitsByView[1] + nHitsByView[2];
1299 
1300  if (segHits < 15) {
1301  if ((nHitsByView[0] == 0) && ((nHitsByView[1] < 4) || (nHitsByView[2] < 4))) continue;
1302  if ((nHitsByView[1] == 0) && ((nHitsByView[0] < 4) || (nHitsByView[2] < 4))) continue;
1303  if ((nHitsByView[2] == 0) && ((nHitsByView[0] < 4) || (nHitsByView[1] < 4))) continue;
1304  }
1305 
1306  nHits = fNodes[i]->NEnabledHits() + seg->NEnabledHits() + fNodes[i - 1]->NEnabledHits();
1307 
1308  if (nHits > maxHits) {
1309  maxHits = nHits;
1310  maxLength = seg->Length();
1311  maxSegHits = segHits;
1312  maxSeg = seg;
1313  vIndex = i;
1314  }
1315  else if (nHits == maxHits) {
1316  segLength = seg->Length();
1317  if (segLength > maxLength) {
1318  maxLength = segLength;
1319  maxSegHits = segHits;
1320  maxSeg = seg;
1321  vIndex = i;
1322  }
1323  }
1324  }
1325 
1326  if (maxSegHits > 1) {
1327  maxSeg->SortHits();
1328 
1329  nHitsByView[0] = maxSeg->NEnabledHits(geo::kU);
1330  nHitsByView[1] = maxSeg->NEnabledHits(geo::kV);
1331  nHitsByView[2] = maxSeg->NEnabledHits(geo::kZ);
1332 
1333  unsigned int maxViewIdx = 2, midViewIdx = 2;
1334  if ((nHitsByView[2] >= nHitsByView[1]) && (nHitsByView[1] >= nHitsByView[0])) {
1335  maxViewIdx = 2;
1336  midViewIdx = 1;
1337  }
1338  else if ((nHitsByView[1] >= nHitsByView[2]) && (nHitsByView[2] >= nHitsByView[0])) {
1339  maxViewIdx = 1;
1340  midViewIdx = 2;
1341  }
1342  else if ((nHitsByView[0] >= nHitsByView[2]) && (nHitsByView[2] >= nHitsByView[1])) {
1343  maxViewIdx = 0;
1344  midViewIdx = 2;
1345  }
1346  else if ((nHitsByView[2] >= nHitsByView[0]) && (nHitsByView[0] >= nHitsByView[1])) {
1347  maxViewIdx = 2;
1348  midViewIdx = 0;
1349  }
1350  else if ((nHitsByView[0] >= nHitsByView[1]) && (nHitsByView[1] >= nHitsByView[2])) {
1351  maxViewIdx = 0;
1352  midViewIdx = 1;
1353  }
1354  else if ((nHitsByView[1] >= nHitsByView[0]) && (nHitsByView[0] >= nHitsByView[2])) {
1355  maxViewIdx = 1;
1356  midViewIdx = 0;
1357  }
1358  if (nHitsByView[midViewIdx] < 2) midViewIdx = maxViewIdx;
1359 
1360  if (nHitsByView[midViewIdx] < 2) {
1361  mf::LogVerbatim("pma::Track3D") << "AddNode(): too few hits.";
1362  return false;
1363  }
1364 
1365  unsigned int mHits[3] = {0, 0, 0};
1366  unsigned int halfIndex = (nHitsByView[midViewIdx] >> 1) - 1;
1367  unsigned int n = 0, i = 0, i0 = 0, i1 = 0;
1368  while (i < maxSeg->NHits() - 1) {
1369  if (maxSeg->Hit(i).IsEnabled()) {
1370  mHits[maxSeg->Hit(i).View2D()]++;
1371  if (maxSeg->Hit(i).View2D() == midViewIdx) {
1372  if (n == halfIndex) break;
1373  n++;
1374  }
1375  }
1376  i++;
1377  }
1378 
1379  i0 = i;
1380  i++;
1381  while ((i < maxSeg->NHits()) &&
1382  !((maxSeg->Hit(i).View2D() == midViewIdx) && maxSeg->Hit(i).IsEnabled())) {
1383  i++;
1384  }
1385  i1 = i;
1386 
1387  if (!nHitsByView[0]) {
1388  if (nHitsByView[1] && (mHits[1] < 2)) {
1389  mf::LogVerbatim("pma::Track3D") << "AddNode(): low Ind2 hits.";
1390  return false;
1391  }
1392  if (nHitsByView[2] && (mHits[2] < 2)) {
1393  mf::LogVerbatim("pma::Track3D") << "AddNode(): low Coll hits.";
1394  return false;
1395  }
1396  }
1397 
1398  maxSeg->SetProjection(maxSeg->Hit(i0));
1399  maxSeg->SetProjection(maxSeg->Hit(i1));
1400 
1401  unsigned int tpc = maxSeg->Hit(i0).TPC();
1402  unsigned int cryo = maxSeg->Hit(i0).Cryo();
1403 
1404  pma::Node3D* p = new pma::Node3D(detProp,
1405  (maxSeg->Hit(i0).Point3D() + maxSeg->Hit(i1).Point3D()) * 0.5,
1406  tpc,
1407  cryo,
1408  false,
1409  fNodes[vIndex]->GetDriftShift());
1410 
1411  fNodes.insert(fNodes.begin() + vIndex, p);
1412 
1413  maxSeg->AddNext(fNodes[vIndex]);
1414 
1415  seg = new pma::Segment3D(this, fNodes[vIndex], fNodes[vIndex + 1]);
1416  fSegments.insert(fSegments.begin() + vIndex, seg);
1417 
1418  return true;
1419  }
1420  else
1421  return false;
1422 }
1423 
1424 void
1426  TVector3 const& p3d,
1427  size_t at_idx,
1428  unsigned int tpc,
1429  unsigned int cryo)
1430 {
1431  pma::Node3D* vtx =
1432  new pma::Node3D(detProp, p3d, tpc, cryo, false, fNodes[at_idx]->GetDriftShift());
1433  fNodes.insert(fNodes.begin() + at_idx, vtx);
1434 
1435  if (fNodes.size() > 1) RebuildSegments();
1436 }
1437 
1438 bool
1440 {
1441  if ((fNodes.size() > 1) && (idx < fNodes.size())) {
1442  pma::Node3D* vtx = fNodes[idx];
1443  fNodes.erase(fNodes.begin() + idx);
1444  RebuildSegments();
1445 
1446  if (!vtx->NextCount() && !vtx->Prev()) { delete vtx; }
1447 
1448  return true;
1449  }
1450  else
1451  return false;
1452 }
1453 
1454 pma::Track3D*
1456  size_t idx,
1457  bool try_start_at_idx)
1458 {
1459  if (!idx || (idx + 1 >= fNodes.size())) return 0;
1460 
1461  pma::Node3D* n = nullptr;
1462  pma::Track3D* t0 = new pma::Track3D();
1463  t0->fT0 = fT0;
1464  t0->fT0Flag = fT0Flag;
1465  t0->fTag = fTag;
1466 
1467  for (size_t i = 0; i < idx; ++i) {
1468  n = fNodes.front();
1469  n->ClearAssigned();
1470 
1471  pma::Segment3D* s = static_cast<pma::Segment3D*>(n->Prev());
1472  if (s && (s->Parent() == this)) s->RemoveNext(n);
1473 
1474  size_t k = 0;
1475  while (k < n->NextCount()) {
1476  s = static_cast<pma::Segment3D*>(n->Next(k));
1477  if (s->Parent() == this)
1478  n->RemoveNext(s);
1479  else
1480  k++;
1481  }
1482 
1483  fNodes.erase(fNodes.begin());
1484  t0->fNodes.push_back(n);
1485  }
1486 
1487  n = fNodes.front();
1488  t0->fNodes.push_back(
1489  new pma::Node3D(detProp, n->Point3D(), n->TPC(), n->Cryo(), false, n->GetDriftShift()));
1490  t0->RebuildSegments();
1491  RebuildSegments();
1492 
1493  size_t h = 0;
1494  while (h < size()) {
1495  pma::Hit3D* h3d = fHits[h];
1496  unsigned int view = h3d->View2D(), tpc = h3d->TPC(), cryo = h3d->Cryo();
1497  double dist2D_old = Dist2(h3d->Point2D(), view, tpc, cryo);
1498  double dist2D_new = t0->Dist2(h3d->Point2D(), view, tpc, cryo);
1499 
1500  if ((dist2D_new < dist2D_old) && t0->HasTPC(tpc))
1501  t0->push_back(release_at(h));
1502  else if (!HasTPC(tpc) && t0->HasTPC(tpc))
1503  t0->push_back(release_at(h));
1504  else
1505  h++;
1506  }
1507 
1508  bool passed = true;
1509  if (HasTwoViews() && t0->HasTwoViews()) {
1510  if (try_start_at_idx && t0->CanFlip()) {
1511  mf::LogVerbatim("pma::Track3D") << " attach new t0 and this trk to a common start node";
1512  t0->Flip();
1513  passed = t0->AttachTo(fNodes.front());
1514  }
1515  else {
1516  mf::LogVerbatim("pma::Track3D") << " attach this trk to the new t0 end node";
1517  passed = AttachTo(t0->fNodes.back());
1518  }
1519  }
1520  else {
1521  mf::LogVerbatim("pma::Track3D") << " single-view track";
1522  passed = false;
1523  }
1524 
1525  if (!passed) {
1526  mf::LogVerbatim("pma::Track3D") << " undo split";
1527  while (t0->size())
1528  push_back(t0->release_at(0));
1529 
1530  for (size_t i = 0; i < idx; ++i) {
1531  fNodes.insert(fNodes.begin() + i, t0->fNodes.front());
1532  t0->fNodes.erase(t0->fNodes.begin());
1533  }
1534 
1535  RebuildSegments();
1536  delete t0;
1537  t0 = 0;
1538  }
1539 
1540  return t0;
1541 }
1542 
1543 bool
1545 {
1546  pma::Node3D* vtx = fNodes.front();
1547 
1548  if (vtx == vStart) return true; // already connected!
1549 
1550  pma::Track3D* rootThis = GetRoot();
1551  if (vStart->Prev()) {
1552  pma::Track3D* rootPrev = static_cast<pma::Segment3D*>(vStart->Prev())->Parent()->GetRoot();
1553  if (rootPrev->IsAttachedTo(rootThis)) { return false; }
1554  }
1555  else if (vStart->NextCount()) {
1556  pma::Track3D* rootNext = static_cast<pma::Segment3D*>(vStart->Next(0))->Parent()->GetRoot();
1557  if (rootNext->IsAttachedTo(rootThis)) { return false; }
1558  }
1559  else {
1560  return false;
1561  }
1562 
1563  for (auto n : fNodes)
1564  if (n == vStart) {
1565  mf::LogError("pma::Track3D") << "Don't create loops!";
1566  return false;
1567  }
1568 
1569  if (!noFlip && CanFlip() && (vStart->TPC() == fNodes.back()->TPC()) &&
1570  (pma::Dist2(vtx->Point3D(), vStart->Point3D()) >
1571  pma::Dist2(fNodes.back()->Point3D(), vStart->Point3D())) &&
1572  (fNodes.back()->NextCount() == 0)) {
1573  mf::LogError("pma::Track3D") << "Flip, endpoint closer to vStart.";
1574  Flip();
1575  }
1576 
1577  if (vStart->TPC() == vtx->TPC())
1578  return AttachToSameTPC(vStart);
1579  else
1580  return AttachToOtherTPC(vStart);
1581 }
1582 
1583 bool
1585 {
1586  if (fNodes.front()->Prev()) return false;
1587 
1588  mf::LogVerbatim("pma::Track3D") << "Attach to track in another TPC.";
1589 
1590  fNodes.insert(fNodes.begin(), vStart);
1591 
1592  fSegments.insert(fSegments.begin(), new pma::Segment3D(this, fNodes[0], fNodes[1]));
1593 
1594  return true;
1595 }
1596 
1597 bool
1599 {
1600  pma::Node3D* vtx = fNodes.front();
1601 
1602  if (vtx->Prev()) {
1603  pma::Segment3D* segPrev = static_cast<pma::Segment3D*>(vtx->Prev());
1604  pma::Track3D* tpPrev = segPrev->Parent();
1605  if (tpPrev->NextSegment(vtx)) { return false; }
1606  else if (tpPrev->CanFlip()) {
1607  tpPrev->Flip();
1608  } // flip in local vtx, no problem
1609  else {
1610  if (vStart->Prev()) {
1611  pma::Segment3D* segNew = static_cast<pma::Segment3D*>(vStart->Prev());
1612  pma::Track3D* tpNew = segNew->Parent();
1613  if (tpNew->NextSegment(vStart)) { return false; }
1614  else if (tpNew->CanFlip()) // flip in remote vStart, no problem
1615  {
1616  tpNew->Flip();
1617 
1618  mf::LogVerbatim("pma::Track3D") << "Reconnect prev to vStart.";
1619  tpPrev->fNodes[tpPrev->fNodes.size() - 1] = vStart;
1620  segPrev->AddNext(vStart);
1621  }
1622  else {
1623  mf::LogError("pma::Track3D") << "Flips in vtx and vStart not possible, cannot attach.";
1624  return false;
1625  }
1626  }
1627  else {
1628  mf::LogVerbatim("pma::Track3D") << "Reconnect prev to vStart.";
1629  tpPrev->fNodes[tpPrev->fNodes.size() - 1] = vStart;
1630  segPrev->AddNext(vStart);
1631  }
1632  }
1633  }
1634 
1635  while (vtx->NextCount()) // reconnect nexts to vStart
1636  {
1637  pma::Segment3D* seg = static_cast<pma::Segment3D*>(vtx->Next(0));
1638  pma::Track3D* trk = seg->Parent();
1639 
1640  vtx->RemoveNext(seg);
1641  trk->fNodes[0] = vStart;
1642  vStart->AddNext(seg);
1643  }
1644 
1645  if (vtx->NextCount() || vtx->Prev()) // better throw here
1646  {
1647  throw cet::exception("pma::Track3D") << "Something is still using disconnected vertex.";
1648  }
1649  else
1650  delete vtx; // ok
1651  return true;
1652 }
1653 
1654 bool
1656 {
1657  pma::Node3D* vtx = fNodes.back();
1658 
1659  if (vtx == vStart) return true; // already connected!
1660 
1661  pma::Track3D* rootThis = GetRoot();
1662  if (vStart->Prev()) {
1663  pma::Track3D* rootPrev = static_cast<pma::Segment3D*>(vStart->Prev())->Parent()->GetRoot();
1664  if (rootPrev->IsAttachedTo(rootThis)) { return false; }
1665  }
1666  else if (vStart->NextCount()) {
1667  pma::Track3D* rootNext = static_cast<pma::Segment3D*>(vStart->Next(0))->Parent()->GetRoot();
1668  if (rootNext->IsAttachedTo(rootThis)) { return false; }
1669  }
1670  else {
1671  return false;
1672  }
1673 
1674  for (auto n : fNodes)
1675  if (n == vStart) {
1676  mf::LogError("pma::Track3D") << "Don't create loops!";
1677  return false;
1678  }
1679 
1680  if (vStart->TPC() == vtx->TPC())
1681  return AttachBackToSameTPC(vStart);
1682  else
1683  return AttachBackToOtherTPC(vStart);
1684 }
1685 
1686 bool
1688 {
1689  if (vStart->Prev()) return false;
1690 
1691  mf::LogVerbatim("pma::Track3D") << "Attach to track in another TPC.";
1692 
1693  fNodes.push_back(vStart);
1694 
1695  size_t idx = fNodes.size() - 1;
1696  fSegments.push_back(new pma::Segment3D(this, fNodes[idx - 1], fNodes[idx]));
1697 
1698  return true;
1699 }
1700 
1701 bool
1703 {
1704  pma::Node3D* vtx = fNodes.back();
1705 
1706  if (vStart->Prev()) {
1707  pma::Segment3D* seg = static_cast<pma::Segment3D*>(vStart->Prev());
1708  pma::Track3D* tp = seg->Parent();
1709  if (tp->NextSegment(vStart)) {
1710  mf::LogError("pma::Track3D") << "Cannot attach back to inner node of other track.";
1711  return false;
1712  }
1713 
1714  if (tp->CanFlip())
1715  tp->Flip(); // flip in remote vStart, no problem
1716  else {
1717  mf::LogError("pma::Track3D") << "Flip not possible, cannot attach.";
1718  return false;
1719  }
1720  }
1721  fNodes[fNodes.size() - 1] = vStart;
1722  fSegments[fSegments.size() - 1]->AddNext(vStart);
1723 
1724  while (vtx->NextCount()) // reconnect nexts to vStart
1725  {
1726  pma::Segment3D* seg = static_cast<pma::Segment3D*>(vtx->Next(0));
1727  pma::Track3D* trk = seg->Parent();
1728 
1729  vtx->RemoveNext(seg);
1730  trk->fNodes[0] = vStart;
1731  vStart->AddNext(seg);
1732  }
1733 
1734  if (vtx->NextCount() || vtx->Prev()) {
1735  throw cet::exception("pma::Track3D")
1736  << "Something is still using disconnected vertex." << std::endl;
1737  }
1738  else
1739  delete vtx; // ok
1740 
1741  return true;
1742 }
1743 
1744 void
1746 {
1747  if (src->fNodes.front()->Prev()) {
1748  throw cet::exception("pma::Track3D")
1749  << "Cant extend with a track being a daughter of another track." << std::endl;
1750  }
1751 
1752  src->DeleteSegments(); // disconnect from vertices and delete
1753  for (size_t i = 0; i < src->fNodes.size(); ++i) {
1754  fNodes.push_back(src->fNodes[i]);
1755 
1756  size_t idx = fNodes.size() - 1;
1757  fSegments.push_back(new pma::Segment3D(this, fNodes[idx - 1], fNodes[idx]));
1758  }
1759  src->fNodes.clear(); // just empty the node collection
1760 
1761  while (src->size()) {
1762  push_back(src->release_at(0));
1763  } // move hits
1764 
1765  for (auto p : src->fAssignedPoints) {
1766  fAssignedPoints.push_back(p);
1767  } // move 3D reference points
1768  src->fAssignedPoints.clear();
1769 
1770  MakeProjection();
1771  SortHits();
1772 
1773  delete src;
1774 }
1775 
1776 pma::Track3D*
1778 {
1779  pma::Track3D* trk = nullptr;
1780 
1781  if (fNodes.size()) {
1782  pma::Segment3D* seg = static_cast<pma::Segment3D*>(fNodes.front()->Prev());
1783  if (seg) trk = seg->Parent()->GetRoot();
1784  if (!trk) trk = this;
1785  }
1786  else
1787  throw cet::exception("pma::Track3D") << "Broken track, no nodes.";
1788 
1789  return trk;
1790 }
1791 
1792 bool
1793 pma::Track3D::GetBranches(std::vector<pma::Track3D const*>& branches, bool skipFirst) const
1794 {
1795  for (auto trk : branches)
1796  if (trk == this) { throw cet::exception("pma::Track3D") << "Track tree has loop."; }
1797 
1798  branches.push_back(this);
1799 
1800  size_t i0 = 0;
1801  if (skipFirst) i0 = 1;
1802 
1803  for (size_t i = i0; i < fNodes.size(); ++i)
1804  for (size_t n = 0; n < fNodes[i]->NextCount(); n++) {
1805  pma::Segment3D* seg = static_cast<pma::Segment3D*>(fNodes[i]->Next(n));
1806  if (seg && (seg->Parent() != this)) seg->Parent()->GetBranches(branches, true);
1807  }
1808 
1809  return true;
1810 }
1811 
1812 bool
1814 {
1815  if (trk == this) return true;
1816 
1817  std::vector<pma::Track3D const*> branchesThis, branchesTrk;
1818 
1819  if (!trk->GetBranches(branchesTrk)) return true; // has loop - better check the reason
1820  if (!GetBranches(branchesThis)) return true; // has loop - better check the reason
1821 
1822  for (auto bThis : branchesThis)
1823  for (auto bTrk : branchesTrk)
1824  if (bThis == bTrk) return true;
1825  return false;
1826 }
1827 
1828 bool
1830 {
1831  for (auto point : fAssignedPoints)
1832  if (point == p) return true;
1833  return false;
1834 }
1835 
1836 double
1837 pma::Track3D::GetMse(unsigned int view) const
1838 {
1839  double sumMse = 0.0;
1840  unsigned int nEnabledHits = 0;
1841  for (auto n : fNodes) {
1842  sumMse += n->SumDist2(view);
1843  nEnabledHits += n->NEnabledHits(view);
1844  }
1845  for (auto s : fSegments) {
1846  sumMse += s->SumDist2(view);
1847  nEnabledHits += s->NEnabledHits(view);
1848  }
1849 
1850  if (nEnabledHits)
1851  return sumMse / nEnabledHits;
1852  else
1853  return 0.0;
1854 }
1855 
1856 double
1857 pma::Track3D::GetObjFunction(float penaltyFactor) const
1858 {
1859  double sum = 0.0;
1860  float p = penaltyFactor * fPenaltyValue;
1861  for (size_t i = 0; i < fNodes.size(); i++) {
1862  sum += fNodes[i]->GetObjFunction(p, fEndSegWeight);
1863  }
1864  return sum / fNodes.size();
1865 }
1866 
1867 double
1869  int nNodes,
1870  double eps,
1871  bool selAllHits,
1872  bool setAllNodes,
1873  size_t selSegHits,
1874  size_t selVtxHits)
1875 {
1876  if (!fNodes.size()) {
1877  mf::LogError("pma::Track3D") << "Track not initialized.";
1878  return 0.0;
1879  }
1880 
1881  if (!UpdateParams()) {
1882  mf::LogError("pma::Track3D") << "Track empty.";
1883  return 1.0e10;
1884  }
1885  double g0 = GetObjFunction(), g1 = 0.0;
1886  if (g0 == 0.0) return g0;
1887 
1888  // set branching flag only at the beginning, optimization is not changing
1889  // that and new nodes are not branching
1890  for (auto n : fNodes)
1891  n->SetVertexToBranching(setAllNodes);
1892 
1893  bool stop = false;
1894  fMinSegStop = fSegments.size();
1895  fMaxSegStop = (int)(size() / fMaxSegStopFactor) + 1;
1896  do {
1897  if (selSegHits || selVtxHits) SelectRndHits(selSegHits, selVtxHits);
1898 
1899  bool stepDone = true;
1900  unsigned int stepIter = 0;
1901  do {
1902  double gstep = 1.0;
1903  unsigned int iter = 0;
1904  while ((gstep > eps) && (iter < 1000)) {
1905  if ((fNodes.size() < 4) || (iter % 10 == 0))
1906  MakeProjection();
1907  else
1909 
1910  if (!UpdateParams()) {
1911  mf::LogError("pma::Track3D") << "Track empty.";
1912  return 0.0;
1913  }
1914 
1915  for (auto n : fNodes)
1916  n->Optimize(fPenaltyValue, fEndSegWeight);
1917 
1918  g1 = g0;
1919  g0 = GetObjFunction();
1920 
1921  if (g0 == 0.0F) {
1922  MakeProjection();
1923  break;
1924  }
1925  gstep = fabs(g0 - g1) / g0;
1926  iter++;
1927  }
1928 
1929  stepIter++;
1930  if (fNodes.size() > 2) {
1932  }
1933  } while (!stepDone && (stepIter < 5));
1934 
1935  if (selAllHits) {
1936  selAllHits = false;
1937  selSegHits = 0;
1938  selVtxHits = 0;
1939  if (SelectAllHits()) continue;
1940  }
1941 
1942  switch (nNodes) {
1943  case 0: stop = true; break; // just optimize existing vertices
1944 
1945  case -1: // grow and optimize until automatic stop condition
1946  mf::LogVerbatim("pma::Track3D") << "optimized segments: " << fSegments.size();
1947  if ((fSegments.size() >= fSegStopValue) || (fSegments.size() >= fMaxSegStop)) { stop = true; }
1948  else {
1949  if (!AddNode(detProp)) stop = true;
1950  }
1951  break;
1952 
1953  default: // grow and optimize until fixed number of vertices is added
1954  if (nNodes > 12) {
1955  if (AddNode(detProp)) {
1956  MakeProjection();
1957  nNodes--;
1958  }
1959  else {
1960  mf::LogVerbatim("pma::Track3D") << "stop (3)";
1961  stop = true;
1962  break;
1963  }
1964 
1965  if (AddNode(detProp)) {
1966  MakeProjection();
1967  nNodes--;
1968  if (AddNode(detProp)) nNodes--;
1969  }
1970  }
1971  else if (nNodes > 4) {
1972  if (AddNode(detProp)) {
1973  MakeProjection();
1974  nNodes--;
1975  }
1976  else {
1977  mf::LogVerbatim("pma::Track3D") << "stop (2)";
1978  stop = true;
1979  break;
1980  }
1981 
1982  if (AddNode(detProp)) nNodes--;
1983  }
1984  else {
1985  if (AddNode(detProp)) { nNodes--; }
1986  else {
1987  mf::LogVerbatim("pma::Track3D") << "stop (1)";
1988  stop = true;
1989  break;
1990  }
1991  }
1992  break;
1993  }
1994 
1995  } while (!stop);
1996 
1997  MakeProjection();
1998  return GetObjFunction();
1999 }
2000 
2001 bool
2002 pma::Track3D::UpdateParamsInTree(bool skipFirst, size_t& depth)
2003 {
2004  constexpr size_t maxTreeDepth = 100; // really big tree...
2005 
2006  pma::Node3D* vtx = fNodes.front();
2007 
2008  if (skipFirst) {
2009  if (auto segThis = NextSegment(vtx)) vtx = static_cast<pma::Node3D*>(segThis->Next());
2010 
2011  if (++depth > maxTreeDepth) { mf::LogError("pma::Track3D") << "Tree depth above allowed max."; }
2012  }
2013 
2014  bool isOK = true;
2015 
2016  while (vtx) {
2017  auto segThis = NextSegment(vtx);
2018 
2019  for (size_t i = 0; i < vtx->NextCount(); i++) {
2020  auto seg = static_cast<pma::Segment3D*>(vtx->Next(i));
2021  if (seg != segThis) { isOK &= seg->Parent()->UpdateParamsInTree(true, depth); }
2022  }
2023 
2024  if (segThis)
2025  vtx = static_cast<pma::Node3D*>(segThis->Next());
2026  else
2027  break;
2028  }
2029 
2030  if (!UpdateParams()) {
2031  mf::LogError("pma::Track3D") << "Track empty.";
2032  isOK = false;
2033  }
2034 
2035  --depth;
2036 
2037  return isOK;
2038 }
2039 
2040 double
2042 {
2043  pma::Node3D* vtx = fNodes.front();
2044 
2045  if (skipFirst) {
2046  if (auto segThis = NextSegment(vtx)) vtx = static_cast<pma::Node3D*>(segThis->Next());
2047  }
2048 
2049  double g = 0.0;
2050  while (vtx) {
2052  auto segThis = NextSegment(vtx);
2053 
2054  for (size_t i = 0; i < vtx->NextCount(); i++) {
2055  auto seg = static_cast<pma::Segment3D*>(vtx->Next(i));
2056  if (seg != segThis) g += seg->Parent()->TuneSinglePass(true);
2057  }
2058 
2059  if (segThis)
2060  vtx = static_cast<pma::Node3D*>(segThis->Next());
2061  else
2062  break;
2063  }
2064 
2065  return g + GetObjFunction();
2066 }
2067 
2068 pma::Track3D*
2069 pma::Track3D::GetNearestTrkInTree(const TVector3& p3d_cm, double& dist, bool skipFirst)
2070 {
2071  pma::Node3D* vtx = fNodes.front();
2072 
2073  if (skipFirst) {
2074  if (auto segThis = NextSegment(vtx)) vtx = static_cast<pma::Node3D*>(segThis->Next());
2075  }
2076 
2077  pma::Track3D* result = this;
2078  dist = sqrt(Dist2(p3d_cm));
2079 
2080  pma::Track3D* candidate = nullptr;
2081  while (vtx) {
2082  auto segThis = NextSegment(vtx);
2083 
2084  double d;
2085  for (size_t i = 0; i < vtx->NextCount(); i++) {
2086  auto seg = static_cast<pma::Segment3D*>(vtx->Next(i));
2087  if (seg != segThis) {
2088  candidate = seg->Parent()->GetNearestTrkInTree(p3d_cm, d, true);
2089  if (d < dist) {
2090  dist = d;
2091  result = candidate;
2092  }
2093  }
2094  }
2095 
2096  if (segThis)
2097  vtx = static_cast<pma::Node3D*>(segThis->Next());
2098  else
2099  break;
2100  }
2101 
2102  return result;
2103 }
2104 
2105 pma::Track3D*
2106 pma::Track3D::GetNearestTrkInTree(const TVector2& p2d_cm,
2107  unsigned view,
2108  unsigned int tpc,
2109  unsigned int cryo,
2110  double& dist,
2111  bool skipFirst)
2112 {
2113  pma::Node3D* vtx = fNodes.front();
2114 
2115  if (skipFirst) {
2116  if (auto segThis = NextSegment(vtx)) vtx = static_cast<pma::Node3D*>(segThis->Next());
2117  }
2118 
2119  pma::Track3D* result = this;
2120  dist = Dist2(p2d_cm, view, tpc, cryo);
2121 
2122  pma::Track3D* candidate = nullptr;
2123  while (vtx) {
2124  auto segThis = NextSegment(vtx);
2125 
2126  double d;
2127  for (size_t i = 0; i < vtx->NextCount(); i++) {
2128  auto seg = static_cast<pma::Segment3D*>(vtx->Next(i));
2129  if (seg != segThis) {
2130  candidate = seg->Parent()->GetNearestTrkInTree(p2d_cm, view, tpc, cryo, d, true);
2131  if (d < dist) {
2132  dist = d;
2133  result = candidate;
2134  }
2135  }
2136  }
2137 
2138  if (segThis)
2139  vtx = static_cast<pma::Node3D*>(segThis->Next());
2140  else
2141  break;
2142  }
2143 
2144  dist = sqrt(dist);
2145  return result;
2146 }
2147 
2148 void
2150 {
2151  bool skipFirst;
2152  if (trkRoot)
2153  skipFirst = true;
2154  else {
2155  trkRoot = this;
2156  skipFirst = false;
2157  }
2158 
2159  pma::Node3D* vtx = fNodes.front();
2160 
2161  if (skipFirst) {
2162  if (auto segThis = NextSegment(vtx)) vtx = static_cast<pma::Node3D*>(segThis->Next());
2163  }
2164 
2165  while (vtx) {
2166  auto segThis = NextSegment(vtx);
2167 
2168  for (size_t i = 0; i < vtx->NextCount(); i++) {
2169  auto seg = static_cast<pma::Segment3D*>(vtx->Next(i));
2170  if (seg != segThis) seg->Parent()->ReassignHitsInTree(trkRoot);
2171  }
2172 
2173  if (segThis)
2174  vtx = static_cast<pma::Node3D*>(segThis->Next());
2175  else
2176  break;
2177  }
2178 
2179  double d0, dmin;
2180  pma::Hit3D* hit = nullptr;
2181  pma::Track3D* nearestTrk = nullptr;
2182  size_t i = 0;
2183  while (HasTwoViews(2) && (i < size())) {
2184  hit = fHits[i];
2185  d0 = hit->GetDistToProj();
2186 
2187  nearestTrk = GetNearestTrkInTree(hit->Point2D(), hit->View2D(), hit->TPC(), hit->Cryo(), dmin);
2188  if ((nearestTrk != this) && (dmin < 0.5 * d0)) {
2189  nearestTrk->push_back(release_at(i));
2190  mf::LogVerbatim("pma::Track3D") << "*** hit moved to another track ***";
2191  }
2192  else
2193  i++;
2194  }
2195  if (!size()) { mf::LogError("pma::Track3D") << "ALL hits moved to other tracks."; }
2196 }
2197 
2198 void
2200 {
2201  pma::Node3D* vtx = fNodes.front();
2202 
2203  if (skipFirst) {
2204  if (auto segThis = NextSegment(vtx)) vtx = static_cast<pma::Node3D*>(segThis->Next());
2205  }
2206 
2207  while (vtx) {
2208  auto segThis = NextSegment(vtx);
2209 
2210  for (size_t i = 0; i < vtx->NextCount(); i++) {
2211  auto seg = static_cast<pma::Segment3D*>(vtx->Next(i));
2212  if (seg != segThis) seg->Parent()->MakeProjectionInTree(true);
2213  }
2214 
2215  if (segThis)
2216  vtx = static_cast<pma::Node3D*>(segThis->Next());
2217  else
2218  break;
2219  }
2220 
2221  MakeProjection();
2222 }
2223 
2224 void
2226 {
2227  pma::Node3D* vtx = fNodes.front();
2228 
2229  if (skipFirst) {
2230  if (auto segThis = NextSegment(vtx)) vtx = static_cast<pma::Node3D*>(segThis->Next());
2231  }
2232 
2233  while (vtx) {
2234  auto segThis = NextSegment(vtx);
2235 
2236  for (size_t i = 0; i < vtx->NextCount(); i++) {
2237  auto seg = static_cast<pma::Segment3D*>(vtx->Next(i));
2238  if (seg != segThis) seg->Parent()->SortHitsInTree(true);
2239  }
2240 
2241  if (segThis)
2242  vtx = static_cast<pma::Node3D*>(segThis->Next());
2243  else
2244  break;
2245  }
2246 
2247  SortHits();
2248 }
2249 
2250 double
2252 {
2253  pma::Node3D* vtx = fNodes.front();
2254 
2255  if (skipFirst) {
2256  if (auto segThis = NextSegment(vtx)) vtx = static_cast<pma::Node3D*>(segThis->Next());
2257  }
2258 
2259  double g = 0.0;
2260  while (vtx) {
2261  auto segThis = NextSegment(vtx);
2262 
2263  for (size_t i = 0; i < vtx->NextCount(); i++) {
2264  auto seg = static_cast<pma::Segment3D*>(vtx->Next(i));
2265  if (seg != segThis) g += seg->Parent()->GetObjFnInTree(true);
2266  }
2267 
2268  if (segThis)
2269  vtx = static_cast<pma::Node3D*>(segThis->Next());
2270  else
2271  break;
2272  }
2273 
2274  return g + GetObjFunction();
2275 }
2276 
2277 double
2278 pma::Track3D::TuneFullTree(double eps, double gmax)
2279 {
2280  if (size_t depth = 1; !UpdateParamsInTree(false, depth)) {
2281  mf::LogError("pma::Track3D") << "TuneFullTree failed.";
2282  return -2; // negetive to tag destroyed tree
2283  }
2284 
2285  double g0 = GetObjFnInTree(), g1 = 0.0;
2286  if (!std::isfinite(g0)) {
2287  mf::LogError("pma::Track3D") << "Infinifte value of g.";
2288  return -2; // negetive to tag destroyed tree
2289  }
2290  if (g0 > gmax) {
2291  mf::LogWarning("pma::Track3D") << "Too high value of g: " << g0;
2292  return -1; // negetive to tag bad tree
2293  }
2294  if (g0 == 0.0) return g0;
2295 
2296  mf::LogVerbatim("pma::Track3D") << "Tune tree, g = " << g0;
2297  unsigned int stepIter = 0;
2298  do {
2299  double gstep = 1.0;
2300  unsigned int iter = 0;
2301  while ((gstep > eps) && (iter < 60)) {
2302  g1 = g0;
2303  g0 = TuneSinglePass();
2304 
2306 
2307  if (size_t depth = 1; !UpdateParamsInTree(false, depth)) {
2308  g0 = -2;
2309  break;
2310  } // negetive to tag destroyed tree
2311 
2312  if (g0 == 0.0F) break;
2313 
2314  gstep = fabs(g0 - g1) / g0;
2315  iter++;
2316  }
2317 
2318  stepIter++;
2319 
2320  } while (stepIter < 5);
2321 
2323  SortHitsInTree();
2324 
2325  if (g0 >= 0) { mf::LogVerbatim("pma::Track3D") << " done, g = " << g0; }
2326  else {
2327  mf::LogError("pma::Track3D") << "TuneFullTree failed.";
2328  }
2329  return g0;
2330 }
2331 
2332 void
2334  const detinfo::DetectorPropertiesData& detProp,
2335  double dx,
2336  bool skipFirst)
2337 {
2338  pma::Node3D* node = fNodes.front();
2339 
2340  if (skipFirst) {
2341  if (auto segThis = NextSegment(node)) node = static_cast<pma::Node3D*>(segThis->Next());
2342  }
2343 
2344  while (node) {
2345  node->ApplyDriftShift(dx);
2346 
2347  auto segThis = NextSegment(node);
2348  for (size_t i = 0; i < node->NextCount(); i++) {
2349  auto seg = static_cast<pma::Segment3D*>(node->Next(i));
2350  if (seg != segThis) seg->Parent()->ApplyDriftShiftInTree(clockData, detProp, dx, true);
2351  }
2352 
2353  if (segThis)
2354  node = static_cast<pma::Node3D*>(segThis->Next());
2355  else
2356  break;
2357  }
2358 
2359  for (auto h : fHits) {
2360  h->fPoint3D[0] += dx;
2361  }
2362 
2363  for (auto p : fAssignedPoints) {
2364  (*p)[0] += dx;
2365  }
2366 
2367  // For T0 we need to make sure we use the total shift, not just this current
2368  // one in case of multiple shifts
2369  double newdx = fNodes.front()->GetDriftShift();
2370 
2371  // Now convert this newdx into T0 and store in fT0
2372  SetT0FromDx(clockData, detProp, newdx);
2373 }
2374 
2375 void
2377  detinfo::DetectorPropertiesData const& detProp,
2378  double dx)
2379 {
2380  auto const* geom = lar::providerFrom<geo::Geometry>();
2381  const geo::TPCGeo& tpcGeo = geom->TPC(fNodes.front()->TPC(), fNodes.front()->Cryo());
2382 
2383  // GetXTicksCoefficient has a sign that we don't care about. We need to decide
2384  // the sign for ourselves based on the drift direction of the TPC
2385  double correctedSign = 0;
2386  if (tpcGeo.DetectDriftDirection() > 0) {
2387  if (dx > 0) { correctedSign = 1.0; }
2388  else {
2389  correctedSign = -1.0;
2390  }
2391  }
2392  else {
2393  if (dx > 0) { correctedSign = -1.0; }
2394  else {
2395  correctedSign = 1.0;
2396  }
2397  }
2398 
2399  // The magnitude of x in ticks is fine
2400  double dxInTicks = fabs(dx / detProp.GetXTicksCoefficient());
2401  // Now correct the sign
2402  dxInTicks = dxInTicks * correctedSign;
2403  // At this stage, we have xInTicks relative to the trigger time
2404  double dxInTime = dxInTicks * clockData.TPCClock().TickPeriod();
2405  // Reconstructed times are relative to the trigger (t=0), so this is our T0
2406  fT0 = dxInTime;
2407 
2408  mf::LogDebug("pma::Track3D") << dx << ", " << dxInTicks << ", " << correctedSign << ", " << fT0
2409  << ", " << tpcGeo.DetectDriftDirection()
2410  << " :: " << clockData.TriggerTime() << ", "
2411  << clockData.TriggerOffsetTPC() << std::endl;
2412 
2413  // Mark this track as having a measured T0
2414  fT0Flag = true;
2415 }
2416 
2417 void
2419 {
2420  for (size_t i = 0; i < fSegments.size(); i++)
2421  delete fSegments[i];
2422  fSegments.clear();
2423 }
2424 
2425 void
2427 {
2428  DeleteSegments();
2429 
2430  fSegments.reserve(fNodes.size() - 1);
2431  for (size_t i = 1; i < fNodes.size(); i++) {
2432  fSegments.push_back(new pma::Segment3D(this, fNodes[i - 1], fNodes[i]));
2433  }
2434 }
2435 
2436 void
2438 {
2439  unsigned int nhits = 0;
2440  while (!nhits && (fNodes.size() > 2) && !fNodes.front()->IsBranching()) {
2441  pma::Node3D* vtx = fNodes.front();
2442  nhits = vtx->NHits();
2443 
2444  pma::Segment3D* seg = NextSegment(vtx);
2445  nhits += seg->NHits();
2446 
2447  if (!nhits) {
2448  if (vtx->NextCount() < 2) delete vtx;
2449  fNodes.erase(fNodes.begin());
2450 
2451  delete seg;
2452  fSegments.erase(fSegments.begin());
2453 
2454  MakeProjection();
2455  }
2456  }
2457 
2458  nhits = 0;
2459  while (!nhits && (fNodes.size() > 2) && !fNodes.back()->IsBranching()) {
2460  pma::Node3D* vtx = fNodes.back();
2461  nhits = vtx->NHits();
2462 
2463  pma::Segment3D* seg = PrevSegment(vtx);
2464  nhits += seg->NHits();
2465 
2466  if (!nhits) {
2467  if (vtx->NextCount() < 1) delete fNodes.back();
2468  fNodes.pop_back();
2469 
2470  delete seg;
2471  fSegments.pop_back();
2472 
2473  MakeProjection();
2474  }
2475  }
2476 }
2477 
2478 bool
2480 {
2481  pma::Element3D* el;
2482  pma::Node3D* vtx;
2483 
2484  if (!(fNodes.front()->Prev())) {
2485  el = GetNearestElement(front()->Point3D());
2486  vtx = dynamic_cast<pma::Node3D*>(el);
2487  if (vtx) {
2488  if (vtx == fNodes.front())
2489  fNodes.front()->SetPoint3D(front()->Point3D());
2490  else {
2491  mf::LogWarning("pma::Track3D") << "First hit is projected to inner node.";
2492  return false;
2493  }
2494  }
2495  else {
2496  pma::Segment3D* seg = dynamic_cast<pma::Segment3D*>(el);
2497  if (seg) {
2498  if (seg->Prev() == fNodes.front()) {
2499  double l0 = seg->Length();
2500  fNodes.front()->SetPoint3D(front()->Point3D());
2501  if ((seg->Length() < 0.2 * l0) && (fNodes.size() > 2)) {
2502  pma::Node3D* toRemove = fNodes[1];
2503  if (toRemove->NextCount() == 1) {
2504  mf::LogWarning("pma::Track3D")
2505  << "ShiftEndsToHits(): Short start segment, node removed.";
2506  fNodes.erase(fNodes.begin() + 1);
2507 
2508  RebuildSegments();
2509  MakeProjection();
2510 
2511  delete toRemove;
2512  }
2513  else {
2514  mf::LogWarning("pma::Track3D") << "Branching node, not removed.";
2515  return false;
2516  }
2517  }
2518  }
2519  else {
2520  mf::LogWarning("pma::Track3D") << "First hit is projected to inner segment.";
2521  return false;
2522  }
2523  }
2524  }
2525  }
2526 
2527  if (!(fNodes.back()->NextCount())) {
2528  el = GetNearestElement(back()->Point3D());
2529  vtx = dynamic_cast<pma::Node3D*>(el);
2530  if (vtx) {
2531  if (vtx == fNodes.back())
2532  fNodes.back()->SetPoint3D(back()->Point3D());
2533  else {
2534  mf::LogWarning("pma::Track3D") << "First hit is projected to inner node.";
2535  return false;
2536  }
2537  }
2538  else {
2539  pma::Segment3D* seg = dynamic_cast<pma::Segment3D*>(el);
2540  if (seg) {
2541  if (seg->Next() == fNodes.back()) {
2542  double l0 = seg->Length();
2543  fNodes.back()->SetPoint3D(back()->Point3D());
2544  if ((seg->Length() < 0.2 * l0) && (fNodes.size() > 2)) {
2545  size_t idx = fNodes.size() - 2;
2546  pma::Node3D* toRemove = fNodes[idx];
2547  if (toRemove->NextCount() == 1) {
2548  mf::LogWarning("pma::Track3D")
2549  << "ShiftEndsToHits(): Short end segment, node removed.";
2550  fNodes.erase(fNodes.begin() + idx);
2551 
2552  RebuildSegments();
2553  MakeProjection();
2554 
2555  delete toRemove;
2556  }
2557  else {
2558  mf::LogWarning("pma::Track3D") << "Branching node, not removed.";
2559  return false;
2560  }
2561  }
2562  }
2563  else {
2564  mf::LogWarning("pma::Track3D") << "First hit is projected to inner segment.";
2565  return false;
2566  }
2567  }
2568  }
2569  }
2570 
2571  return true;
2572 }
2573 
2574 double
2575 pma::Track3D::Dist2(const TVector2& p2d,
2576  unsigned int view,
2577  unsigned int tpc,
2578  unsigned int cryo) const
2579 {
2580  double dist, min_dist = 1.0e12;
2581 
2582  int t = (int)tpc, c = (int)cryo;
2583  auto n0 = fNodes.front();
2584  if ((n0->TPC() == t) && (n0->Cryo() == c)) {
2585  dist = n0->GetDistance2To(p2d, view);
2586  if (dist < min_dist) min_dist = dist;
2587  }
2588  auto n1 = fNodes.back();
2589  if ((n1->TPC() == t) && (n1->Cryo() == c)) {
2590  dist = n1->GetDistance2To(p2d, view);
2591  if (dist < min_dist) min_dist = dist;
2592  }
2593 
2594  for (auto s : fSegments)
2595  if ((s->TPC() == t) && (s->Cryo() == c)) {
2596  dist = s->GetDistance2To(p2d, view);
2597  if (dist < min_dist) min_dist = dist;
2598  }
2599  return min_dist;
2600 }
2601 
2602 double
2603 pma::Track3D::Dist2(const TVector3& p3d) const
2604 {
2605  using namespace ranges;
2606  auto to_distance2 = [&p3d](auto seg) { return seg->GetDistance2To(p3d); };
2607  return min(fSegments | views::transform(to_distance2));
2608 }
2609 
2612  unsigned int view,
2613  int tpc,
2614  bool skipFrontVtx,
2615  bool skipBackVtx) const
2616 {
2617  if (fSegments.front()->TPC() < 0) skipFrontVtx = false;
2618  if (fSegments.back()->TPC() < 0) skipBackVtx = false;
2619 
2620  if (skipFrontVtx && skipBackVtx && (fSegments.size() == 1))
2621  return fSegments.front(); // no need for searching...
2622 
2623  size_t v0 = 0, v1 = fNodes.size();
2624  if (skipFrontVtx) v0 = 1;
2625  if (skipBackVtx) --v1;
2626 
2627  pma::Element3D* pe_min = nullptr;
2628  auto min_dist = std::numeric_limits<double>::max();
2629  for (size_t i = v0; i < v1; i++)
2630  if (fNodes[i]->TPC() == tpc) {
2631  double dist = fNodes[i]->GetDistance2To(p2d, view);
2632  if (dist < min_dist) {
2633  min_dist = dist;
2634  pe_min = fNodes[i];
2635  }
2636  }
2637  for (auto segment : fSegments)
2638  if (segment->TPC() == tpc) {
2639  if (segment->TPC() < 0) continue; // segment between TPC's
2640 
2641  double const dist = segment->GetDistance2To(p2d, view);
2642  if (dist < min_dist) {
2643  min_dist = dist;
2644  pe_min = segment;
2645  }
2646  }
2647  if (!pe_min) throw cet::exception("pma::Track3D") << "Nearest element not found." << std::endl;
2648  return pe_min;
2649 }
2650 
2652 pma::Track3D::GetNearestElement(const TVector3& p3d) const
2653 {
2654  pma::Element3D* pe_min = fNodes.front();
2655  double dist, min_dist = pe_min->GetDistance2To(p3d);
2656  for (size_t i = 1; i < fNodes.size(); i++) {
2657  dist = fNodes[i]->GetDistance2To(p3d);
2658  if (dist < min_dist) {
2659  min_dist = dist;
2660  pe_min = fNodes[i];
2661  }
2662  }
2663  for (size_t i = 0; i < fSegments.size(); i++) {
2664  dist = fSegments[i]->GetDistance2To(p3d);
2665  if (dist < min_dist) {
2666  min_dist = dist;
2667  pe_min = fSegments[i];
2668  }
2669  }
2670  return pe_min;
2671 }
2672 
2673 bool
2676  TVector3& p3d,
2677  double& dist2) const
2678 {
2679  TVector2 p2d = pma::WireDriftToCm(detProp,
2680  hit->WireID().Wire,
2681  hit->PeakTime(),
2682  hit->WireID().Plane,
2683  hit->WireID().TPC,
2684  hit->WireID().Cryostat);
2685 
2686  pma::Segment3D* seg = nullptr;
2687  double d2, min_d2 = 1.0e100;
2688  int tpc = (int)hit->WireID().TPC;
2689  int view = hit->WireID().Plane;
2690  for (size_t i = 0; i < fSegments.size(); i++) {
2691  if (fSegments[i]->TPC() != tpc) continue;
2692 
2693  d2 = fSegments[i]->GetDistance2To(p2d, view);
2694  if (d2 < min_d2) {
2695  min_d2 = d2;
2696  seg = fSegments[i];
2697  }
2698  }
2699  if (seg) {
2700  p3d = seg->GetUnconstrainedProj3D(p2d, view);
2701  dist2 = min_d2;
2702 
2703  pma::Node3D* prev = static_cast<pma::Node3D*>(seg->Prev());
2704  return prev->SameTPC(p3d); // 3D can be beyond the segment endpoints => in other TPC
2705  }
2706 
2707  return false;
2708 }
2709 
2710 void
2712 {
2713  std::vector<pma::Hit3D*> hits_tmp;
2714  hits_tmp.reserve(size());
2715 
2716  pma::Node3D* vtx = fNodes.front();
2717  pma::Segment3D* seg = NextSegment(vtx);
2718  while (vtx) {
2719  vtx->SortHits();
2720  for (size_t i = 0; i < vtx->NHits(); i++) {
2721  pma::Hit3D* h3d = &(vtx->Hit(i));
2722  if (h3d->fParent == this) hits_tmp.push_back(h3d);
2723  }
2724 
2725  if (seg) {
2726  seg->SortHits();
2727  for (size_t i = 0; i < seg->NHits(); i++) {
2728  pma::Hit3D* h3d = &(seg->Hit(i));
2729  if (h3d->fParent == this) hits_tmp.push_back(h3d);
2730  }
2731 
2732  vtx = static_cast<pma::Node3D*>(seg->Next());
2733  seg = NextSegment(vtx);
2734  }
2735  else
2736  break;
2737  }
2738 
2739  if (size() == hits_tmp.size()) {
2740  for (size_t i = 0; i < size(); i++) {
2741  fHits[i] = hits_tmp[i];
2742  }
2743  }
2744  else
2745  mf::LogError("pma::Track3D") << "Hit sorting problem.";
2746 }
2747 
2748 unsigned int
2750 {
2751  SortHits();
2752 
2753  unsigned int nDisabled = 0;
2754 
2755  std::array<int, 4> hasHits{{}};
2756 
2757  pma::Hit3D* nextHit = nullptr;
2758  int hitIndex = -1;
2759 
2760  bool rebuild = false, stop = false;
2761  int nViews = 0;
2762 
2763  do {
2764  pma::Node3D* vtx = fNodes.front();
2765  pma::Segment3D* seg = NextSegment(vtx);
2766  if (!seg) break;
2767 
2768  if (vtx->NPoints() + seg->NPoints() > 0) hasHits[3] = 1;
2769 
2770  for (size_t i = 0; i < vtx->NHits(); i++) {
2771  hitIndex = index_of(&(vtx->Hit(i)));
2772  if ((hitIndex >= 0) && (hitIndex + 1 < (int)size()))
2773  nextHit = fHits[hitIndex + 1];
2774  else
2775  nextHit = 0;
2776 
2777  if (vtx->Hit(i).IsEnabled()) hasHits[vtx->Hit(i).View2D()] = 1;
2778  if (nextHit && nextHit->IsEnabled()) hasHits[nextHit->View2D()] = 1;
2779  nViews = hasHits[0] + hasHits[1] + hasHits[2] + hasHits[3];
2780  if (nViews < 2) {
2781  if (vtx->Hit(i).IsEnabled()) {
2782  vtx->Hit(i).SetEnabled(false);
2783  nDisabled++;
2784  }
2785  }
2786  }
2787  for (size_t i = 0; i < seg->NHits(); i++) {
2788  hitIndex = index_of(&(seg->Hit(i)));
2789  if ((hitIndex >= 0) && (hitIndex + 1 < (int)size()))
2790  nextHit = fHits[hitIndex + 1];
2791  else
2792  nextHit = 0;
2793 
2794  if (seg->Hit(i).IsEnabled()) hasHits[seg->Hit(i).View2D()] = 1;
2795  if (nextHit && nextHit->IsEnabled()) hasHits[nextHit->View2D()] = 1;
2796  nViews = hasHits[0] + hasHits[1] + hasHits[2] + hasHits[3];
2797  if (nViews < 2) {
2798  if (seg->Hit(i).IsEnabled()) {
2799  seg->Hit(i).SetEnabled(false);
2800  nDisabled++;
2801  }
2802  }
2803  }
2804 
2805  if (fNodes.size() < 3) break;
2806 
2807  nViews = hasHits[1] + hasHits[2] + hasHits[3];
2808  if (hasHits[0] || (nViews > 1))
2809  stop = true;
2810  else if (!fNodes.front()->IsBranching()) {
2811  pma::Node3D* vtx_front = fNodes.front();
2812  fNodes.erase(fNodes.begin());
2813  delete vtx_front;
2814  rebuild = true;
2815  }
2816 
2817  } while (!stop);
2818 
2819  stop = false;
2820  nViews = 0;
2821  hasHits.fill(0);
2822 
2823  do {
2824  pma::Node3D* vtx = fNodes.back();
2825  pma::Segment3D* seg = PrevSegment(vtx);
2826  if (!seg) break;
2827 
2828  if (vtx->NPoints() || seg->NPoints()) hasHits[3] = 1;
2829 
2830  for (int i = vtx->NHits() - 1; i >= 0; i--) {
2831  hitIndex = index_of(&(vtx->Hit(i)));
2832  if ((hitIndex >= 0) && (hitIndex - 1 >= 0))
2833  nextHit = fHits[hitIndex - 1];
2834  else
2835  nextHit = 0;
2836 
2837  if (vtx->Hit(i).IsEnabled()) hasHits[vtx->Hit(i).View2D()] = 1;
2838  if (nextHit && nextHit->IsEnabled()) hasHits[nextHit->View2D()] = 1;
2839  nViews = hasHits[0] + hasHits[1] + hasHits[2] + hasHits[3];
2840  if (nViews < 2) {
2841  if (vtx->Hit(i).IsEnabled()) {
2842  vtx->Hit(i).SetEnabled(false);
2843  nDisabled++;
2844  }
2845  }
2846  }
2847  for (int i = seg->NHits() - 1; i >= 0; i--) {
2848  hitIndex = index_of(&(seg->Hit(i)));
2849  if ((hitIndex >= 0) && (hitIndex - 1 >= 0))
2850  nextHit = fHits[hitIndex - 1];
2851  else
2852  nextHit = 0;
2853 
2854  if (seg->Hit(i).IsEnabled()) hasHits[seg->Hit(i).View2D()] = 1;
2855  if (nextHit && nextHit->IsEnabled()) hasHits[nextHit->View2D()] = 1;
2856  nViews = hasHits[0] + hasHits[1] + hasHits[2] + hasHits[3];
2857  if (nViews < 2) {
2858  if (seg->Hit(i).IsEnabled()) {
2859  seg->Hit(i).SetEnabled(false);
2860  nDisabled++;
2861  }
2862  }
2863  }
2864 
2865  if (fNodes.size() < 3) break;
2866 
2867  nViews = hasHits[1] + hasHits[2] + hasHits[3];
2868  if (hasHits[0] || (nViews > 1))
2869  stop = true;
2870  else if (!fNodes.back()->IsBranching()) {
2871  pma::Node3D* vtx_back = fNodes.back();
2872  fNodes.pop_back();
2873  delete vtx_back;
2874  rebuild = true;
2875  }
2876 
2877  } while (!stop);
2878 
2879  if (rebuild) {
2880  RebuildSegments();
2881  MakeProjection();
2882  }
2883 
2884  return nDisabled;
2885 }
2886 
2887 bool
2889 {
2890  if (fraction < 0.0F) fraction = 0.0F;
2891  if (fraction > 1.0F) fraction = 1.0F;
2892  if (fraction < 1.0F) std::sort(fHits.begin(), fHits.end(), pma::bTrajectory3DDistLess());
2893 
2894  size_t nHitsColl = (size_t)(fraction * NHits(geo::kZ));
2895  size_t nHitsInd2 = (size_t)(fraction * NHits(geo::kV));
2896  size_t nHitsInd1 = (size_t)(fraction * NHits(geo::kU));
2897  size_t coll = 0, ind2 = 0, ind1 = 0;
2898 
2899  bool b, changed = false;
2900  for (auto h : fHits) {
2901  b = h->IsEnabled();
2902  if (fraction < 1.0F) {
2903  h->SetEnabled(false);
2904  switch (h->View2D()) {
2905  case geo::kZ:
2906  if (coll++ < nHitsColl) h->SetEnabled(true);
2907  break;
2908  case geo::kV:
2909  if (ind2++ < nHitsInd2) h->SetEnabled(true);
2910  break;
2911  case geo::kU:
2912  if (ind1++ < nHitsInd1) h->SetEnabled(true);
2913  break;
2914  }
2915  }
2916  else
2917  h->SetEnabled(true);
2918 
2919  changed |= (b != h->IsEnabled());
2920  }
2921 
2922  if (fraction < 1.0F) {
2925  }
2926  return changed;
2927 }
2928 
2929 bool
2930 pma::Track3D::SelectRndHits(size_t segmax, size_t vtxmax)
2931 {
2932  bool changed = false;
2933  for (auto n : fNodes)
2934  changed |= n->SelectRndHits(vtxmax);
2935  for (auto s : fSegments)
2936  changed |= s->SelectRndHits(segmax);
2937  return changed;
2938 }
2939 
2940 bool
2942 {
2943  bool changed = false;
2944  for (auto h : fHits) {
2945  changed |= !(h->IsEnabled());
2946  h->SetEnabled(true);
2947  }
2948  return changed;
2949 }
2950 
2951 void
2953 {
2954  for (auto n : fNodes)
2955  n->ClearAssigned(this);
2956  for (auto s : fSegments)
2957  s->ClearAssigned(this);
2958 
2959  pma::Element3D* pe = nullptr;
2960 
2961  bool skipFrontVtx = false, skipBackVtx = false;
2962 
2963  // skip outermost vertices if not branching
2964  if (!(fNodes.front()->IsFrozen()) && !(fNodes.front()->Prev()) &&
2965  (fNodes.front()->NextCount() == 1)) {
2966  skipFrontVtx = true;
2967  }
2968  if (!(fNodes.front()->IsFrozen()) && (fNodes.back()->NextCount() == 0)) { skipBackVtx = true; }
2969 
2970  for (auto h : fHits) // assign hits to nodes/segments
2971  {
2972  pe = GetNearestElement(h->Point2D(), h->View2D(), h->TPC(), skipFrontVtx, skipBackVtx);
2973  pe->AddHit(h);
2974  }
2975 
2976  for (auto p : fAssignedPoints) // assign ref points to nodes/segments
2977  {
2978  pe = GetNearestElement(*p);
2979  pe->AddPoint(p);
2980  }
2981 
2982  for (auto n : fNodes)
2983  n->UpdateHitParams();
2984  for (auto s : fSegments)
2985  s->UpdateHitParams();
2986 }
2987 
2988 void
2990 {
2991  std::vector<std::pair<pma::Hit3D*, pma::Element3D*>> assignments;
2992  assignments.reserve(fHits.size());
2993 
2994  for (auto hi : fHits) {
2995  pma::Element3D* pe = nullptr;
2996 
2997  for (auto s : fSegments) {
2998  for (size_t j = 0; j < s->NHits(); ++j)
2999  if (hi == s->Hits()[j]) // look at next/prev vtx,seg,vtx
3000  {
3001  pe = s;
3002  double min_d2 = s->GetDistance2To(hi->Point2D(), hi->View2D());
3003  int const tpc = hi->TPC();
3004 
3005  pma::Node3D* nnext = static_cast<pma::Node3D*>(s->Next());
3006  if (nnext->TPC() == tpc) {
3007  double const d2 = nnext->GetDistance2To(hi->Point2D(), hi->View2D());
3008  if (d2 < min_d2) {
3009  min_d2 = d2;
3010  pe = nnext;
3011  }
3012 
3013  pma::Segment3D* snext = NextSegment(nnext);
3014  if (snext && (snext->TPC() == tpc)) {
3015  double const d2 = snext->GetDistance2To(hi->Point2D(), hi->View2D());
3016  if (d2 < min_d2) {
3017  min_d2 = d2;
3018  pe = snext;
3019  }
3020 
3021  nnext = static_cast<pma::Node3D*>(snext->Next());
3022  if (nnext->TPC() == tpc) {
3023  double const d2 = nnext->GetDistance2To(hi->Point2D(), hi->View2D());
3024  if (d2 < min_d2) {
3025  min_d2 = d2;
3026  pe = nnext;
3027  }
3028  }
3029  }
3030  }
3031 
3032  pma::Node3D* nprev = static_cast<pma::Node3D*>(s->Prev());
3033  if (nprev->TPC() == tpc) {
3034  double const d2 = nprev->GetDistance2To(hi->Point2D(), hi->View2D());
3035  if (d2 < min_d2) {
3036  min_d2 = d2;
3037  pe = nprev;
3038  }
3039 
3040  pma::Segment3D* sprev = PrevSegment(nprev);
3041  if (sprev && (sprev->TPC() == tpc)) {
3042  double const d2 = sprev->GetDistance2To(hi->Point2D(), hi->View2D());
3043  if (d2 < min_d2) {
3044  min_d2 = d2;
3045  pe = sprev;
3046  }
3047 
3048  nprev = static_cast<pma::Node3D*>(sprev->Prev());
3049  if (nprev->TPC() == tpc) {
3050  double const d2 = nprev->GetDistance2To(hi->Point2D(), hi->View2D());
3051  if (d2 < min_d2) {
3052  min_d2 = d2;
3053  pe = nprev;
3054  }
3055  }
3056  }
3057  }
3058 
3059  s->RemoveHitAt(j);
3060  break;
3061  }
3062  if (pe) break;
3063  }
3064 
3065  if (!pe)
3066  for (auto n : fNodes) {
3067  for (size_t j = 0; j < n->NHits(); ++j)
3068  if (hi == n->Hits()[j]) // look at next/prev seg,vtx,seg
3069  {
3070  pe = n;
3071  double d2, min_d2 = n->GetDistance2To(hi->Point2D(), hi->View2D());
3072  int tpc = hi->TPC();
3073 
3074  pma::Segment3D* snext = NextSegment(n);
3075  if (snext && (snext->TPC() == tpc)) {
3076  d2 = snext->GetDistance2To(hi->Point2D(), hi->View2D());
3077  if (d2 < min_d2) {
3078  min_d2 = d2;
3079  pe = snext;
3080  }
3081 
3082  pma::Node3D* nnext = static_cast<pma::Node3D*>(snext->Next());
3083  if (nnext->TPC() == tpc) {
3084  d2 = nnext->GetDistance2To(hi->Point2D(), hi->View2D());
3085  if (d2 < min_d2) {
3086  min_d2 = d2;
3087  pe = nnext;
3088  }
3089 
3090  snext = NextSegment(nnext);
3091  if (snext && (snext->TPC() == tpc)) {
3092  d2 = snext->GetDistance2To(hi->Point2D(), hi->View2D());
3093  if (d2 < min_d2) {
3094  min_d2 = d2;
3095  pe = snext;
3096  }
3097  }
3098  }
3099  }
3100 
3101  pma::Segment3D* sprev = PrevSegment(n);
3102  if (sprev && (sprev->TPC() == tpc)) {
3103  d2 = sprev->GetDistance2To(hi->Point2D(), hi->View2D());
3104  if (d2 < min_d2) {
3105  min_d2 = d2;
3106  pe = sprev;
3107  }
3108 
3109  pma::Node3D* nprev = static_cast<pma::Node3D*>(sprev->Prev());
3110  if (nprev->TPC() == tpc) {
3111  d2 = nprev->GetDistance2To(hi->Point2D(), hi->View2D());
3112  if (d2 < min_d2) {
3113  min_d2 = d2;
3114  pe = nprev;
3115  }
3116 
3117  sprev = PrevSegment(nprev);
3118  if (sprev && (sprev->TPC() == tpc)) {
3119  d2 = sprev->GetDistance2To(hi->Point2D(), hi->View2D());
3120  if (d2 < min_d2) {
3121  min_d2 = d2;
3122  pe = sprev;
3123  }
3124  }
3125  }
3126  }
3127 
3128  n->RemoveHitAt(j);
3129  break;
3130  }
3131  if (pe) break;
3132  }
3133 
3134  if (pe)
3135  assignments.emplace_back(hi, pe);
3136  else
3137  mf::LogWarning("pma::Track3D") << "Hit was not assigned to any element.";
3138  }
3139 
3140  for (auto const& a : assignments)
3141  a.second->AddHit(a.first);
3142 
3143  for (auto n : fNodes)
3144  n->UpdateHitParams();
3145  for (auto s : fSegments)
3146  s->UpdateHitParams();
3147 }
3148 
3149 void
3151 {
3152  for (auto n : fNodes)
3153  n->UpdateProjection();
3154  for (auto s : fSegments)
3155  s->UpdateProjection();
3156 }
3157 
3158 double
3160 {
3161  double sum = 0.0;
3162  unsigned int count = 0;
3163 
3164  for (auto n : fNodes) {
3165  sum += n->SumDist2();
3166  count += n->NEnabledHits();
3167  }
3168 
3169  for (auto s : fSegments) {
3170  sum += s->SumDist2();
3171  count += s->NEnabledHits();
3172  }
3173 
3174  if (count) { return sum / count; }
3175  else {
3176  mf::LogError("pma::Track3D") << "0 enabled hits in AverageDist2 calculation.";
3177  return 0;
3178  }
3179 }
3180 
3181 bool
3183 {
3184  size_t n = size();
3185  if (n == 0) {
3186  fPenaltyValue = 1;
3187  fSegStopValue = 1;
3188  return false;
3189  }
3190 
3191  float nCubeRoot = pow((double)n, 1.0 / 3.0);
3192  float avgDist2Root = sqrt(AverageDist2());
3193  if (avgDist2Root > 0) {
3194  fPenaltyValue = fPenaltyFactor * pow((double)fSegments.size(), 1.8) * avgDist2Root /
3195  (fHitsRadius * nCubeRoot);
3196 
3197  fSegStopValue = (int)(fSegStopFactor * nCubeRoot * fHitsRadius / avgDist2Root);
3199  return true;
3200  }
3201  else {
3202  fPenaltyValue = 1;
3203  fSegStopValue = 1;
3204  return false;
3205  }
3206 }
3207 
3208 bool
3209 pma::Track3D::SwapVertices(size_t v0, size_t v1)
3210 {
3211  if (v0 == v1) return false;
3212 
3213  if (v0 > v1) {
3214  size_t vx = v0;
3215  v0 = v1;
3216  v1 = vx;
3217  }
3218 
3219  pma::Node3D* vtmp;
3220  if (v1 - v0 == 1) {
3221  pma::Segment3D* midSeg = NextSegment(fNodes[v0]);
3222  pma::Segment3D* prevSeg = PrevSegment(fNodes[v0]);
3223  pma::Segment3D* nextSeg = NextSegment(fNodes[v1]);
3224 
3225  fNodes[v1]->RemoveNext(nextSeg);
3226  midSeg->Disconnect();
3227 
3228  vtmp = fNodes[v0];
3229  fNodes[v0] = fNodes[v1];
3230  fNodes[v1] = vtmp;
3231 
3232  if (prevSeg) prevSeg->AddNext(fNodes[v0]);
3233  fNodes[v0]->AddNext(midSeg);
3234  midSeg->AddNext(fNodes[v1]);
3235  if (nextSeg) fNodes[v1]->AddNext(nextSeg);
3236 
3237  return false;
3238  }
3239  else {
3240  vtmp = fNodes[v0];
3241  fNodes[v0] = fNodes[v1];
3242  fNodes[v1] = vtmp;
3243  return true;
3244  }
3245 }
3246 
3247 bool
3249 {
3250  unsigned int v1, v2;
3251  switch (endCode) {
3252  case pma::Track3D::kBegin:
3253  if (fSegments.front()->IsFrozen()) return false;
3254  if (fNodes.front()->NextCount() > 1) return false;
3255  v1 = 0;
3256  v2 = 1;
3257  break;
3258  case pma::Track3D::kEnd:
3259  if (fSegments.back()->IsFrozen()) return false;
3260  if (fNodes.back()->NextCount() > 1) return false;
3261  v1 = fNodes.size() - 1;
3262  v2 = fNodes.size() - 2;
3263  break;
3264  default: return false;
3265  }
3266  if (fNodes[v1]->TPC() != fNodes[v2]->TPC()) return false;
3267 
3268  double g1, g0 = GetObjFunction();
3269 
3270  if (SwapVertices(v1, v2)) RebuildSegments();
3271  MakeProjection();
3272  g1 = GetObjFunction();
3273 
3274  if (g1 >= g0) {
3275  if (SwapVertices(v1, v2)) RebuildSegments();
3276  MakeProjection();
3277  return false;
3278  }
3279  else
3280  return true;
3281 }
3282 
3283 void
3285 {
3286  std::vector<pma::Hit3D*> hitsColl, hitsInd1, hitsInd2;
3287  for (auto hit : fHits) {
3288  switch (hit->View2D()) {
3289  case geo::kZ: hitsColl.push_back(hit); break;
3290  case geo::kV: hitsInd2.push_back(hit); break;
3291  case geo::kU: hitsInd1.push_back(hit); break;
3292  }
3293  }
3294  fHitsRadius = std::max({pma::GetHitsRadius2D(hitsColl, true),
3295  pma::GetHitsRadius2D(hitsInd2, true),
3296  pma::GetHitsRadius2D(hitsInd1, true)});
3297 }
void MakeProjection()
bool SelectHits(float fraction=1.0F)
void ApplyDriftShift(double dx)
Definition: PmaNode3D.h:138
end
while True: pbar.update(maxval-len(onlies[E][S])) #print iS, "/", len(onlies[E][S]) found = False for...
code to link reconstructed objects back to the MC truth information
void AddHits(detinfo::DetectorPropertiesData const &detProp, const std::vector< art::Ptr< recob::Hit >> &hits)
Definition: PmaTrack3D.cxx:403
void MakeFastProjection()
MaybeLogger_< ELseverityLevel::ELsev_info, true > LogVerbatim
TVector3 const & Point3D() const
Definition: PmaNode3D.h:45
void AutoFlip(pma::Track3D::EDirection dir, double thr=0.0, unsigned int n=0)
Definition: PmaTrack3D.cxx:672
bool HasTPC(int tpc) const
Definition: PmaTrack3D.h:167
def stdev(lst)
Definition: HandyFuncs.py:269
virtual bool AddNext(pma::SortedObjectBase *nextElement)
double GetMse(unsigned int view=geo::kUnknown) const
MSE of hits weighted with hit amplidudes and wire plane coefficients.
void SortHitsInTree(bool skipFirst=false)
bool AttachTo(pma::Node3D *vStart, bool noFlip=false)
double GetHitsRadius2D(const std::vector< pma::Hit3D * > &hits, bool exact=false)
Definition: Utilities.cxx:98
static constexpr double g
Definition: Units.h:144
static QCString result
double Dist2(const TVector2 &p2d, unsigned int view, unsigned int tpc, unsigned int cryo) const
Implementation of the Projection Matching Algorithm.
TVector2 const & Point2D() const noexcept
Definition: PmaHit3D.h:72
bool IsAttachedTo(pma::Track3D const *trk) const
G4double thr[100]
Definition: G4S2Light.cc:59
bool SelectAllHits()
pma::Track3D * fParent
Definition: PmaHit3D.h:199
double Dist2(const TVector2 &v1, const TVector2 &v2)
Definition: Utilities.cxx:37
void ClearNodes()
Definition: PmaTrack3D.cxx:113
int PrevHit(int index, unsigned int view=geo::kZ, bool inclDisabled=false) const
Definition: PmaTrack3D.cxx:927
double GetXTicksCoefficient(int t, int c) const
void ApplyDriftShiftInTree(const detinfo::DetectorClocksData &clockData, detinfo::DetectorPropertiesData const &detProp, double dx, bool skipFirst=false)
Planes which measure V.
Definition: geo_types.h:130
Unknown view.
Definition: geo_types.h:136
geo::WireID WireID() const
Definition: Hit.h:233
void ClearAssigned(pma::Track3D *trk=0) override
Definition: PmaNode3D.cxx:853
void AddPoint(TVector3 *p)
Definition: PmaElement3D.h:80
virtual int RemoveNext(pma::SortedObjectBase *nextElement)
pma::Hit3D const * front() const
Definition: PmaTrack3D.h:98
bool IsEnabled() const noexcept
Definition: PmaHit3D.h:161
Implementation of the Projection Matching Algorithm.
unsigned int TestHits(detinfo::DetectorPropertiesData const &detProp, const std::vector< art::Ptr< recob::Hit >> &hits, double dist=0.4) const
Count close 2D hits.
Definition: PmaTrack3D.cxx:858
::fhicl::TupleAs< Point(::geo::Length_t,::geo::Length_t,::geo::Length_t)> Point3D
Atom object for reading a 3D point or vector (centimeters).
constexpr T pow(T x)
Definition: pow.h:72
unsigned int Cryo() const noexcept
Definition: PmaHit3D.h:83
double MinX() const
Returns the world x coordinate of the start of the box.
Definition: BoxBoundedGeo.h:88
Geometry information for a single TPC.
Definition: TPCGeo.h:38
void ReassignHitsInTree(pma::Track3D *plRoot=nullptr)
int index_of(const pma::Hit3D *hit) const
Definition: PmaTrack3D.cxx:335
void RebuildSegments()
double GetDistance2To(const TVector3 &p3d) const override
Distance [cm] from the 3D point to the point 3D.
Definition: PmaNode3D.cxx:178
bool AttachBackToSameTPC(pma::Node3D *vStart)
struct vector vector
bool GetBranches(std::vector< pma::Track3D const * > &branches, bool skipFirst=false) const
virtual unsigned int NextCount(void) const
Definition: SortedObjects.h:84
bool AttachToOtherTPC(pma::Node3D *vStart)
bool CheckEndSegment(pma::Track3D::ETrackEnd endCode)
bool InitFromHits(detinfo::DetectorPropertiesData const &detProp, int tpc, int cryo, float initEndSegW=0.05F)
Definition: PmaTrack3D.cxx:121
double GetObjFunction(float penaltyFactor=1.0F) const
Objective function optimized in track reconstruction.
TVector3 const & Point3D() const
Definition: PmaHit3D.h:55
CryostatID_t Cryostat
Index of cryostat.
Definition: geo_types.h:212
bool Initialize(detinfo::DetectorPropertiesData const &detProp, float initEndSegW=0.05F)
Definition: PmaTrack3D.cxx:77
void Optimize(float penaltyValue, float endSegWeight)
Definition: PmaNode3D.cxx:843
Planes which measure Z direction.
Definition: geo_types.h:132
TVector2 WireDriftToCm(detinfo::DetectorPropertiesData const &detProp, unsigned int wire, float drift, unsigned int plane, unsigned int tpc, unsigned int cryo)
Definition: Utilities.cxx:294
std::vector< unsigned int > TPCs() const
Definition: PmaTrack3D.cxx:452
WireID_t Wire
Index of the wire within its plane.
Definition: geo_types.h:580
string dir
MaybeLogger_< ELseverityLevel::ELsev_error, false > LogError
std::vector< pma::Segment3D * > fSegments
Definition: PmaTrack3D.h:494
bool AttachBackToOtherTPC(pma::Node3D *vStart)
float fPenaltyValue
Definition: PmaTrack3D.h:505
unsigned int Wire() const noexcept
Definition: PmaHit3D.h:98
recob::tracking::Vector_t Vector3D
Definition: Utilities.h:31
double TuneFullTree(double eps=0.001, double gmax=50.0)
pma::Vector3D GetDirection3D(size_t index) const
Get trajectory direction at given hit index.
Definition: PmaTrack3D.cxx:378
double Optimize(const detinfo::DetectorPropertiesData &detProp, int nNodes=-1, double eps=0.01, bool selAllHits=true, bool setAllNodes=true, size_t selSegHits=0, size_t selVtxHits=0)
Main optimization method.
double TuneSinglePass(bool skipFirst=false)
float fEndSegWeight
Definition: PmaTrack3D.h:506
art framework interface to geometry description
constexpr double TickPeriod() const noexcept
A single tick period in microseconds.
Definition: ElecClock.h:352
unsigned int NEnabledHits(unsigned int view=geo::kUnknown) const
Definition: PmaTrack3D.cxx:432
int TPC(void) const
TPC index or -1 if out of any TPC.
Definition: PmaElement3D.h:34
std::vector< unsigned int > Cryos() const
Definition: PmaTrack3D.cxx:471
pma::Track3D * Split(detinfo::DetectorPropertiesData const &detProp, size_t idx, bool try_start_at_idx=true)
T abs(T value)
double TestHitsMse(detinfo::DetectorPropertiesData const &detProp, const std::vector< art::Ptr< recob::Hit >> &hits, bool normalized=true) const
MSE of 2D hits.
Definition: PmaTrack3D.cxx:832
bool UpdateParamsInTree(bool skipFirst, size_t &depth)
Planes which measure U.
Definition: geo_types.h:129
bool CanFlip() const
Check if the track can be flipped without breaking any other track.
Definition: PmaTrack3D.cxx:653
size_t NPoints(void) const
Definition: PmaElement3D.h:79
unsigned int fSegStopValue
Definition: PmaTrack3D.h:500
void ExtendWith(pma::Track3D *src)
Extend the track with everything from src, delete the src;.
void AddNode(pma::Node3D *node)
pma::Hit3D * release_at(size_t index)
Definition: PmaTrack3D.cxx:343
bool isfinite(Vector const &v)
Returns whether all components of the vector are finite.
unsigned int NHits(unsigned int view) const
Definition: PmaTrack3D.cxx:422
pma::Node3D * FirstElement() const
Definition: PmaTrack3D.h:340
pma::Node3D * LastElement() const
Definition: PmaTrack3D.h:345
float PeakTime() const noexcept
Definition: PmaHit3D.h:103
pma::Track3D * GetRoot()
std::void_t< T > n
virtual int RemoveNext(pma::SortedObjectBase *nextElement)
const double a
double fT0
Definition: PmaTrack3D.h:509
bool GetUnconstrainedProj3D(detinfo::DetectorPropertiesData const &detProp, art::Ptr< recob::Hit > hit, TVector3 &p3d, double &dist2) const
double GetDriftShift() const
Definition: PmaNode3D.h:144
bool HasRefPoint(TVector3 *p) const
bool InitFromRefPoints(detinfo::DetectorPropertiesData const &detProp, int tpc, int cryo)
Definition: PmaTrack3D.cxx:211
std::vector< float > DriftsOfWireIntersection(detinfo::DetectorPropertiesData const &detProp, unsigned int wire, unsigned int view) const
void SetT0FromDx(const detinfo::DetectorClocksData &clockData, detinfo::DetectorPropertiesData const &detProp, double dx)
Function to convert dx into dT0.
p
Definition: test.py:223
ElecClock const & TPCClock() const noexcept
Borrow a const TPC clock with time set to Trigger time [us].
float SummedADC() const noexcept
Definition: PmaHit3D.h:109
pma::Hit3D & Hit(size_t index)
Definition: PmaElement3D.h:62
void SetEnabled(bool state) noexcept
Definition: PmaHit3D.h:166
void RemoveHits(const std::vector< art::Ptr< recob::Hit >> &hits)
Remove hits; removes also hit->node/seg assignments.
Definition: PmaTrack3D.cxx:412
string tmp
Definition: languages.py:63
float fPenaltyFactor
Definition: PmaTrack3D.h:497
bool SameTPC(const TVector3 &p3d, float margin=0.0F) const
Check if p3d is in the same TPC as the node.
Definition: PmaNode3D.cxx:102
double GetObjFnInTree(bool skipFirst=false)
void InternalFlip(std::vector< pma::Track3D * > &toSort)
Definition: PmaTrack3D.cxx:604
bool erase(const art::Ptr< recob::Hit > &hit)
Definition: PmaTrack3D.cxx:364
static int max(int a, int b)
void UpdateHitsRadius()
PlaneID_t Plane
Index of the plane within its TPC.
Definition: geo_types.h:493
unsigned int DisableSingleViewEnds()
bool IntersectionPoint(geo::WireID const &wid1, geo::WireID const &wid2, double &y, double &z) const
Returns the intersection point of two wires.
bool AttachToSameTPC(pma::Node3D *vStart)
void InsertNode(detinfo::DetectorPropertiesData const &detProp, TVector3 const &p3d, size_t at_idx, unsigned int tpc, unsigned int cryo)
double TriggerTime() const
Trigger electronics clock time in [us].
std::vector< pma::Node3D * > fNodes
Definition: PmaTrack3D.h:493
Detector simulation of raw signals on wires.
bool SelectRndHits(size_t segmax, size_t vtxmax)
size_t CompleteMissingWires(detinfo::DetectorPropertiesData const &detProp, unsigned int view)
double ConvertTicksToX(double ticks, int p, int t, int c) const
void InitFromMiddle(detinfo::DetectorPropertiesData const &detProp, int tpc, int cryo)
Definition: PmaTrack3D.cxx:299
void DeleteSegments()
void push_back(pma::Hit3D *hit)
Definition: PmaTrack3D.h:87
bool UpdateParams()
double AverageDist2() const
unsigned int View2D() const noexcept
Definition: PmaHit3D.h:93
bool HasTwoViews(size_t nmin=1) const
Definition: PmaTrack3D.cxx:442
int Cryo(void) const
Cryostat index or -1 if out of any cryostat.
Definition: PmaElement3D.h:36
void CleanupTails()
Cut out tails with no hits assigned.
Implementation of the Projection Matching Algorithm.
float PeakTime() const
Time of the signal peak, in tick units.
Definition: Hit.h:218
void MakeProjectionInTree(bool skipFirst=false)
std::map< size_t, std::vector< double > > dedx_map
Definition: Utilities.h:33
void SortHits(void)
virtual pma::SortedObjectBase * Next(unsigned int index=0) const
Definition: SortedObjects.h:79
double Length(size_t step=1) const
Definition: PmaTrack3D.h:117
bool IsFrozen(void) const
Check if the vertex 3D position is fixed.
Definition: PmaElement3D.h:99
double GetRawdEdxSequence(std::map< size_t, std::vector< double >> &dedx, unsigned int view=geo::kZ, unsigned int skip=0, bool inclDisabled=false) const
T min(sqlite3 *const db, std::string const &table_name, std::string const &column_name)
Definition: statistics.h:55
Contains all timing reference information for the detector.
constexpr double dist(const TReal *x, const TReal *y, const unsigned int dimension)
std::vector< TVector3 * > fAssignedPoints
Definition: PmaTrack3D.h:484
pma::Segment3D * PrevSegment(pma::Node3D *vtx) const
bool RemoveNode(size_t idx)
static unsigned int reverse(QString &chars, unsigned char *level, unsigned int a, unsigned int b)
Definition: qstring.cpp:11649
MaybeLogger_< ELseverityLevel::ELsev_success, false > LogDebug
float fSegStopFactor
Definition: PmaTrack3D.h:504
double GetDistToProj() const
Definition: PmaHit3D.h:136
MaybeLogger_< ELseverityLevel::ELsev_warning, false > LogWarning
int NextHit(int index, unsigned int view=geo::kZ, bool inclDisabled=false) const
Definition: PmaTrack3D.cxx:909
virtual double GetDistance2To(const TVector3 &p3d) const =0
Distance [cm] from the 3D point to the object 3D.
pma::Hit3D const * back() const
Definition: PmaTrack3D.h:103
static bool * b
Definition: config.cpp:1043
TVector2 CmToWireDrift(detinfo::DetectorPropertiesData const &detProp, float xw, float yd, unsigned int plane, unsigned int tpc, unsigned int cryo)
Definition: Utilities.cxx:307
double HitDxByView(size_t index, unsigned int view) const
double GetDistance2To(const TVector3 &p3d) const override
Distance [cm] from the 3D segment to the point 3D.
void AddHit(pma::Hit3D *h)
Definition: PmaElement3D.h:68
list x
Definition: train.py:276
bool SwapVertices(size_t v0, size_t v1)
virtual void Disconnect(void)
TPCGeo const & TPC(unsigned int const tpc=0, unsigned int const cstat=0) const
Returns the specified TPC.
pma::Track3D * GetNearestTrkInTree(const TVector3 &p3d_cm, double &dist, bool skipFirst=false)
virtual bool AddNext(pma::SortedObjectBase *nextElement)
float fMaxSegStopFactor
Definition: PmaTrack3D.h:498
bool SelectAllHits(void)
virtual pma::SortedObjectBase * Prev(void) const
Definition: SortedObjects.h:44
bool Flip(const detinfo::DetectorPropertiesData &detProp, std::vector< pma::Track3D * > &allTracks)
Definition: PmaTrack3D.cxx:533
TPCID_t TPC
Index of the TPC within its cryostat.
Definition: geo_types.h:406
unsigned int TPC() const noexcept
Definition: PmaHit3D.h:88
unsigned int fMinSegStop
Definition: PmaTrack3D.h:501
float fHitsRadius
Definition: PmaTrack3D.h:507
size_t size() const
Definition: PmaTrack3D.h:108
pma::Track3D * Parent(void) const
Definition: PmaSegment3D.h:68
size_t NHits(void) const
Definition: PmaElement3D.h:74
bool ShiftEndsToHits()
virtual pma::SortedObjectBase * Next(unsigned int index=0) const
Definition: SortedObjects.h:45
double mean(sqlite3 *db, std::string const &table_name, std::string const &column_name)
Definition: statistics.cc:16
bool AttachBackTo(pma::Node3D *vStart)
unsigned int fMaxSegStop
Definition: PmaTrack3D.h:502
void SetProjection(pma::Hit3D &h) const override
Set hit 3D position and its 2D projection to the vertex.
pma::Element3D * GetNearestElement(const TVector2 &p2d, unsigned int view, int tpc=-1, bool skipFrontVtx=false, bool skipBackVtx=false) const
static QCString * s
Definition: config.cpp:1042
pma::Segment3D * NextSegment(pma::Node3D *vtx) const
double Length(void) const
Definition: PmaElement3D.h:52
size_t NEnabledHits(unsigned int view=geo::kUnknown) const
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
std::pair< TVector2, TVector2 > WireDriftRange(detinfo::DetectorPropertiesData const &detProp, unsigned int view, unsigned int tpc, unsigned int cryo) const
Definition: PmaTrack3D.cxx:490
QTextStream & endl(QTextStream &s)
std::vector< pma::Hit3D * > fHits
Definition: PmaTrack3D.h:482
void UpdateProjection()