Utils.cxx
Go to the documentation of this file.
2 
3 #include "larsim/MCCheater/ParticleInventoryService.h" // for printing
4 #include <boost/algorithm/string/classification.hpp> // Include boost::for is_any_of
5 #include <boost/algorithm/string/split.hpp> // Include for boost::split
6 
8 
15 #include "lardataobj/RecoBase/Hit.h" // for Hit
22 #include "larreco/RecoAlg/TCAlg/TCVertex.h" // for tcc
24 
25 #include <algorithm>
26 #include <array>
27 #include <bitset>
28 #include <fstream>
29 #include <iomanip>
30 #include <iostream>
31 #include <limits.h>
32 #include <math.h>
33 #include <stdlib.h>
34 #include <string>
35 #include <utility>
36 #include <vector>
37 
38 namespace {
39  struct SortEntry {
40  unsigned int index;
41  float val;
42  };
43 
44  bool
46  {
47  return (c1.val > c2.val);
48  }
49 }
50 
51 namespace tca {
52 
53  // dressed muons
54  void
55  MakeHaloTj(TCSlice& slc, Trajectory& muTj, bool prt)
56  {
57  // Creates a "halo trajectory" around a muon tj consisting of hits and trajectories
58  // that are within MuonTag[4] distance. The halo tj is a virtual clone of muTj in the
59  // sense that it has the same number of points and the same start and end points.
60 
61  if (tcc.muonTag.size() < 5) return;
62  if (tcc.muonTag[4] <= 0) return;
63  if (!tcc.useAlg[kHaloTj]) return;
64 
65  if (muTj.PDGCode != 13) return;
66 
67  // check for daughter delta-rays
68  std::vector<int> dtrs;
69  for (auto& dtj : slc.tjs) {
70  if (dtj.AlgMod[kKilled]) continue;
71  if (dtj.ParentID != muTj.ID) continue;
72  dtrs.push_back(dtj.ID);
73  if (!dtj.AlgMod[kDeltaRay]) continue;
74  if (prt) mf::LogVerbatim("TC") << "MakeHaloTj: Killing delta-ray T" << dtj.ID;
75  // Kill a delta-ray PFParticle?
76  if (dtj.AlgMod[kMat3D]) {
77  unsigned short pfpIndex = GetPFPIndex(slc, dtj.ID);
78  if (pfpIndex == USHRT_MAX) {
79  if (prt) mf::LogVerbatim("TC") << " No PFP found for 3D-matched delta-ray";
80  }
81  else {
82  auto& pfp = slc.pfps[pfpIndex];
83  if (prt) mf::LogVerbatim("TC") << " Killing delta-ray PFParticle P" << pfp.UID;
84  pfp.ID = 0;
85  // correct the parent -> daughter assn
86  if (pfp.ParentUID > 0) {
87  auto parentIndx = GetSliceIndex("P", pfp.ParentUID);
88  if (parentIndx.first != USHRT_MAX) {
89  auto& parent = slices[parentIndx.first].pfps[parentIndx.second];
90  std::vector<int> newDtrUIDs;
91  for (auto uid : parent.DtrUIDs)
92  if (uid != dtj.UID) newDtrUIDs.push_back(uid);
93  parent.DtrUIDs = newDtrUIDs;
94  } // parent found
95  } // correct the parent
96  } // kill PFParticle
97  } // kill
98  MakeTrajectoryObsolete(slc, (unsigned int)(dtj.ID - 1));
99  } // dtj
100 
101  // make a copy
102  Trajectory tj;
103  tj.CTP = muTj.CTP;
104  // We can't use StoreTraj so variables need to be defined here
105  tj.ID = slc.tjs.size() + 1;
106  tj.WorkID = muTj.WorkID;
107  // increment the global ID
108  ++evt.globalT_UID;
109  tj.UID = evt.globalT_UID;
110  tj.PDGCode = 11;
111  tj.Pass = muTj.Pass;
112  tj.StepDir = muTj.StepDir;
113  tj.StartEnd = muTj.StartEnd;
114  tj.TotChg = 0;
115  tj.ChgRMS = 0;
116  tj.EndPt[0] = 0;
117  tj.ParentID = muTj.ID;
118  tj.AlgMod.reset();
119  tj.AlgMod[kHaloTj] = true;
120  // start a list of tjs that have points near the muon
121  std::vector<int> closeTjs;
122  for (unsigned short ipt = muTj.EndPt[0]; ipt <= muTj.EndPt[1]; ++ipt) {
123  auto tp = muTj.Pts[ipt];
124  tp.Hits.resize(0);
125  tp.UseHit.reset();
126  tp.Chg = 0;
127  tp.AveChg = 0;
128  tp.ChgPull = 0;
129  tp.Delta = 0;
130  tp.DeltaRMS = 0;
131  tp.FitChi = 0;
132  tp.NTPsFit = 0;
133  float window = tcc.muonTag[4];
134  if (tp.Dir[0] != 0) window *= std::abs(1 / tp.Dir[0]);
135  if (!FindCloseHits(slc, tp, window, kAllHits)) continue;
136  // add unused hits to the point and look for close tjs
137  bool hitsAdded = false;
138  for (unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
139  unsigned int iht = tp.Hits[ii];
140  auto inTraj = slc.slHits[iht].InTraj;
141  if (inTraj < 0) continue;
142  if (inTraj == 0) {
143  tp.UseHit[ii] = true;
144  slc.slHits[iht].InTraj = tj.ID;
145  hitsAdded = true;
146  }
147  else {
148  // add to the closeTjs list
149  if (inTraj != muTj.ID &&
150  std::find(closeTjs.begin(), closeTjs.end(), inTraj) == closeTjs.end())
151  closeTjs.push_back(inTraj);
152  }
153  } // ii
154  if (hitsAdded) {
155  DefineHitPos(slc, tp);
156  tp.Delta = PointTrajDOCA(slc, tp.HitPos[0], tp.HitPos[1], tp);
157  tj.TotChg += tp.Chg;
158  tj.Pts.push_back(tp);
159  } // hitsAdded
160  } // ipt
161  if (tj.Pts.empty()) return;
162  tj.EndPt[1] = tj.Pts.size() - 1;
163  if (prt) {
164  mf::LogVerbatim myprt("TC");
165  myprt << "MHTj: T" << muTj.ID << " npts " << tj.Pts.size() << " close";
166  for (auto tid : closeTjs)
167  myprt << " T" << tid;
168  myprt << "\n";
169  PrintTrajectory("DM", slc, tj, USHRT_MAX);
170  }
171  slc.tjs.push_back(tj);
172  } // MakeHaloTj
173 
174  /////////////////////////////////////////
175  void
176  DefineTjParents(TCSlice& slc, bool prt)
177  {
178  /*
179  This function sets the ParentUID of Tjs in this tpcid to create a hierarchy. The highest Score
180  3D vertex in a chain of Tjs and vertices is declared the primary vertex; vx3.Primary = true. Tjs directly attached
181  to that vertex are declared Primary trajectories with ParentUID = 0. All other Tjs in the chain have ParentUID
182  set to the next upstream Tj to which it is attached by a vertex. In the graphical description below, V1 and V4 are
183  2D vertices that are matched to a high-score 3D vertex. The V1 Score is greater than the V2 Score and V3 Score.
184  V1 and V4 are declared to be primary vertices. T1, T2, T6 and T7 are declared to be primary Tjs
185 
186  V1 - T1 - V2 - T3 V4 - T6 / T8
187  \ \ /
188  T2 - V3 - T4 T7
189  \
190  T5
191 
192  This is represented as follows. The NeutrinoPrimaryTjID is defined by a function.
193  Tj ParentUID NeutrinoPrimaryTjID
194  -----------------------------------
195  T1 0 T1
196  T2 0 T2
197  T3 T1 T2
198  T4 T2 T2
199  T5 T2 T2
200  T6 0 -1
201  T7 0 -1
202  T8 -1 -1
203 */
204 
205  // don't do anything if this is test beam data
206  if (tcc.modes[kTestBeam]) return;
207 
208  // clear old information
209  for (auto& tj : slc.tjs) {
210  if (tj.AlgMod[kKilled]) continue;
211  // ignore delta rays
212  if (tj.AlgMod[kDeltaRay] || tj.AlgMod[kHaloTj]) continue;
213  tj.ParentID = 0;
214  } // tj
215 
216  // sort vertice by decreasing score
217  std::vector<int> temp;
218  for (auto& vx3 : slc.vtx3s) {
219  if (vx3.ID == 0) continue;
220  // clear the Primary flag while we are here
221  vx3.Primary = false;
222  temp.push_back(vx3.ID);
223  } // vx3
224  if (temp.empty()) return;
225 
226  // Make a master list of all Tjs that are attached to these vertices
227  std::vector<int> masterlist;
228  for (auto vx3id : temp) {
229  auto& vx3 = slc.vtx3s[vx3id - 1];
230  float score;
231  auto tjlist = GetVtxTjIDs(slc, vx3, score);
232  for (auto tjid : tjlist) {
233  auto& tj = slc.tjs[tjid - 1];
234  if (tj.ParentID != 0) tj.ParentID = 0;
235  if (std::find(masterlist.begin(), masterlist.end(), tjid) == masterlist.end())
236  masterlist.push_back(tjid);
237  } // tjid
238  } // vxid
239  if (prt) {
240  mf::LogVerbatim myprt("TC");
241  myprt << "DTP: masterlist Tjs";
242  for (auto tjid : masterlist)
243  myprt << " " << tjid;
244  }
245 
246  // Do the sort
247  std::vector<SortEntry> sortVec(temp.size());
248  for (unsigned short indx = 0; indx < temp.size(); ++indx) {
249  auto& vx3 = slc.vtx3s[temp[indx] - 1];
250  sortVec[indx].index = indx;
251  sortVec[indx].val = vx3.Score;
252  } // indx
253  if (sortVec.size() > 1) std::sort(sortVec.begin(), sortVec.end(), valDecreasing);
254  // put them into order
255  auto vlist = temp;
256  for (unsigned short indx = 0; indx < temp.size(); ++indx)
257  vlist[indx] = temp[sortVec[indx].index];
258 
259  // make a neutrino PFParticle to associate with the highest score vertex if it is high enough
260  if (tcc.match3DCuts[0] > 0) {
261  auto& vx3 = slc.vtx3s[vlist[0] - 1];
262  if (vx3.Score > tcc.vtx2DCuts[7]) {
263  auto neutrinoPFP = CreatePFP(slc);
264  // call it the neutrino vertex
265  vx3.Neutrino = true;
266  // put the vertex at the end of the neutrino
267  auto& sf = neutrinoPFP.SectionFits[0];
268  sf.Pos[0] = vx3.X;
269  sf.Pos[1] = vx3.Y;
270  sf.Pos[2] = vx3.Z;
271  sf.Dir[2] = 1;
272  // This may be set to 12 later on if a primary shower is reconstructed
273  neutrinoPFP.PDGCode = 14;
274  neutrinoPFP.Vx3ID[1] = vx3.ID;
275  neutrinoPFP.Vx3ID[0] = vx3.ID;
276  neutrinoPFP.Flags[kNeedsUpdate] = false;
277  // the rest of this will be defined later
278  if (!StorePFP(slc, neutrinoPFP)) return;
279  }
280  } // User wants to make PFParticles
281  // a temp vector to ensure that we only consider a vertex once
282  std::vector<bool> lookedAt3(slc.vtx3s.size() + 1, false);
283  std::vector<bool> lookedAt2(slc.vtxs.size() + 1, false);
284  // vector of parent-daughter pairs
285  std::vector<std::pair<int, int>> pardtr;
286  // Start with the highest score vertex
287  for (unsigned short indx = 0; indx < vlist.size(); ++indx) {
288  auto& vx3 = slc.vtx3s[vlist[indx] - 1];
289  if (lookedAt3[vx3.ID]) continue;
290  vx3.Primary = true;
291  lookedAt3[vx3.ID] = true;
292  // make a list of Tjs attached to this vertex
293  float score;
294  auto primTjList = GetVtxTjIDs(slc, vx3, score);
295  if (primTjList.empty()) continue;
296  pardtr.clear();
297  for (auto primTjID : primTjList) {
298  auto& primTj = slc.tjs[primTjID - 1];
299  // This isn't a primary tj if the parent ID isn't -1
300  if (primTj.ParentID != -1) continue;
301  if (prt) mf::LogVerbatim("TC") << "Vx3 " << vx3.ID << " Primary tj " << primTj.ID;
302  // declare this a primary tj
303  primTj.ParentID = 0;
304  // look for daughter tjs = those that are attached to a 2D vertex
305  // at the other end
306  for (unsigned short end = 0; end < 2; ++end) {
307  if (primTj.VtxID[end] == 0) continue;
308  auto& vx2 = slc.vtxs[primTj.VtxID[end] - 1];
309  if (vx2.Vx3ID == vx3.ID) continue;
310  // found a 2D vertex. Check for daughters
311  auto dtrList = GetVtxTjIDs(slc, vx2);
312  for (auto dtrID : dtrList) {
313  // ignore the primary tj
314  if (dtrID == primTjID) continue;
315  auto& dtj = slc.tjs[dtrID - 1];
316  if (dtj.ParentID != -1) continue;
317  pardtr.push_back(std::make_pair(primTjID, dtrID));
318  if (prt) mf::LogVerbatim("TC") << " primTj " << primTjID << " dtrID " << dtrID;
319  } // tjid
320  } // end
321  // Ensure that end 0 of the trajectory is attached to the primary vertex
322  for (unsigned short end = 0; end < 2; ++end) {
323  if (primTj.VtxID[end] == 0) continue;
324  auto& vx2 = slc.vtxs[primTj.VtxID[end] - 1];
325  if (vx2.Vx3ID == vx3.ID && end != 0) ReverseTraj(slc, primTj);
326  } // end
327  } // tjid
328  if (pardtr.empty()) continue;
329  if (prt) {
330  mf::LogVerbatim myprt("TC");
331  myprt << " par_dtr";
332  for (auto pdtr : pardtr)
333  myprt << " " << pdtr.first << "_" << pdtr.second;
334  }
335  // iterate through the parent - daughter stack, removing the last pair when a
336  // ParentID is updated and adding pairs for new daughters
337  for (unsigned short nit = 0; nit < 100; ++nit) {
338  auto lastPair = pardtr[pardtr.size() - 1];
339  auto& dtj = slc.tjs[lastPair.second - 1];
340  dtj.ParentID = lastPair.first;
341  // reverse the daughter trajectory if necessary so that end 0 is closest to the parent
342  float doca = 100;
343  unsigned short dpt = 0, ppt = 0;
344  auto& ptj = slc.tjs[lastPair.first - 1];
345  // find the point on the daughter tj that is closest to the parent
346  TrajTrajDOCA(slc, dtj, ptj, dpt, ppt, doca);
347  // reverse the daughter if the closest point is near end 1 of the daughter
348  if (prt) mf::LogVerbatim("TC") << "Set parent " << ptj.ID << " dtr " << dtj.ID;
349  // remove that entry
350  pardtr.pop_back();
351  // Add entries for new daughters
352  for (unsigned short end = 0; end < 2; ++end) {
353  if (dtj.VtxID[end] == 0) continue;
354  auto& vx2 = slc.vtxs[dtj.VtxID[end] - 1];
355  if (lookedAt2[vx2.ID]) continue;
356  lookedAt2[vx2.ID] = true;
357  auto tjlist = GetVtxTjIDs(slc, vx2);
358  for (auto tjid : tjlist) {
359  if (tjid == dtj.ID || tjid == ptj.ID) continue;
360  pardtr.push_back(std::make_pair(dtj.ID, tjid));
361  if (prt) {
362  mf::LogVerbatim myprt("TC");
363  myprt << " add par_dtr";
364  for (auto pdtr : pardtr)
365  myprt << " " << pdtr.first << "_" << pdtr.second;
366  }
367  }
368  } // end
369  if (pardtr.empty()) break;
370  } // nit
371  } // indx
372  // check the master list
373  for (auto tjid : masterlist) {
374  auto& tj = slc.tjs[tjid - 1];
375  if (tj.ParentID < 0) tj.ParentID = tj.ID;
376  } // tjid
377 
378  } // DefineTjParents
379 
380  /////////////////////////////////////////
381  float
382  MaxChargeAsymmetry(TCSlice& slc, std::vector<int>& tjIDs)
383  {
384  // calculates the maximum charge asymmetry in all planes using the supplied list of Tjs
385  if (tjIDs.size() < 2) return 1;
386  std::vector<float> plnchg(slc.nPlanes);
387  for (auto tjid : tjIDs) {
388  if (tjid <= 0 || tjid > (int)slc.tjs.size()) return 1;
389  auto& tj = slc.tjs[tjid - 1];
390  if (tj.TotChg == 0) UpdateTjChgProperties("MCA", slc, tj, false);
391  unsigned short plane = DecodeCTP(tj.CTP).Plane;
392  plnchg[plane] += tj.TotChg;
393  } // tjid
394  float aveChg = 0;
395  float cnt = 0;
396  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
397  if (plnchg[plane] == 0) continue;
398  aveChg += plnchg[plane];
399  ++cnt;
400  } // plane
401  if (cnt < 2) return 1;
402  aveChg /= cnt;
403  float maxAsym = 0;
404  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
405  // ignore zeros
406  if (plnchg[plane] == 0) continue;
407  float asym = std::abs(plnchg[plane] - aveChg) / (plnchg[plane] + aveChg);
408  if (asym > maxAsym) maxAsym = asym;
409  } // plane
410  return maxAsym;
411  } // MaxChargeAsymmetry
412 
413  /////////////////////////////////////////
414  int
415  PDGCodeVote(const TCSlice& slc, const std::vector<int>& tjIDs)
416  {
417  // Returns the most likely PDGCode for the set of Tjs provided
418  // The PDG codes are:
419  // 0 = your basic track-like trajectory
420  // 11 = Tagged delta-ray
421  // 13 = Tagged muon
422  // 211 = pion-like. There exists a Bragg peak at an end with a vertex
423  // 2212 = proton-like. There exists a Bragg peak at an end without a vertex
424  std::array<int, 5> codeList = {{0, 11, 13, 111, 211}};
425  unsigned short codeIndex = 0;
426  if (tjIDs.empty()) return codeList[codeIndex];
427 
428  std::array<unsigned short, 5> cnts;
429  cnts.fill(0);
430  float maxLen = 0;
431  for (auto tjid : tjIDs) {
432  if (tjid <= 0 || tjid > (int)slc.tjs.size()) continue;
433  auto& tj = slc.tjs[tjid - 1];
434  for (unsigned short ii = 0; ii < 5; ++ii)
435  if (tj.PDGCode == codeList[ii]) ++cnts[ii];
436  float len = TrajLength(tj);
437  if (len > maxLen) maxLen = len;
438  } // tjid
439  unsigned maxCnt = 0;
440  // ignore the first PDG code in the list (the default)
441  for (unsigned short ii = 1; ii < 5; ++ii) {
442  if (cnts[ii] > maxCnt) {
443  maxCnt = cnts[ii];
444  codeIndex = ii;
445  }
446  } // ii
447  return codeList[codeIndex];
448  } // PDGCodeVote
449 
450  /////////////////////////////////////////
451  int
452  NeutrinoPrimaryTjID(const TCSlice& slc, const Trajectory& tj)
453  {
454  // Returns the ID of the grandparent of this tj that is a primary tj that is attached
455  // to the neutrino vertex. 0 is returned if this condition is not met.
456  if (tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) return -1;
457  if (tj.ParentID <= 0) return -1;
458  int primID = PrimaryID(slc, tj);
459  if (primID <= 0 || primID > (int)slc.tjs.size()) return -1;
460 
461  // We have the ID of the primary tj. Now see if it is attached to the neutrino vertex
462  auto& ptj = slc.tjs[primID - 1];
463  for (unsigned short end = 0; end < 2; ++end) {
464  if (ptj.VtxID[end] == 0) continue;
465  auto& vx2 = slc.vtxs[ptj.VtxID[end] - 1];
466  if (vx2.Vx3ID == 0) continue;
467  auto& vx3 = slc.vtx3s[vx2.Vx3ID - 1];
468  if (vx3.Neutrino) return primID;
469  } // end
470  return -1;
471  } // NeutrinoPrimaryTjUID
472 
473  /////////////////////////////////////////
474  int
475  PrimaryID(const TCSlice& slc, const Trajectory& tj)
476  {
477  // Returns the ID of the grandparent trajectory of this trajectory that is a primary
478  // trajectory (i.e. whose ParentID = 0).
479  if (tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) return -1;
480  if (tj.ParentID < 0 || tj.ParentID > (int)slc.tjs.size()) return -1;
481  if (tj.ParentID == 0) return tj.ID;
482  int parid = tj.ParentID;
483  for (unsigned short nit = 0; nit < 10; ++nit) {
484  if (parid < 1 || parid > (int)slc.tjs.size()) break;
485  auto& tj = slc.tjs[parid - 1];
486  if (tj.ParentID < 0 || tj.ParentID > (int)slc.tjs.size()) return -1;
487  if (tj.ParentID == 0) return tj.ID;
488  parid = tj.ParentID;
489  } // nit
490  return -1;
491  } // PrimaryID
492 
493  /////////////////////////////////////////
494  int
495  PrimaryUID(const TCSlice& slc, const PFPStruct& pfp)
496  {
497  // returns the UID of the most upstream PFParticle (that is not a neutrino)
498 
499  if (int(pfp.ParentUID) == pfp.UID || pfp.ParentUID <= 0) return pfp.ID;
500  int paruid = pfp.ParentUID;
501  int dtruid = pfp.UID;
502  unsigned short nit = 0;
503  while (true) {
504  auto slcIndx = GetSliceIndex("P", paruid);
505  auto& parent = slices[slcIndx.first].pfps[slcIndx.second];
506  // found a neutrino
507  if (parent.PDGCode == 14 || parent.PDGCode == 12) return dtruid;
508  // found a primary PFParticle?
509  if (parent.ParentUID == 0) return parent.UID;
510  if (int(parent.ParentUID) == parent.UID) return parent.UID;
511  dtruid = parent.UID;
512  paruid = parent.ParentUID;
513  if (paruid < 0) return 0;
514  ++nit;
515  if (nit == 10) return 0;
516  }
517  } // PrimaryUID
518 
519  /////////////////////////////////////////
520  bool
521  MergeTjIntoPFP(TCSlice& slc, int mtjid, PFPStruct& pfp, bool prt)
522  {
523  // Tries to merge Tj with ID tjid into PFParticle pfp
524  if (mtjid > (int)slc.tjs.size()) return false;
525  auto& mtj = slc.tjs[mtjid - 1];
526  // find the Tj in pfp.TjIDs which it should be merged with
527  int otjid = 0;
528  for (auto tjid : pfp.TjIDs) {
529  auto& otj = slc.tjs[tjid - 1];
530  if (otj.CTP == mtj.CTP) {
531  otjid = tjid;
532  break;
533  }
534  } // tjid
535  if (otjid == 0) return false;
536  if (MergeAndStore(slc, otjid - 1, mtjid - 1, prt)) {
537  int newtjid = slc.tjs.size();
538  if (prt)
539  mf::LogVerbatim("TC") << "MergeTjIntoPFP: merged T" << otjid << " with T" << mtjid
540  << " -> T" << newtjid;
541  std::replace(pfp.TjIDs.begin(), pfp.TjIDs.begin(), otjid, newtjid);
542  return true;
543  }
544  else {
545  if (prt)
546  mf::LogVerbatim("TC") << "MergeTjIntoPFP: merge T" << otjid << " with T" << mtjid
547  << " failed ";
548  return false;
549  }
550  } // MergeTjIntoPFP
551 
552  /////////////////////////////////////////
553  float
554  PointPull(TCSlice& slc, Point2_t pos, float chg, const Trajectory& tj)
555  {
556  // returns the combined position and charge pull for the charge at pos
557  // relative to the Tj closest to that point using a loose requirement on position separation.
558  if (tj.AlgMod[kKilled]) return 100;
559  if (tj.AveChg <= 0) return 100;
560  // find the closest point on the tj to pos
561  unsigned short closePt = USHRT_MAX;
562  float close = 1000;
563  for (unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
564  auto& tp = tj.Pts[ipt];
565  float sep2 = PosSep2(pos, tp.Pos);
566  if (sep2 > close) continue;
567  close = sep2;
568  closePt = ipt;
569  } // ipt
570  if (closePt == USHRT_MAX) return 100;
571  // find the delta between the projection of the Tj close TP to inTP
572  auto& tp = tj.Pts[closePt];
573  float delta = PointTrajDOCA(slc, pos[0], pos[1], tp);
574  // estimate the proejcted position error (roughly)
575  float posErr = tp.DeltaRMS;
576  if (tp.AngErr > 0 && close > 10) posErr += sqrt(tp.AngErr * sqrt(close));
577  if (posErr < 0.1) posErr = 0.1;
578  float posPull = delta / posErr;
579  float chgErr = tj.ChgRMS;
580  if (chgErr < 0.15) chgErr = 0.15;
581  float chgPull = std::abs(chg / tj.AveChg - 1) / chgErr;
582  // return a simple average
583  return 0.5 * (posPull + chgPull);
584  } // PointPull
585 
586  /////////////////////////////////////////
587  bool
588  CompatibleMerge(const TCSlice& slc, std::vector<int>& tjIDs, bool prt)
589  {
590  // Returns true if the last Tj in tjIDs has a topology consistent with it being
591  // merged with other Tjs in the same plane in the list. This is done by requiring that
592  // the closest TP between the last Tj and any other Tj is EndPt[0] or EndPt[1]. This is
593  // shown graphically here where the numbers represent the ID of a Tj that has a TP on a wire.
594  // Assume that TjIDs = {1, 2, 3, 4, 7} where T1 and T3 are in plane 0, T2 is in plane 1 and
595  // T4 is in plane 2. T7, in plane 0, was added to TjIDs with the intent of merging it with
596  // T1 and T3 into a single trajectory. This is a compatible merge if Tj7 has the following
597  // topology:
598  // 111111 333333 7777777
599  // This is an incompatible topology
600  // 111111 333333
601  // 7777777777
602  if (tjIDs.size() < 2) return false;
603  unsigned short lasttj = tjIDs[tjIDs.size() - 1] - 1;
604  auto& mtj = slc.tjs[lasttj];
605  bool mtjIsShort = (mtj.Pts.size() < 5);
606  // minimum separation from each end of mtj
607  std::array<float, 2> minsep2{{1000, 1000}};
608  // ID of the Tj with the minimum separation
609  std::array<int, 2> minsepTj{{0, 0}};
610  // and the index of the point on that Tj
611  std::array<unsigned short, 2> minsepPt;
612  // determine the end of the closest Tj point. Start by assuming
613  // the closest Tj point is not near an end (end = 0);
614  std::array<unsigned short, 2> minsepEnd;
615  for (auto tjid : tjIDs) {
616  auto& tj = slc.tjs[tjid - 1];
617  if (tj.CTP != mtj.CTP) continue;
618  if (tj.ID == mtj.ID) continue;
619  for (unsigned short mend = 0; mend < 2; ++mend) {
620  Point2_t mendPos = mtj.Pts[mtj.EndPt[mend]].Pos;
621  float sep2 = minsep2[mend];
622  unsigned short closePt = 0;
623  if (!TrajClosestApproach(tj, mendPos[0], mendPos[1], closePt, sep2)) continue;
624  minsep2[mend] = sep2;
625  minsepTj[mend] = tjid;
626  minsepPt[mend] = closePt;
627  // set the end to a bogus value (not near an end)
628  minsepEnd[mend] = 2;
629  short dend0 = abs((short)closePt - tj.EndPt[0]);
630  short dend1 = abs((short)closePt - tj.EndPt[1]);
631  if (dend0 < dend1 && dend0 < 3) minsepEnd[mend] = 0;
632  if (dend1 < dend0 && dend1 < 3) minsepEnd[mend] = 1;
633  } // mend
634  } // tjid
635  // don't require that the minsepTjs be the same. This would reject this topology
636  // 111111 333333 7777777
637  // if mtj.ID = 3
638  bool isCompatible = (minsepEnd[0] != 2 && minsepEnd[1] != 2);
639  // check for large separation between the closest points for short Tjs
640  if (isCompatible && mtjIsShort) {
641  float minminsep = minsep2[0];
642  if (minsep2[1] < minminsep) minminsep = minsep2[1];
643  // require that the separation be less than sqrt(5)
644  isCompatible = minminsep < 5;
645  }
646  if (prt) {
647  mf::LogVerbatim myprt("TC");
648  myprt << "CompatibleMerge: T" << mtj.ID << " end";
649  for (unsigned short end = 0; end < 2; ++end)
650  myprt << " T" << minsepTj[end] << "_I" << minsepPt[end] << "_E" << minsepEnd[end]
651  << " minsep " << sqrt(minsep2[end]);
652  myprt << " Compatible? " << isCompatible;
653  } // prt
654  return isCompatible;
655 
656  } // CompatibleMerge
657 
658  /////////////////////////////////////////
659  bool
660  CompatibleMerge(const TCSlice& slc, const Trajectory& tj1, const Trajectory& tj2, bool prt)
661  {
662  // returns true if the two Tjs are compatible with and end0-end1 merge. This function has many aspects of the
663  // compatibility checks done in EndMerge but with looser cuts.
664  if (tj1.AlgMod[kKilled] || tj2.AlgMod[kKilled]) return false;
665  if (tj1.AlgMod[kHaloTj] || tj2.AlgMod[kHaloTj]) return false;
666  if (tj1.CTP != tj2.CTP) return false;
667  unsigned short end1 = -1, end2 = 0;
668  float minLen = PosSep(tj1.Pts[tj1.EndPt[0]].Pos, tj1.Pts[tj1.EndPt[1]].Pos);
669  float len2 = PosSep(tj2.Pts[tj2.EndPt[0]].Pos, tj2.Pts[tj2.EndPt[1]].Pos);
670  if (len2 < minLen) minLen = len2;
671  minLen *= 1.2;
672  if (minLen > 10) minLen = 10;
673  for (unsigned short e1 = 0; e1 < 2; ++e1) {
674  auto& tp1 = tj1.Pts[tj1.EndPt[e1]];
675  for (unsigned short e2 = 0; e2 < 2; ++e2) {
676  auto& tp2 = tj2.Pts[tj2.EndPt[e2]];
677  float sep = PosSep(tp1.Pos, tp2.Pos);
678  if (sep < minLen) {
679  minLen = sep;
680  end1 = e1;
681  end2 = e2;
682  }
683  } // e2
684  } // e1
685  if (end1 < 0) return false;
686  // require end to end
687  if (end2 != 1 - end1) return false;
688 
689  float overlapFraction = OverlapFraction(slc, tj1, tj2);
690  if (overlapFraction > 0.25) {
691  if (prt)
692  mf::LogVerbatim("TC") << "CM: " << tj1.ID << " " << tj2.ID << " overlapFraction "
693  << overlapFraction << " > 0.25 ";
694  return false;
695  }
696 
697  auto& tp1 = tj1.Pts[tj1.EndPt[end1]];
698  auto& tp2 = tj2.Pts[tj2.EndPt[end2]];
699  float doca1 = PointTrajDOCA(slc, tp1.Pos[0], tp1.Pos[1], tp2);
700  float doca2 = PointTrajDOCA(slc, tp2.Pos[0], tp2.Pos[1], tp1);
701  if (doca1 > 2 && doca2 > 2) {
702  if (prt)
703  mf::LogVerbatim("TC") << "CM: " << tj1.ID << " " << tj2.ID << " Both docas > 2 " << doca1
704  << " " << doca2;
705  return false;
706  }
707 
708  float dang = DeltaAngle(tp1.Ang, tp2.Ang);
709  if (dang > 2 * tcc.kinkCuts[0]) {
710  if (prt)
711  mf::LogVerbatim("TC") << "CM: " << tj1.ID << " " << tj2.ID << " dang " << dang << " > "
712  << 2 * tcc.kinkCuts[0];
713  return false;
714  }
715 
716  return true;
717  } // CompatibleMerge
718 
719  /////////////////////////////////////////
720  float
721  OverlapFraction(const TCSlice& slc, const Trajectory& tj1, const Trajectory& tj2)
722  {
723  // returns the fraction of wires spanned by two trajectories
724  float minWire = 1E6;
725  float maxWire = -1E6;
726 
727  float cnt1 = 0;
728  for (auto& tp : tj1.Pts) {
729  if (tp.Chg == 0) continue;
730  if (tp.Pos[0] < 0) continue;
731  if (tp.Pos[0] < minWire) minWire = tp.Pos[0];
732  if (tp.Pos[0] > maxWire) maxWire = tp.Pos[0];
733  ++cnt1;
734  }
735  if (cnt1 == 0) return 0;
736  float cnt2 = 0;
737  for (auto& tp : tj2.Pts) {
738  if (tp.Chg == 0) continue;
739  if (tp.Pos[0] < 0) continue;
740  if (tp.Pos[0] < minWire) minWire = tp.Pos[0];
741  if (tp.Pos[0] > maxWire) maxWire = tp.Pos[0];
742  ++cnt2;
743  }
744  if (cnt2 == 0) return 0;
745  int span = maxWire - minWire;
746  if (span <= 0) return 0;
747  std::vector<unsigned short> wcnt(span);
748  for (auto& tp : tj1.Pts) {
749  if (tp.Chg == 0) continue;
750  if (tp.Pos[0] < -0.4) continue;
751  int indx = std::nearbyint(tp.Pos[0] - minWire);
752  if (indx < 0 || indx > span - 1) continue;
753  ++wcnt[indx];
754  }
755  for (auto& tp : tj2.Pts) {
756  if (tp.Chg == 0) continue;
757  if (tp.Pos[0] < -0.4) continue;
758  int indx = std::nearbyint(tp.Pos[0] - minWire);
759  if (indx < 0 || indx > span - 1) continue;
760  ++wcnt[indx];
761  }
762  float cntOverlap = 0;
763  for (auto cnt : wcnt)
764  if (cnt > 1) ++cntOverlap;
765  if (cnt1 < cnt2) { return cntOverlap / cnt1; }
766  else {
767  return cntOverlap / cnt2;
768  }
769 
770  } // OverlapFraction
771 
772  /////////////////////////////////////////
773  unsigned short
775  {
776  return AngleRange(tp.Ang);
777  }
778 
779  /////////////////////////////////////////
780  void
782  {
783  unsigned short ar = AngleRange(tp.Ang);
784  if (ar == tcc.angleRanges.size() - 1) {
785  // Very large angle
786  tp.AngleCode = 2;
787  }
788  else if (tcc.angleRanges.size() > 2 && ar == tcc.angleRanges.size() - 2) {
789  // Large angle
790  tp.AngleCode = 1;
791  }
792  else {
793  // Small angle
794  tp.AngleCode = 0;
795  }
796 
797  } // SetAngleCode
798 
799  /////////////////////////////////////////
800  unsigned short
801  AngleRange(float angle)
802  {
803  // returns the index of the angle range
804  if (angle > M_PI) angle = M_PI;
805  if (angle < -M_PI) angle = M_PI;
806  if (angle < 0) angle = -angle;
807  if (angle > M_PI / 2) angle = M_PI - angle;
808  for (unsigned short ir = 0; ir < tcc.angleRanges.size(); ++ir) {
809  if (angle < tcc.angleRanges[ir]) return ir;
810  }
811  return tcc.angleRanges.size() - 1;
812  } // AngleRange
813 
814  //////////////////////////////////////////
815  void
817  {
818  // Jacket around FitTraj to fit the leading edge of the supplied trajectory
819  unsigned short originPt = tj.EndPt[1];
820  unsigned short npts = tj.Pts[originPt].NTPsFit;
821  TrajPoint tpFit;
822  unsigned short fitDir = -1;
823  FitTraj(slc, tj, originPt, npts, fitDir, tpFit);
824  tj.Pts[originPt] = tpFit;
825 
826  } // FitTraj
827 
828  //////////////////////////////////////////
829  void
831  Trajectory& tj,
832  unsigned short originPt,
833  unsigned short npts,
834  short fitDir,
835  TrajPoint& tpFit)
836  {
837  // Fit the supplied trajectory using HitPos positions with the origin at originPt.
838  // The npts is interpreted as the number of points on each side of the origin
839  // The allowed modes are as follows, where i denotes a TP that is included, . denotes
840  // a TP with no hits, and x denotes a TP that is not included
841  //TP 012345678 fitDir originPt npts
842  // Oiiixxxxx 1 0 4 << npts in the fit
843  // xi.iiOxxx -1 5 4
844  // xiiiOiiix 0 4 4 << 2 * npts + 1 points in the fit
845  // xxxiO.ixx 0 4 1
846  // 0iiixxxxx 0 0 4
847  // This routine puts the results into tp if the fit is successfull. The
848  // fit "direction" is in increasing order along the trajectory from 0 to tj.Pts.size() - 1.
849 
850  // static const float twoPi = 2 * M_PI;
851 
852  if (originPt > tj.Pts.size() - 1) {
853  mf::LogWarning("TC") << "FitTraj: Requesting fit of invalid TP " << originPt;
854  return;
855  }
856 
857  // copy the origin TP into the fit TP
858  tpFit = tj.Pts[originPt];
859  // Assume that the fit will fail
860  tpFit.FitChi = 999;
861  if (fitDir < -1 || fitDir > 1) return;
862 
863  std::vector<double> x, y;
864  Point2_t origin = tj.Pts[originPt].HitPos;
865  // Use TP position if there aren't any hits on it
866  if (tj.Pts[originPt].Chg == 0) origin = tj.Pts[originPt].Pos;
867 
868  // simple two point case
869  if (NumPtsWithCharge(slc, tj, false) == 2) {
870  for (unsigned short ipt = tj.EndPt[0]; ipt < tj.EndPt[1]; ++ipt) {
871  if (tj.Pts[ipt].Chg <= 0) continue;
872  double xx = tj.Pts[ipt].HitPos[0] - origin[0];
873  double yy = tj.Pts[ipt].HitPos[1] - origin[1];
874  x.push_back(xx);
875  y.push_back(yy);
876  } // ii
877  if (x.size() != 2) return;
878  if (x[0] == x[1]) {
879  // Either + or - pi/2
880  tpFit.Ang = M_PI / 2;
881  if (y[1] < y[0]) tpFit.Ang = -tpFit.Ang;
882  }
883  else {
884  double dx = x[1] - x[0];
885  double dy = y[1] - y[0];
886  tpFit.Ang = atan2(dy, dx);
887  }
888  tpFit.Dir[0] = cos(tpFit.Ang);
889  tpFit.Dir[1] = sin(tpFit.Ang);
890  tpFit.Pos[0] += origin[0];
891  tpFit.Pos[1] += origin[1];
892  tpFit.AngErr = 0.01;
893  tpFit.FitChi = 0.01;
894  SetAngleCode(tpFit);
895  return;
896  } // two points
897 
898  std::vector<double> w, q;
899  std::array<double, 2> dir;
900  double xx, yy, xr, yr;
901  double chgWt;
902 
903  // Rotate the traj hit position into the coordinate system defined by the
904  // originPt traj point, where x = along the trajectory, y = transverse
905  double rotAngle = tj.Pts[originPt].Ang;
906  double cs = cos(-rotAngle);
907  double sn = sin(-rotAngle);
908 
909  // enter the originPT hit info if it exists
910  if (tj.Pts[originPt].Chg > 0) {
911  xx = tj.Pts[originPt].HitPos[0] - origin[0];
912  yy = tj.Pts[originPt].HitPos[1] - origin[1];
913  xr = cs * xx - sn * yy;
914  yr = sn * xx + cs * yy;
915  x.push_back(xr);
916  y.push_back(yr);
917  chgWt = tj.Pts[originPt].ChgPull;
918  if (chgWt < 1) chgWt = 1;
919  chgWt *= chgWt;
920  w.push_back(chgWt * tj.Pts[originPt].HitPosErr2);
921  }
922 
923  // correct npts to account for the origin point
924  if (fitDir != 0) --npts;
925 
926  // step in the + direction first
927  if (fitDir != -1) {
928  unsigned short cnt = 0;
929  for (unsigned short ipt = originPt + 1; ipt < tj.Pts.size(); ++ipt) {
930  if (tj.Pts[ipt].Chg <= 0) continue;
931  xx = tj.Pts[ipt].HitPos[0] - origin[0];
932  yy = tj.Pts[ipt].HitPos[1] - origin[1];
933  xr = cs * xx - sn * yy;
934  yr = sn * xx + cs * yy;
935  x.push_back(xr);
936  y.push_back(yr);
937  chgWt = tj.Pts[ipt].ChgPull;
938  if (chgWt < 1) chgWt = 1;
939  chgWt *= chgWt;
940  w.push_back(chgWt * tj.Pts[ipt].HitPosErr2);
941  ++cnt;
942  if (cnt == npts) break;
943  } // ipt
944  } // fitDir != -1
945 
946  // step in the - direction next
947  if (fitDir != 1 && originPt > 0) {
948  unsigned short cnt = 0;
949  for (unsigned short ii = 1; ii < tj.Pts.size(); ++ii) {
950  unsigned short ipt = originPt - ii;
951  if (ipt > tj.Pts.size() - 1) continue;
952  if (tj.Pts[ipt].Chg == 0) continue;
953  xx = tj.Pts[ipt].HitPos[0] - origin[0];
954  yy = tj.Pts[ipt].HitPos[1] - origin[1];
955  xr = cs * xx - sn * yy;
956  yr = sn * xx + cs * yy;
957  x.push_back(xr);
958  y.push_back(yr);
959  chgWt = tj.Pts[ipt].ChgPull;
960  if (chgWt < 1) chgWt = 1;
961  chgWt *= chgWt;
962  w.push_back(chgWt * tj.Pts[ipt].HitPosErr2);
963  ++cnt;
964  if (cnt == npts) break;
965  if (ipt == 0) break;
966  } // ipt
967  } // fitDir != -1
968 
969  // Not enough points to define a line?
970  if (x.size() < 2) return;
971 
972  double sum = 0.;
973  double sumx = 0.;
974  double sumy = 0.;
975  double sumxy = 0.;
976  double sumx2 = 0.;
977  double sumy2 = 0.;
978 
979  // weight by the charge ratio and accumulate sums
980  double wght;
981  for (unsigned short ipt = 0; ipt < x.size(); ++ipt) {
982  if (w[ipt] < 0.00001) w[ipt] = 0.00001;
983  wght = 1 / w[ipt];
984  sum += wght;
985  sumx += wght * x[ipt];
986  sumy += wght * y[ipt];
987  sumx2 += wght * x[ipt] * x[ipt];
988  sumy2 += wght * y[ipt] * y[ipt];
989  sumxy += wght * x[ipt] * y[ipt];
990  }
991  // calculate coefficients and std dev
992  double delta = sum * sumx2 - sumx * sumx;
993  if (delta == 0) return;
994  // A is the intercept
995  double A = (sumx2 * sumy - sumx * sumxy) / delta;
996  // B is the slope
997  double B = (sumxy * sum - sumx * sumy) / delta;
998 
999  // The chisq will be set below if there are enough points. Don't allow it to be 0
1000  // so we can take Chisq ratios later
1001  tpFit.FitChi = 0.01;
1002  double newang = atan(B);
1003  dir[0] = cos(newang);
1004  dir[1] = sin(newang);
1005  // rotate back into the (w,t) coordinate system
1006  cs = cos(rotAngle);
1007  sn = sin(rotAngle);
1008  tpFit.Dir[0] = cs * dir[0] - sn * dir[1];
1009  tpFit.Dir[1] = sn * dir[0] + cs * dir[1];
1010  // ensure that the direction is consistent with the originPt direction
1011  bool flipDir = false;
1012  if (AngleRange(tj.Pts[originPt]) > 0) {
1013  flipDir = std::signbit(tpFit.Dir[1]) != std::signbit(tj.Pts[originPt].Dir[1]);
1014  }
1015  else {
1016  flipDir = std::signbit(tpFit.Dir[0]) != std::signbit(tj.Pts[originPt].Dir[0]);
1017  }
1018  if (flipDir) {
1019  tpFit.Dir[0] = -tpFit.Dir[0];
1020  tpFit.Dir[1] = -tpFit.Dir[1];
1021  }
1022  tpFit.Ang = atan2(tpFit.Dir[1], tpFit.Dir[0]);
1023  SetAngleCode(tpFit);
1024 
1025  // rotate (0, intcpt) into (W,T) coordinates
1026  tpFit.Pos[0] = -sn * A + origin[0];
1027  tpFit.Pos[1] = cs * A + origin[1];
1028  // force the origin to be at origin[0]
1029  if (tpFit.AngleCode < 2) MoveTPToWire(tpFit, origin[0]);
1030 
1031  if (x.size() < 3) return;
1032 
1033  // Calculate chisq/DOF
1034  double ndof = x.size() - 2;
1035  double varnce =
1036  (sumy2 + A * A * sum + B * B * sumx2 - 2 * (A * sumy + B * sumxy - A * B * sumx)) / ndof;
1037  if (varnce > 0.) {
1038  // Intercept error is not used
1039  // InterceptError = sqrt(varnce * sumx2 / delta);
1040  double slopeError = sqrt(varnce * sum / delta);
1041  tpFit.AngErr = std::abs(atan(slopeError));
1042  }
1043  else {
1044  tpFit.AngErr = 0.01;
1045  }
1046  sum = 0;
1047  // calculate chisq
1048  double arg;
1049  for (unsigned short ii = 0; ii < y.size(); ++ii) {
1050  arg = y[ii] - A - B * x[ii];
1051  sum += arg * arg / w[ii];
1052  }
1053  tpFit.FitChi = sum / ndof;
1054  } // FitTraj
1055 
1056  ////////////////////////////////////////////////
1057  unsigned short
1058  GetPFPIndex(const TCSlice& slc, int tjID)
1059  {
1060  if (slc.pfps.empty()) return USHRT_MAX;
1061  for (unsigned int ipfp = 0; ipfp < slc.pfps.size(); ++ipfp) {
1062  const auto& pfp = slc.pfps[ipfp];
1063  if (std::find(pfp.TjIDs.begin(), pfp.TjIDs.end(), tjID) != pfp.TjIDs.end()) return ipfp;
1064  } // indx
1065  return USHRT_MAX;
1066  } // GetPFPIndex
1067 
1068  ////////////////////////////////////////////////
1069  void
1071  {
1072  // Sets InTraj[] = 0 for all TPs in work. Called when abandoning work
1073  for (auto& tp : tj.Pts) {
1074  for (auto iht : tp.Hits) {
1075  if (slc.slHits[iht].InTraj == tj.ID) slc.slHits[iht].InTraj = 0;
1076  }
1077  } // tp
1078 
1079  } // ReleaseWorkHits
1080 
1081  //////////////////////////////////////////
1082  void
1084  {
1085  // Sets InTraj = 0 and UseHit false for all used hits in tp
1086  for (unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1087  if (tp.UseHit[ii]) {
1088  slc.slHits[tp.Hits[ii]].InTraj = 0;
1089  tp.UseHit[ii] = false;
1090  } // UseHit
1091  } // ii
1092  tp.Chg = 0;
1093  } // UnsetUsedHits
1094 
1095  ////////////////////////////////////////////////
1096  bool
1098  {
1099 
1100  // check for errors
1101  for (auto& tp : tj.Pts) {
1102  if (tp.Hits.size() > 16) return false;
1103  } // tp
1104 
1105  if (tj.NeedsUpdate) UpdateTjChgProperties("ST", slc, tj, false);
1106 
1107  // This shouldn't be necessary but do it anyway
1108  SetEndPoints(tj);
1109 
1110  if (slc.tjs.size() >= USHRT_MAX || tj.EndPt[1] <= tj.EndPt[0] || tj.EndPt[1] > tj.Pts.size()) {
1111  ReleaseHits(slc, tj);
1112  return false;
1113  }
1114 
1115  unsigned short npts = tj.EndPt[1] - tj.EndPt[0] + 1;
1116  if (npts < 2) return false;
1117 
1118  auto& endTp0 = tj.Pts[tj.EndPt[0]];
1119  auto& endTp1 = tj.Pts[tj.EndPt[1]];
1120 
1121  // ensure that angle errors are defined at both ends, ignoring junk Tjs
1122  if (!tj.AlgMod[kJunkTj]) {
1123  if (endTp0.AngErr == 0.1 && endTp1.AngErr != 0.1) { endTp0.AngErr = endTp1.AngErr; }
1124  else if (endTp0.AngErr != 0.1 && endTp1.AngErr == 0.1) {
1125  endTp1.AngErr = endTp0.AngErr;
1126  }
1127  } // not a junk Tj
1128 
1129  // Calculate the charge near the end and beginning if necessary. This must be a short
1130  // trajectory. Find the average using 4 points
1131  if (endTp0.AveChg <= 0) {
1132  unsigned short cnt = 0;
1133  float sum = 0;
1134  for (unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
1135  if (tj.Pts[ipt].Chg == 0) continue;
1136  sum += tj.Pts[ipt].Chg;
1137  ++cnt;
1138  if (cnt == 4) break;
1139  }
1140  tj.Pts[tj.EndPt[0]].AveChg = sum / (float)cnt;
1141  }
1142  if (endTp1.AveChg <= 0 && npts < 5) endTp1.AveChg = endTp0.AveChg;
1143  if (endTp1.AveChg <= 0) {
1144  float sum = 0;
1145  unsigned short cnt = 0;
1146  for (unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
1147  short ipt = tj.EndPt[1] - ii;
1148  if (ipt < 0) break;
1149  if (tj.Pts[ipt].Chg == 0) continue;
1150  sum += tj.Pts[ipt].Chg;
1151  ++cnt;
1152  if (cnt == 4) break;
1153  if (ipt == 0) break;
1154  } // ii
1155  tj.Pts[tj.EndPt[1]].AveChg = sum / (float)cnt;
1156  } // begin charge == end charge
1157 
1158  // update the kink significance
1159  if (!tj.AlgMod[kJunkTj]) {
1160  unsigned short nPtsFit = tcc.kinkCuts[0];
1161  bool useChg = (tcc.kinkCuts[2] > 0);
1162  if (npts > 2 * nPtsFit) {
1163  for (unsigned short ipt = tj.EndPt[0] + nPtsFit; ipt < tj.EndPt[1] - nPtsFit; ++ipt) {
1164  auto& tp = tj.Pts[ipt];
1165  if (tp.KinkSig < 0) tp.KinkSig = KinkSignificance(slc, tj, ipt, nPtsFit, useChg, false);
1166  }
1167  } // long trajectory
1168  } // not JunkTj
1169 
1170  UpdateTjChgProperties("ST", slc, tj, false);
1171 
1172  int trID = slc.tjs.size() + 1;
1173 
1174  for (unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
1175  for (unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
1176  if (tj.Pts[ipt].UseHit[ii]) {
1177  unsigned int iht = tj.Pts[ipt].Hits[ii];
1178  if (iht > slc.slHits.size() - 1) {
1179  ReleaseHits(slc, tj);
1180  return false;
1181  }
1182  if (slc.slHits[iht].InTraj > 0) {
1183  ReleaseHits(slc, tj);
1184  return false;
1185  } // error
1186  slc.slHits[iht].InTraj = trID;
1187  }
1188  } // ii
1189  } // ipt
1190 
1191  // ensure that inTraj is clean for the ID
1192  for (unsigned int iht = 0; iht < slc.slHits.size(); ++iht) {
1193  if (slc.slHits[iht].InTraj == tj.ID) {
1194  mf::LogWarning("TC") << "StoreTraj: Hit " << PrintHit(slc.slHits[iht])
1195  << " thinks it belongs to T" << tj.ID << " but it isn't in the Tj\n";
1196  return false;
1197  }
1198  } // iht
1199 
1200  tj.WorkID = tj.ID;
1201  tj.ID = trID;
1202  // increment the global ID
1203  ++evt.globalT_UID;
1204  tj.UID = evt.globalT_UID;
1205  // Don't clobber the ParentID if it was defined by the calling function
1206  if (tj.ParentID == 0) tj.ParentID = trID;
1207  slc.tjs.push_back(tj);
1208  if (tcc.modes[kDebug] && tcc.dbgSlc && debug.Hit != UINT_MAX) {
1209  // print some debug info
1210  for (unsigned short ipt = 0; ipt < tj.Pts.size(); ++ipt) {
1211  for (unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
1212  unsigned int iht = tj.Pts[ipt].Hits[ii];
1213  if (slc.slHits[iht].allHitsIndex == debug.Hit) {
1214  std::cout << "Debug hit appears in trajectory w WorkID " << tj.WorkID << " UseHit "
1215  << tj.Pts[ipt].UseHit[ii] << "\n";
1216  }
1217  } // ii
1218  } // ipt
1219  } // debug.Hit ...
1220 
1221  return true;
1222 
1223  } // StoreTraj
1224 
1225  //////////////////////////////////////////
1226  void
1227  FitPar(const TCSlice& slc,
1228  const Trajectory& tj,
1229  unsigned short originPt,
1230  unsigned short npts,
1231  short fitDir,
1232  ParFit& pFit,
1233  unsigned short usePar)
1234  {
1235  // Fit a TP parameter, like Chg or Delta, to a line using the points starting at originPT.
1236  // Currently supported values of usePar are Chg (1) and Delta (2)
1237 
1238  pFit.ChiDOF = 999;
1239  pFit.AvePar = 0.;
1240  if (originPt > tj.Pts.size() - 1) return;
1241  if (fitDir != 1 && fitDir != -1) return;
1242  Point2_t inPt;
1243  Vector2_t outVec, outVecErr;
1244  float pErr, chiDOF;
1245  Fit2D(0, inPt, pErr, outVec, outVecErr, chiDOF);
1246  unsigned short cnt = 0;
1247  for (unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
1248  unsigned short ipt = originPt + ii * fitDir;
1249  if (ipt < tj.EndPt[0] || ipt > tj.EndPt[1]) break;
1250  auto& tp = tj.Pts[ipt];
1251  if (tp.Chg <= 0) continue;
1252  // Accumulate and save points
1253  inPt[0] = std::abs(tp.Pos[0] - tj.Pts[originPt].Pos[0]);
1254  float parVal = tp.Chg;
1255  // Assume errors are 10% for a charge fit
1256  pErr = 0.1 * parVal;
1257  if (usePar > 1) {
1258  parVal = tp.Delta;
1259  // use the TP hit position error for a Delta Fit
1260  pErr = sqrt(tp.HitPosErr2);
1261  }
1262  inPt[1] = parVal;
1263  pFit.AvePar += parVal;
1264  if (!Fit2D(2, inPt, pErr, outVec, outVecErr, chiDOF)) break;
1265  ++cnt;
1266  if (cnt == npts) break;
1267  } // ii
1268  if (cnt < npts) return;
1269  // do the fit and get the results
1270  if (!Fit2D(-1, inPt, pErr, outVec, outVecErr, chiDOF)) return;
1271  pFit.Pos = tj.Pts[originPt].Pos;
1272  pFit.Par0 = outVec[0];
1273  pFit.AvePar /= (float)cnt;
1274  pFit.ParErr = outVecErr[0];
1275  pFit.Pos = tj.Pts[originPt].Pos;
1276  pFit.ParSlp = outVec[1];
1277  pFit.ParSlpErr = outVecErr[1];
1278  pFit.ChiDOF = chiDOF;
1279  pFit.nPtsFit = cnt;
1280  } // FitPar
1281 
1282  ////////////////////////////////////////////////
1283  bool
1284  InTrajOK(TCSlice& slc, std::string someText)
1285  {
1286  // Check slc.tjs -> InTraj associations
1287 
1288  unsigned short tID;
1289  unsigned int iht;
1290  unsigned short itj = 0;
1291  std::vector<unsigned int> tHits;
1292  std::vector<unsigned int> atHits;
1293  for (auto& tj : slc.tjs) {
1294  // ignore abandoned trajectories
1295  if (tj.AlgMod[kKilled]) continue;
1296  tID = tj.ID;
1297  tHits = PutTrajHitsInVector(tj, kUsedHits);
1298  if (tHits.size() < 2) continue;
1299  std::sort(tHits.begin(), tHits.end());
1300  atHits.clear();
1301  for (iht = 0; iht < slc.slHits.size(); ++iht) {
1302  if (slc.slHits[iht].InTraj == tID) atHits.push_back(iht);
1303  } // iht
1304  if (atHits.size() < 2) continue;
1305  if (!std::equal(tHits.begin(), tHits.end(), atHits.begin())) {
1306  mf::LogVerbatim myprt("TC");
1307  myprt << someText << " ChkInTraj failed: inTraj - UseHit mis-match for T" << tID
1308  << " tj.WorkID " << tj.WorkID << " atHits size " << atHits.size() << " tHits size "
1309  << tHits.size() << " in CTP " << tj.CTP << "\n";
1310  myprt << "AlgMods: ";
1311  for (unsigned short ib = 0; ib < AlgBitNames.size(); ++ib)
1312  if (tj.AlgMod[ib]) myprt << " " << AlgBitNames[ib];
1313  myprt << "\n";
1314  myprt << "index inTraj UseHit \n";
1315  for (iht = 0; iht < atHits.size(); ++iht) {
1316  myprt << "iht " << iht << " " << PrintHit(slc.slHits[atHits[iht]]);
1317  if (iht < tHits.size()) myprt << " " << PrintHit(slc.slHits[tHits[iht]]);
1318  if (atHits[iht] != tHits[iht]) myprt << " <<< " << atHits[iht] << " != " << tHits[iht];
1319  myprt << "\n";
1320  } // iht
1321  if (tHits.size() > atHits.size()) {
1322  for (iht = atHits.size(); iht < atHits.size(); ++iht) {
1323  myprt << "atHits " << iht << " " << PrintHit(slc.slHits[atHits[iht]]) << "\n";
1324  } // iht
1325  PrintTrajectory("CIT", slc, tj, USHRT_MAX);
1326  } // tHit.size > atHits.size()
1327  return false;
1328  }
1329  // check the VtxID
1330  for (unsigned short end = 0; end < 2; ++end) {
1331  if (tj.VtxID[end] > slc.vtxs.size()) {
1332  mf::LogVerbatim("TC") << someText << " ChkInTraj: Bad VtxID " << tj.ID;
1333  tj.AlgMod[kKilled] = true;
1334  return false;
1335  }
1336  } // end
1337  ++itj;
1338  } // tj
1339  return true;
1340 
1341  } // InTrajOK
1342 
1343  //////////////////////////////////////////
1344  void
1345  CheckTrajBeginChg(TCSlice& slc, unsigned short itj)
1346  {
1347  // This function is called after the beginning of the tj has been inspected to see if
1348  // reverse propagation was warranted. Trajectory points at the beginning were removed by
1349  // this process.
1350  // A search has been made for a Bragg peak with nothing
1351  // found. Here we look for a charge pattern like the following, where C means large charge
1352  // and c means lower charge:
1353  // CCCCCCccccccc
1354  // The charge in the two regions should be fairly uniform.
1355 
1356  // This function may split the trajectory so it needs to have been stored
1357  if (itj > slc.tjs.size() - 1) return;
1358  auto& tj = slc.tjs[itj];
1359 
1360  if (!tcc.useAlg[kBeginChg]) return;
1361  if (tj.EndFlag[0][kBragg]) return;
1362  if (tj.AlgMod[kFTBRvProp]) return;
1363  if (tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) return;
1364  if (tj.Pts.size() < 20) return;
1365 
1366  bool prt = (tcc.dbgSlc && (tcc.dbgStp || tcc.dbgAlg[kBeginChg]));
1367 
1368  // look for a large drop between the average charge near the beginning
1369  float chg2 = tj.Pts[tj.EndPt[0] + 2].AveChg;
1370  // and the average charge 15 points away
1371  float chg15 = tj.Pts[tj.EndPt[0] + 15].AveChg;
1372  if (chg2 < 3 * chg15) return;
1373 
1374  // find the point where the charge falls below the mid-point
1375  float midChg = 0.5 * (chg2 + chg15);
1376 
1377  unsigned short breakPt = USHRT_MAX;
1378  for (unsigned short ipt = tj.EndPt[0] + 3; ipt < 15; ++ipt) {
1379  float chgm2 = tj.Pts[ipt - 2].Chg;
1380  if (chgm2 == 0) continue;
1381  float chgm1 = tj.Pts[ipt - 1].Chg;
1382  if (chgm1 == 0) continue;
1383  float chgp1 = tj.Pts[ipt + 1].Chg;
1384  if (chgp1 == 0) continue;
1385  float chgp2 = tj.Pts[ipt + 2].Chg;
1386  if (chgp2 == 0) continue;
1387  if (chgm2 > midChg && chgm1 > midChg && chgp1 < midChg && chgp2 < midChg) {
1388  breakPt = ipt;
1389  break;
1390  }
1391  } // breakPt
1392  if (breakPt == USHRT_MAX) return;
1393  // check the charge and rms before and after the split
1394  std::array<double, 2> cnt, sum, sum2;
1395  for (unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
1396  auto& tp = tj.Pts[ipt];
1397  if (tp.Chg <= 0) continue;
1398  unsigned short end = 0;
1399  if (ipt > breakPt) end = 1;
1400  ++cnt[end];
1401  sum[end] += tp.Chg;
1402  sum2[end] += tp.Chg * tp.Chg;
1403  } // ipt
1404  for (unsigned short end = 0; end < 2; ++end) {
1405  if (cnt[end] < 3) return;
1406  double ave = sum[end] / cnt[end];
1407  double arg = sum2[end] - cnt[end] * ave * ave;
1408  if (arg <= 0) return;
1409  sum2[end] = sqrt(arg / (cnt[end] - 1));
1410  sum2[end] /= ave;
1411  sum[end] = ave;
1412  } // region
1413  bool doSplit = true;
1414  // don't split if this looks like an electron - no significant improvement
1415  // in the charge rms before and after
1416  if (tj.ChgRMS > 0.5 && sum2[0] > 0.3 && sum2[1] > 0.3) doSplit = false;
1417  if (prt) {
1418  mf::LogVerbatim myprt("TC");
1419  myprt << "CTBC: T" << tj.ID << " chgRMS " << tj.ChgRMS;
1420  myprt << " AveChg before split point " << (int)sum[0] << " rms " << sum2[0];
1421  myprt << " after " << (int)sum[1] << " rms " << sum2[1] << " doSplit? " << doSplit;
1422  } // prt
1423  if (!doSplit) return;
1424  // Create a vertex at the break point
1425  VtxStore aVtx;
1426  aVtx.Pos = tj.Pts[breakPt].Pos;
1427  aVtx.NTraj = 2;
1428  aVtx.Pass = tj.Pass;
1429  aVtx.Topo = 8;
1430  aVtx.ChiDOF = 0;
1431  aVtx.CTP = tj.CTP;
1432  aVtx.ID = slc.vtxs.size() + 1;
1433  aVtx.Stat[kFixed] = true;
1434  unsigned short ivx = slc.vtxs.size();
1435  if (!StoreVertex(slc, aVtx)) return;
1436  if (!SplitTraj(slc, itj, breakPt, ivx, prt)) {
1437  if (prt) mf::LogVerbatim("TC") << "CTBC: Failed to split trajectory";
1438  MakeVertexObsolete("CTBC", slc, slc.vtxs[ivx], false);
1439  return;
1440  }
1441  SetVx2Score(slc);
1442  slc.tjs[itj].AlgMod[kBeginChg] = true;
1443 
1444  if (prt)
1445  mf::LogVerbatim("TC") << "CTBC: Split T" << tj.ID << " at "
1446  << PrintPos(slc, tj.Pts[breakPt].Pos) << "\n";
1447 
1448  } // CheckTrajBeginChg
1449 
1450  //////////////////////////////////////////
1451  bool
1452  BraggSplit(TCSlice& slc, unsigned short itj)
1453  {
1454  // Searches the stored trajectory for a Bragg Peak and kink and splits it
1455  if (!tcc.useAlg[kBraggSplit]) return false;
1456  if (itj > slc.tjs.size() - 1) return false;
1457  if (tcc.chkStopCuts.size() < 4) return false;
1458  if (tcc.chkStopCuts[3] <= 0) return false;
1459  unsigned short nPtsToCheck = tcc.chkStopCuts[1];
1460  auto& tj = slc.tjs[itj];
1461  unsigned short npwc = NumPtsWithCharge(slc, tj, false);
1462  if (npwc < 4) return false;
1463  if (npwc < nPtsToCheck) nPtsToCheck = npwc;
1464  // do a rough ChgPull check first
1465  float maxPull = 2;
1466  unsigned short maxPullPt = USHRT_MAX;
1467  for (unsigned short ipt = tj.EndPt[0]; ipt < tj.EndPt[1]; ++ipt) {
1468  auto& tp = tj.Pts[ipt];
1469  if (tp.ChgPull < maxPull) continue;
1470  maxPull = tp.ChgPull;
1471  maxPullPt = ipt;
1472  } // ipt
1473  if (maxPullPt == USHRT_MAX) return false;
1474  short dpt;
1475  if (maxPullPt < 0.5 * (tj.EndPt[0] + tj.EndPt[1])) { dpt = maxPullPt - tj.EndPt[0]; }
1476  else {
1477  dpt = tj.EndPt[1] - maxPullPt;
1478  }
1479  if (dpt < 3) return false;
1480  bool prt = (tcc.dbgSlc && (tcc.dbgStp || tcc.dbgAlg[kBraggSplit]));
1481  if (prt)
1482  mf::LogVerbatim("TC") << "BS: T" << tj.ID << " maxPull " << maxPull << " at "
1483  << PrintPos(slc, tj.Pts[maxPullPt]) << " dpt " << dpt;
1484  unsigned short breakPt = USHRT_MAX;
1485  float bestFOM = tcc.chkStopCuts[3];
1486  unsigned short bestBragg = 0;
1487  unsigned short nPtsFit = tcc.kinkCuts[0];
1488  TrajPoint tp1, tp2;
1489  ParFit chgFit1, chgFit2;
1490  for (unsigned short ipt = maxPullPt - 2; ipt <= maxPullPt + 2; ++ipt) {
1491  FitTraj(slc, tj, ipt - 1, nPtsFit, -1, tp1);
1492  if (tp1.FitChi > 10) continue;
1493  FitTraj(slc, tj, ipt + 1, nPtsFit, 1, tp2);
1494  if (tp2.FitChi > 10) continue;
1495  float dang = std::abs(tp1.Ang - tp2.Ang);
1496  FitPar(slc, tj, ipt - 1, nPtsToCheck, -1, chgFit1, 1);
1497  if (chgFit1.ChiDOF > 100) continue;
1498  chgFit1.ParSlp = -chgFit1.ParSlp;
1499  FitPar(slc, tj, ipt + 1, nPtsToCheck, 1, chgFit2, 1);
1500  if (chgFit2.ChiDOF > 100) continue;
1501  chgFit2.ParSlp = -chgFit2.ParSlp;
1502  // require a large positive slope on at least one side
1503  if (chgFit1.ParSlp < tcc.chkStopCuts[0] && chgFit2.ParSlp < tcc.chkStopCuts[0]) continue;
1504  // assume it is on side 1
1505  unsigned short bragg = 1;
1506  float bchi = chgFit1.ChiDOF;
1507  if (chgFit2.ParSlp > chgFit1.ParSlp) {
1508  bragg = 2;
1509  bchi = chgFit2.ChiDOF;
1510  }
1511  float chgAsym = std::abs(chgFit1.Par0 - chgFit2.Par0) / (chgFit1.Par0 + chgFit2.Par0);
1512  float slpAsym = std::abs(chgFit1.ParSlp - chgFit2.ParSlp) / (chgFit1.ParSlp + chgFit2.ParSlp);
1513  if (bchi < 1) bchi = 1;
1514  float fom = 10 * dang * chgAsym * slpAsym / bchi;
1515  if (prt) {
1516  mf::LogVerbatim myprt("TC");
1517  myprt << "pt " << PrintPos(slc, tj.Pts[ipt]) << " " << std::setprecision(2) << dang;
1518  myprt << " chg1 " << (int)chgFit1.Par0 << " slp " << chgFit1.ParSlp << " chi "
1519  << chgFit1.ChiDOF;
1520  myprt << " chg2 " << (int)chgFit2.Par0 << " slp " << chgFit2.ParSlp << " chi "
1521  << chgFit2.ChiDOF;
1522  myprt << " chgAsym " << chgAsym;
1523  myprt << " slpAsym " << slpAsym;
1524  myprt << " fom " << fom;
1525  myprt << " bragg " << bragg;
1526  }
1527  if (fom < bestFOM) continue;
1528  bestFOM = fom;
1529  breakPt = ipt;
1530  bestBragg = bragg;
1531  } // ipt
1532  if (breakPt == USHRT_MAX) return false;
1533  if (prt)
1534  mf::LogVerbatim("TC") << " breakPt " << PrintPos(slc, tj.Pts[breakPt]) << " bragg "
1535  << bestBragg;
1536  // Create a vertex at the break point
1537  VtxStore aVtx;
1538  aVtx.Pos = tj.Pts[breakPt].Pos;
1539  aVtx.NTraj = 2;
1540  aVtx.Pass = tj.Pass;
1541  aVtx.Topo = 12;
1542  aVtx.ChiDOF = 0;
1543  aVtx.CTP = tj.CTP;
1544  aVtx.ID = slc.vtxs.size() + 1;
1545  aVtx.Stat[kFixed] = true;
1546  unsigned short ivx = slc.vtxs.size();
1547  if (!StoreVertex(slc, aVtx)) return false;
1548  if (!SplitTraj(slc, itj, breakPt, ivx, prt)) {
1549  if (prt) mf::LogVerbatim("TC") << "BS: Failed to split trajectory";
1550  MakeVertexObsolete("BS", slc, slc.vtxs[ivx], false);
1551  return false;
1552  }
1553  SetVx2Score(slc);
1554  slc.tjs[itj].AlgMod[kBraggSplit] = true;
1555  unsigned short otj = slc.tjs.size() - 1;
1556  if (bestBragg == 2) std::swap(itj, otj);
1557  slc.tjs[itj].PDGCode = 211;
1558  slc.tjs[itj].EndFlag[1][kBragg] = true;
1559  slc.tjs[otj].PDGCode = 13;
1560  return true;
1561  } // BraggSplit
1562 
1563  //////////////////////////////////////////
1564  void
1565  TrimHiChgEndPts(TCSlice& slc, Trajectory& tj, bool prt)
1566  {
1567  // Trim points at the end if the charge pull is too high
1568  if (!tcc.useAlg[kTHCEP]) return;
1569  // don't consider long electrons
1570  if (tj.PDGCode == 111) return;
1571  unsigned short npwc = NumPtsWithCharge(slc, tj, false);
1572  // only consider long tjs
1573  if (npwc < 50) return;
1574  // that don't have a Bragg peak
1575  if (tj.EndFlag[1][kBragg]) return;
1576 
1577  // only look at the last points that would have not been considered by GottaKink
1578  unsigned short nPtsMax = tcc.kinkCuts[0];
1579  if (nPtsMax > 8) nPtsMax = 8;
1580 
1581  // find the first point with a high charge pull starting at nPtsMax points before the end
1582  // and count the number of high charge pull points
1583  float cntBad = 0;
1584  unsigned short firstBad = USHRT_MAX;
1585  for (unsigned short ii = 0; ii < nPtsMax; ++ii) {
1586  unsigned short ipt = tj.EndPt[1] - nPtsMax + ii;
1587  auto& tp = tj.Pts[ipt];
1588  if (tp.Chg <= 0) continue;
1589  if (tp.ChgPull < 3) continue;
1590  ++cntBad;
1591  if (firstBad == USHRT_MAX) firstBad = ipt;
1592  } // ii
1593  if (firstBad == USHRT_MAX) return;
1594  // total number of points from the first bad point to the end
1595  float cntTot = tj.EndPt[1] - firstBad;
1596  // fraction of those poins that are bad
1597  float fracBad = cntBad / cntTot;
1598  if (fracBad < 0.5) return;
1599  if (prt)
1600  mf::LogVerbatim("TC") << "THCEP: Trim points starting at " << PrintPos(slc, tj.Pts[firstBad]);
1601  for (unsigned short ipt = firstBad; ipt <= tj.EndPt[1]; ++ipt)
1602  UnsetUsedHits(slc, tj.Pts[ipt]);
1603  tj.AlgMod[kTHCEP] = true;
1604  } // TrimHiChgEndPts
1605 
1606  //////////////////////////////////////////
1607  void
1609  TCSlice& slc,
1610  Trajectory& tj,
1611  const std::vector<float>& fQualityCuts,
1612  bool prt)
1613  {
1614  // Trim the hits off the end until there are at least MinPts consecutive hits at the end
1615  // and the fraction of hits on the trajectory exceeds fQualityCuts[0]
1616  // Minimum length requirement accounting for dead wires where - denotes a wire with a point
1617  // and D is a dead wire. Here is an example with minPts = 3
1618  // ---DDDDD--- is OK
1619  // ----DD-DD-- is OK
1620  // ----DDD-D-- is OK
1621  // ----DDDDD-- is not OK
1622 
1623  if (!tcc.useAlg[kTEP]) return;
1624  if (tj.PDGCode == 111) return;
1625  if (tj.EndFlag[1][kAtKink]) return;
1626 
1627  unsigned short npwc = NumPtsWithCharge(slc, tj, false);
1628  short minPts = fQualityCuts[1];
1629  if (minPts < 1) return;
1630  if (npwc < minPts) return;
1631  // don't consider short Tjs
1632  if (npwc < 8) return;
1633 
1634  // handle short tjs
1635  if (npwc == minPts + 1) {
1636  unsigned short endPt1 = tj.EndPt[1];
1637  auto& tp = tj.Pts[endPt1];
1638  auto& ptp = tj.Pts[endPt1 - 1];
1639  // remove the last point if the previous point has no charge or if
1640  // it isn't on the next wire
1641  float dwire = std::abs(ptp.Pos[0] - tp.Pos[0]);
1642  if (ptp.Chg == 0 || dwire > 1.1) {
1643  UnsetUsedHits(slc, tp);
1644  SetEndPoints(tj);
1645  tj.AlgMod[kTEP] = true;
1646  }
1647  return;
1648  } // short tj
1649 
1650  // find the separation between adjacent points, starting at the end
1651  short lastPt = 0;
1652  for (lastPt = tj.EndPt[1]; lastPt >= minPts; --lastPt) {
1653  // check for an error
1654  if (lastPt == 1) break;
1655  if (tj.Pts[lastPt].Chg == 0) continue;
1656  // number of points on adjacent wires
1657  unsigned short nadj = 0;
1658  unsigned short npwc = 0;
1659  for (short ipt = lastPt - minPts; ipt < lastPt; ++ipt) {
1660  if (ipt < 2) break;
1661  // the current point
1662  auto& tp = tj.Pts[ipt];
1663  // the previous point
1664  auto& ptp = tj.Pts[ipt - 1];
1665  if (tp.Chg > 0 && ptp.Chg > 0) {
1666  ++npwc;
1667  if (std::abs(tp.Pos[0] - ptp.Pos[0]) < 1.5) ++nadj;
1668  }
1669  } // ipt
1670  float ntpwc = NumPtsWithCharge(slc, tj, true, tj.EndPt[0], lastPt);
1671  float nwires = std::abs(tj.Pts[tj.EndPt[0]].Pos[0] - tj.Pts[lastPt].Pos[0]) + 1;
1672  float hitFrac = ntpwc / nwires;
1673  if (prt)
1674  mf::LogVerbatim("TC") << fcnLabel << "-TEP: T" << tj.ID << " lastPt " << lastPt << " npwc "
1675  << npwc << " ntpwc " << ntpwc << " nadj " << nadj << " hitFrac "
1676  << hitFrac;
1677  if (hitFrac > fQualityCuts[0] && npwc == minPts && nadj >= minPts - 1) break;
1678  } // lastPt
1679 
1680  if (prt) mf::LogVerbatim("TC") << " lastPt " << lastPt << " " << tj.EndPt[1] << "\n";
1681  // trim the last point if it just after a dead wire.
1682  if (tj.Pts[lastPt].Pos[0] > -0.4) {
1683  unsigned int prevWire = std::nearbyint(tj.Pts[lastPt].Pos[0]);
1684  if (tj.StepDir > 0) { --prevWire; }
1685  else {
1686  ++prevWire;
1687  }
1688  if (prt) {
1689  mf::LogVerbatim("TC") << fcnLabel << "-TEP: is prevWire " << prevWire << " dead? ";
1690  }
1691  unsigned short plane = DecodeCTP(tj.CTP).Plane;
1692  if (prevWire < slc.nWires[plane] && !evt.goodWire[plane][prevWire]) --lastPt;
1693  } // valid Pos[0]
1694 
1695  // Nothing needs to be done
1696  if (lastPt == tj.EndPt[1]) {
1697  if (prt) mf::LogVerbatim("TC") << fcnLabel << "-TEPo: Tj is OK";
1698  return;
1699  }
1700 
1701  // clear the points after lastPt
1702  for (unsigned short ipt = lastPt + 1; ipt <= tj.EndPt[1]; ++ipt)
1703  UnsetUsedHits(slc, tj.Pts[ipt]);
1704  SetEndPoints(tj);
1705  tj.AlgMod[kTEP] = true;
1706  if (prt) {
1707  fcnLabel += "-TEPo";
1708  PrintTrajectory(fcnLabel, slc, tj, USHRT_MAX);
1709  }
1710 
1711  } // TrimEndPts
1712 
1713  /////////////////////////////////////////
1714  void
1715  ChkEndKink(TCSlice& slc, Trajectory& tj, bool prt)
1716  {
1717  // look for large-angle kink near the end
1718  if (!tcc.useAlg[kEndKink]) return;
1719  if (tj.PDGCode == 111) return;
1720  if (tj.EndPt[1] - tj.EndPt[0] < 6) return;
1721 
1722  if (prt) mf::LogVerbatim("TC") << "CEK: Inside ChkEndKinks T" << tj.ID << " ";
1723 
1724  float maxSig = tcc.kinkCuts[1];
1725  unsigned short withNptsFit = 0;
1726  unsigned short nPtsFit = tcc.kinkCuts[0];
1727  bool useChg = (tcc.kinkCuts[2] > 0);
1728  for (unsigned short nptsf = 3; nptsf < nPtsFit; ++nptsf) {
1729  unsigned short ipt = tj.EndPt[1] - nptsf;
1730  float ks = KinkSignificance(slc, tj, ipt, nptsf, useChg, prt);
1731  if (ks > maxSig) {
1732  maxSig = ks;
1733  withNptsFit = nptsf;
1734  }
1735  } // nptsf
1736  if (withNptsFit > 0) {
1737  unsigned short ipt = tj.EndPt[1] - withNptsFit;
1738  std::cout << "CEK: T" << tj.ID << " ipt " << ipt;
1739  float ks = KinkSignificance(slc, tj, ipt, withNptsFit, false, prt);
1740  auto& tp = tj.Pts[ipt];
1741  std::cout << " " << PrintPos(slc, tp) << " withNptsFit " << withNptsFit << " ks " << ks
1742  << "\n";
1743  }
1744 
1745  } // ChkEndKink
1746 
1747  /////////////////////////////////////////
1748  void
1749  ChkChgAsymmetry(TCSlice& slc, Trajectory& tj, bool prt)
1750  {
1751  // looks for a high-charge point in the trajectory which may be due to the
1752  // trajectory crossing an interaction vertex. The properties of points on the opposite
1753  // sides of the high-charge point are analyzed. If significant differences are found, all points
1754  // near the high-charge point are removed as well as those from that point to the end
1755  if (!tcc.useAlg[kChkChgAsym]) return;
1756  if (tj.PDGCode == 111) return;
1757  unsigned short npts = tj.EndPt[1] - tj.EndPt[0];
1758  if (prt) mf::LogVerbatim("TC") << " Inside ChkChgAsymmetry T" << tj.ID;
1759  // ignore long tjs
1760  if (npts > 50) return;
1761  // ignore short tjs
1762  if (npts < 8) return;
1763  // require the charge pull > 5
1764  float bigPull = 5;
1765  unsigned short atPt = 0;
1766  // Don't consider the first/last few points in case there is a Bragg peak
1767  for (unsigned short ipt = tj.EndPt[0] + 2; ipt <= tj.EndPt[1] - 2; ++ipt) {
1768  auto& tp = tj.Pts[ipt];
1769  if (tp.ChgPull > bigPull) {
1770  bigPull = tp.ChgPull;
1771  atPt = ipt;
1772  }
1773  } // ipt
1774  if (atPt == 0) return;
1775  // require that this point be near the DS end
1776  if ((atPt - tj.EndPt[0]) < 0.5 * npts) return;
1777  if (prt)
1778  mf::LogVerbatim("TC") << "CCA: T" << tj.ID << " Large Chg point at " << atPt
1779  << ". Check charge asymmetry around it.";
1780  unsigned short nchk = 0;
1781  unsigned short npos = 0;
1782  unsigned short nneg = 0;
1783  for (short ii = 1; ii < 5; ++ii) {
1784  short iplu = atPt + ii;
1785  if (iplu > tj.EndPt[1]) break;
1786  short ineg = atPt - ii;
1787  if (ineg < tj.EndPt[0]) break;
1788  if (tj.Pts[iplu].Chg == 0) continue;
1789  if (tj.Pts[ineg].Chg == 0) continue;
1790  float asym = (tj.Pts[iplu].Chg - tj.Pts[ineg].Chg) / (tj.Pts[iplu].Chg + tj.Pts[ineg].Chg);
1791  ++nchk;
1792  if (asym > 0.5) ++npos;
1793  if (asym < -0.5) ++nneg;
1794  if (prt)
1795  mf::LogVerbatim("TC") << " ineg " << ineg << " iplu " << iplu << " asym " << asym
1796  << " nchk " << nchk;
1797  } // ii
1798  if (nchk < 3) return;
1799  // require most of the points be very positive or very negative
1800  nchk -= 2;
1801  bool doTrim = (nneg > nchk) || (npos > nchk);
1802  if (!doTrim) return;
1803  // remove all the points at the end starting at the one just before the peak if the pull is not so good
1804  auto& prevTP = tj.Pts[atPt - 1];
1805  if (std::abs(prevTP.ChgPull) > 2) --atPt;
1806  for (unsigned short ipt = atPt; ipt <= tj.EndPt[1]; ++ipt)
1807  UnsetUsedHits(slc, tj.Pts[ipt]);
1808  SetEndPoints(tj);
1809  tj.AlgMod[kChkChgAsym] = true;
1810  if (prt) PrintTrajectory("CCA", slc, tj, USHRT_MAX);
1811  } // ChkChgAsymmetry
1812 
1813  /////////////////////////////////////////
1814  bool
1816  const TrajPoint& tp1,
1817  const TrajPoint& tp2,
1818  const float& MinWireSignalFraction)
1819  {
1820  // Returns true if there is a signal on > MinWireSignalFraction of the wires between tp1 and tp2.
1821  if (MinWireSignalFraction == 0) return true;
1822 
1823  if (tp1.Pos[0] < -0.4 || tp2.Pos[0] < -0.4) return false;
1824  int fromWire = std::nearbyint(tp1.Pos[0]);
1825  int toWire = std::nearbyint(tp2.Pos[0]);
1826 
1827  if (fromWire == toWire) {
1828  TrajPoint tp = tp1;
1829  // check for a signal midway between
1830  tp.Pos[1] = 0.5 * (tp1.Pos[1] + tp2.Pos[1]);
1831  return SignalAtTp(tp);
1832  }
1833  // define a trajectory point located at tp1 that has a direction towards tp2
1834  TrajPoint tp;
1835  if (!MakeBareTrajPoint(slc, tp1, tp2, tp)) return true;
1836  return SignalBetween(slc, tp, toWire, MinWireSignalFraction);
1837  } // SignalBetween
1838 
1839  /////////////////////////////////////////
1840  bool
1841  SignalBetween(const TCSlice& slc, TrajPoint tp, float toPos0, const float& MinWireSignalFraction)
1842  {
1843  // Returns true if there is a signal on > MinWireSignalFraction of the wires between tp and toPos0.
1844  return ChgFracBetween(slc, tp, toPos0) >= MinWireSignalFraction;
1845  } // SignalBetween
1846 
1847  /////////////////////////////////////////
1848  float
1849  ChgFracBetween(const TCSlice& slc, TrajPoint tp, float toPos0)
1850  {
1851  // Returns the fraction of wires between tp.Pos[0] and toPos0 that have a hit
1852  // on the line defined by tp.Pos and tp.Dir
1853 
1854  if (tp.Pos[0] < -0.4 || toPos0 < -0.4) return 0;
1855  int fromWire = std::nearbyint(tp.Pos[0]);
1856  int toWire = std::nearbyint(toPos0);
1857 
1858  if (fromWire == toWire) return SignalAtTp(tp);
1859 
1860  int nWires = abs(toWire - fromWire) + 1;
1861 
1862  if (std::abs(tp.Dir[0]) < 0.001) tp.Dir[0] = 0.001;
1863  float stepSize = std::abs(1 / tp.Dir[0]);
1864  // ensure that we step in the right direction
1865  if (toWire > fromWire && tp.Dir[0] < 0) stepSize = -stepSize;
1866  if (toWire < fromWire && tp.Dir[0] > 0) stepSize = -stepSize;
1867  float nsig = 0;
1868  float num = 0;
1869  for (unsigned short cnt = 0; cnt < nWires; ++cnt) {
1870  ++num;
1871  if (SignalAtTp(tp)) ++nsig;
1872  tp.Pos[0] += tp.Dir[0] * stepSize;
1873  tp.Pos[1] += tp.Dir[1] * stepSize;
1874  } // cnt
1875  float sigFrac = nsig / num;
1876  return sigFrac;
1877  } // ChgFracBetween
1878 
1879  ////////////////////////////////////////////////
1880  bool
1882  const std::vector<unsigned int>& iHitsInMultiplet,
1883  const std::vector<unsigned int>& jHitsInMultiplet)
1884  {
1885  // Hits (assume to be on adjacent wires have an acceptable signal overlap
1886 
1887  if (iHitsInMultiplet.empty() || jHitsInMultiplet.empty()) return false;
1888 
1889  float sum;
1890  float cvI = HitsPosTick(slc, iHitsInMultiplet, sum, kAllHits);
1891  if (cvI < 0) return false;
1892  float minI = 1E6;
1893  float maxI = 0;
1894  for (auto& iht : iHitsInMultiplet) {
1895  auto const& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
1896  float cv = hit.PeakTime();
1897  float rms = hit.RMS();
1898  float arg = cv - 3.1 * rms;
1899  if (arg < minI) minI = arg;
1900  arg = cv + 3.1 * rms;
1901  if (arg > maxI) maxI = arg;
1902  }
1903 
1904  float cvJ = HitsPosTick(slc, jHitsInMultiplet, sum, kAllHits);
1905  if (cvJ < 0) return false;
1906  float minJ = 1E6;
1907  float maxJ = 0;
1908  for (auto& jht : jHitsInMultiplet) {
1909  auto& hit = (*evt.allHits)[slc.slHits[jht].allHitsIndex];
1910  float cv = hit.PeakTime();
1911  float rms = hit.RMS();
1912  float arg = cv - 3.1 * rms;
1913  if (arg < minJ) minJ = arg;
1914  arg = cv + 3.1 * rms;
1915  if (arg > maxJ) maxJ = arg;
1916  }
1917 
1918  if (cvI < cvJ) {
1919  if (maxI > minJ) return true;
1920  }
1921  else {
1922  if (minI < maxJ) return true;
1923  }
1924  return false;
1925  } // TrajHitsOK
1926 
1927  /////////////////////////////////////////
1928  bool
1929  TrajHitsOK(TCSlice& slc, const unsigned int iht, const unsigned int jht)
1930  {
1931  // ensure that two adjacent hits have an acceptable overlap
1932  if (iht > slc.slHits.size() - 1) return false;
1933  if (jht > slc.slHits.size() - 1) return false;
1934  // require that they be on adjacent wires
1935  auto& ihit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
1936  auto& jhit = (*evt.allHits)[slc.slHits[jht].allHitsIndex];
1937  int iwire = ihit.WireID().Wire;
1938  int jwire = jhit.WireID().Wire;
1939  if (std::abs(iwire - jwire) > 1) return false;
1940  if (ihit.PeakTime() > jhit.PeakTime()) {
1941  float minISignal = ihit.PeakTime() - 3 * ihit.RMS();
1942  float maxJSignal = jhit.PeakTime() + 3 * ihit.RMS();
1943  if (maxJSignal > minISignal) return true;
1944  }
1945  else {
1946  float maxISignal = ihit.PeakTime() + 3 * ihit.RMS();
1947  float minJSignal = jhit.PeakTime() - 3 * ihit.RMS();
1948  if (minJSignal > maxISignal) return true;
1949  }
1950  return false;
1951  } // TrajHitsOK
1952 
1953  ////////////////////////////////////////////////
1954  float
1956  {
1957  // returns the expected RMS of hits for the trajectory point in ticks
1958  if (std::abs(tp.Dir[0]) > 0.001) {
1959  geo::PlaneID planeID = DecodeCTP(tp.CTP);
1960  return 1.5 * evt.aveHitRMS[planeID.Plane] +
1961  2 * std::abs(tp.Dir[1] / tp.Dir[0]) / tcc.unitsPerTick;
1962  }
1963  else {
1964  return 500;
1965  }
1966  } // ExpectedHitsRMS
1967 
1968  ////////////////////////////////////////////////
1969  bool
1970  SignalAtTpInSlc(const TCSlice& slc, const TrajPoint& tp)
1971  {
1972  // Version of SignalAtTP that only checks the hit collection in the current slice
1973 
1974  if (tp.Pos[0] < -0.4) return false;
1975  geo::PlaneID planeID = DecodeCTP(tp.CTP);
1976  unsigned short pln = planeID.Plane;
1977  unsigned int wire = std::nearbyint(tp.Pos[0]);
1978  if (wire > evt.goodWire[pln].size() - 1) return false;
1979  // assume there is a signal on a dead wire
1980  if (!evt.goodWire[pln][wire]) return true;
1981  // no signal here if there are no hits on this wire
1982  if (slc.wireHitRange[pln][wire].first == UINT_MAX) return false;
1983  // check the proximity of all of the hits in the range
1984  float projTick = (float)(tp.Pos[1] / tcc.unitsPerTick);
1985  float tickRange = 0;
1986  if (std::abs(tp.Dir[1]) != 0) {
1987  tickRange = std::abs(0.5 / tp.Dir[1]) / tcc.unitsPerTick;
1988  // don't let it get too large
1989  if (tickRange > 40) tickRange = 40;
1990  }
1991  float loTpTick = projTick - tickRange;
1992  float hiTpTick = projTick + tickRange;
1993  for (unsigned int iht = slc.wireHitRange[pln][wire].first;
1994  iht <= slc.wireHitRange[pln][wire].second;
1995  ++iht) {
1996  unsigned int ahi = slc.slHits[iht].allHitsIndex;
1997  auto& hit = (*evt.allHits)[ahi];
1998  if (projTick < hit.PeakTime()) {
1999  float loHitTick = hit.PeakTime() - 3 * hit.RMS();
2000  if (hiTpTick > loHitTick) return true;
2001  }
2002  else {
2003  float hiHitTick = hit.PeakTime() + 3 * hit.RMS();
2004  if (loTpTick < hiHitTick) return true;
2005  }
2006  } // iht
2007  return false;
2008  } // SignalAtTpInSlc
2009 
2010  /////////////////////////////////////////
2011  bool
2013  {
2014  // returns true if there is a hit near tp.Pos by searching through the full hit collection (if there
2015  // are multiple slices) or through the last slice (if there is only one slice)
2016 
2017  tp.Environment[kEnvNearSrcHit] = false;
2018 
2019  // just check the hits in the last slice
2020  if (evt.wireHitRange.empty()) {
2021  const auto& slc = slices[slices.size() - 1];
2022  return SignalAtTpInSlc(slc, tp);
2023  }
2024 
2025  if (tp.Pos[0] < -0.4) return false;
2026  geo::PlaneID planeID = DecodeCTP(tp.CTP);
2027  unsigned short pln = planeID.Plane;
2028  unsigned int wire = std::nearbyint(tp.Pos[0]);
2029  if (wire > evt.goodWire[pln].size() - 1) return false;
2030  // assume there is a signal on a dead wire
2031  if (!evt.goodWire[pln][wire]) return true;
2032 
2033  // check the proximity of all of the hits in the range
2034  float projTick = (float)(tp.Pos[1] / tcc.unitsPerTick);
2035  float tickRange = 0;
2036  if (std::abs(tp.Dir[1]) != 0) {
2037  tickRange = std::abs(0.5 / tp.Dir[1]) / tcc.unitsPerTick;
2038  // don't let it get too large
2039  if (tickRange > 40) tickRange = 40;
2040  }
2041  float loTpTick = projTick - tickRange;
2042  float hiTpTick = projTick + tickRange;
2043 
2044  // no signal here if there are no hits on this wire
2045  if (evt.wireHitRange[pln][wire].first == UINT_MAX) return false;
2046 
2047  for (unsigned int iht = evt.wireHitRange[pln][wire].first;
2048  iht <= evt.wireHitRange[pln][wire].second;
2049  ++iht) {
2050  auto& hit = (*evt.allHits)[iht];
2051  // We wouldn't need to make this check if hits were sorted
2052  const auto& wid = hit.WireID();
2053  if (wid.Cryostat != planeID.Cryostat) continue;
2054  if (wid.TPC != planeID.TPC) continue;
2055  if (wid.Plane != planeID.Plane) continue;
2056  if (projTick < hit.PeakTime()) {
2057  float loHitTick = hit.PeakTime() - 3 * hit.RMS();
2058  if (hiTpTick > loHitTick) return true;
2059  }
2060  else {
2061  float hiHitTick = hit.PeakTime() + 3 * hit.RMS();
2062  if (loTpTick < hiHitTick) return true;
2063  }
2064  } // iht
2065  // No hit was found near projTick. Search through the source hits collection
2066  // (if it is defined) for a hit that may have been removed by disambiguation
2067  // Use the srcHit collection if it is available
2068  if (evt.srcHits != NULL) {
2069  if (NearbySrcHit(planeID, wire, loTpTick, hiTpTick)) {
2070  tp.Environment[kEnvNearSrcHit] = true;
2071  return true;
2072  } // NearbySrcHit
2073  } // evt.srcHits != NULL
2074  return false;
2075  } // SignalAtTp
2076 
2077  //////////////////////////////////////////
2078  bool
2079  NearbySrcHit(geo::PlaneID plnID, unsigned int wire, float loTick, float hiTick)
2080  {
2081  // Look for a hit on wid in the srcHits collection that has a tick in the range. This
2082  // is a DUNE-specific function in which hit disambiguation is done in the U and V planes
2083  if (evt.srcHits == NULL) return false;
2084  unsigned int pln = plnID.Plane;
2085  if (pln == 2) return false;
2086 
2087  unsigned int cstat = plnID.Cryostat;
2088  unsigned int tpc = plnID.TPC;
2089  // get a valid range of hits to search
2090  if (evt.tpcSrcHitRange[tpc].first >= (*evt.srcHits).size()) return false;
2091  if (evt.tpcSrcHitRange[tpc].second >= (*evt.srcHits).size()) return false;
2092  raw::ChannelID_t chan = tcc.geom->PlaneWireToChannel((int)pln, (int)wire, (int)tpc, (int)cstat);
2093  float atTick = 0.5 * (loTick + hiTick);
2094  for (unsigned int iht = evt.tpcSrcHitRange[tpc].first; iht <= evt.tpcSrcHitRange[tpc].second;
2095  ++iht) {
2096  auto& hit = (*evt.srcHits)[iht];
2097  if (hit.Channel() != chan) continue;
2098  if (atTick < hit.PeakTime()) {
2099  float loHitTick = hit.PeakTime() - 3 * hit.RMS();
2100  if (hiTick > loHitTick) return true;
2101  }
2102  else {
2103  float hiHitTick = hit.PeakTime() + 3 * hit.RMS();
2104  if (loTick < hiHitTick) return true;
2105  }
2106  } // iht
2107  return false;
2108  } // NearbySrcHit
2109 
2110  //////////////////////////////////////////
2111  float
2112  TpSumHitChg(const TCSlice& slc, TrajPoint const& tp)
2113  {
2114  float totchg = 0;
2115  for (size_t i = 0; i < tp.Hits.size(); ++i) {
2116  if (!tp.UseHit[i]) continue;
2117  totchg += (*evt.allHits)[slc.slHits[tp.Hits[i]].allHitsIndex].Integral();
2118  }
2119  return totchg;
2120  } // TpSumHitChg
2121 
2122  //////////////////////////////////////////
2123  unsigned short
2124  NumPtsWithCharge(const TCSlice& slc, const Trajectory& tj, bool includeDeadWires)
2125  {
2126  unsigned short firstPt = tj.EndPt[0];
2127  unsigned short lastPt = tj.EndPt[1];
2128  return NumPtsWithCharge(slc, tj, includeDeadWires, firstPt, lastPt);
2129  }
2130 
2131  //////////////////////////////////////////
2132  unsigned short
2134  const Trajectory& tj,
2135  bool includeDeadWires,
2136  unsigned short firstPt,
2137  unsigned short lastPt)
2138  {
2139  unsigned short ntp = 0;
2140  for (unsigned short ipt = firstPt; ipt <= lastPt; ++ipt)
2141  if (tj.Pts[ipt].Chg > 0) ++ntp;
2142  // Add the count of deadwires
2143  if (includeDeadWires) ntp += DeadWireCount(slc, tj.Pts[firstPt], tj.Pts[lastPt]);
2144  return ntp;
2145  } // NumPtsWithCharge
2146 
2147  //////////////////////////////////////////
2148  float
2149  DeadWireCount(const TCSlice& slc, const TrajPoint& tp1, const TrajPoint& tp2)
2150  {
2151  return DeadWireCount(slc, tp1.Pos[0], tp2.Pos[0], tp1.CTP);
2152  } // DeadWireCount
2153 
2154  //////////////////////////////////////////
2155  float
2156  DeadWireCount(const TCSlice& slc, const float& inWirePos1, const float& inWirePos2, CTP_t tCTP)
2157  {
2158  if (inWirePos1 < -0.4 || inWirePos2 < -0.4) return 0;
2159  unsigned int inWire1 = std::nearbyint(inWirePos1);
2160  unsigned int inWire2 = std::nearbyint(inWirePos2);
2161  geo::PlaneID planeID = DecodeCTP(tCTP);
2162  unsigned short plane = planeID.Plane;
2163  if (inWire1 > slc.nWires[plane] || inWire2 > slc.nWires[plane]) return 0;
2164  if (inWire1 > inWire2) {
2165  // put in increasing order
2166  unsigned int tmp = inWire1;
2167  inWire1 = inWire2;
2168  inWire2 = tmp;
2169  } // inWire1 > inWire2
2170  ++inWire2;
2171  unsigned int wire, ndead = 0;
2172  for (wire = inWire1; wire < inWire2; ++wire)
2173  if (!evt.goodWire[plane][wire]) ++ndead;
2174  return ndead;
2175  } // DeadWireCount
2176 
2177  ////////////////////////////////////////////////
2178  unsigned short
2179  PDGCodeIndex(int PDGCode)
2180  {
2181  unsigned short pdg = abs(PDGCode);
2182  if (pdg == 11) return 0; // electron
2183  if (pdg == 13) return 1; // muon
2184  if (pdg == 211) return 2; // pion
2185  if (pdg == 321) return 3; // kaon
2186  if (pdg == 2212) return 4; // proton
2187  return USHRT_MAX;
2188  } // PDGCodeIndex
2189 
2190  ////////////////////////////////////////////////
2191  void
2192  MakeTrajectoryObsolete(TCSlice& slc, unsigned int itj)
2193  {
2194  // Note that this does not change the state of UseHit to allow
2195  // resurrecting the trajectory later (RestoreObsoleteTrajectory)
2196  if (itj > slc.tjs.size() - 1) return;
2197  int killTjID = slc.tjs[itj].ID;
2198  for (auto& hit : slc.slHits)
2199  if (hit.InTraj == killTjID) hit.InTraj = 0;
2200  slc.tjs[itj].AlgMod[kKilled] = true;
2201  } // MakeTrajectoryObsolete
2202 
2203  ////////////////////////////////////////////////
2204  void
2205  RestoreObsoleteTrajectory(TCSlice& slc, unsigned int itj)
2206  {
2207  if (itj > slc.tjs.size() - 1) return;
2208  if (!slc.tjs[itj].AlgMod[kKilled]) {
2209  mf::LogWarning("TC")
2210  << "RestoreObsoleteTrajectory: Trying to restore not-obsolete trajectory "
2211  << slc.tjs[itj].ID;
2212  return;
2213  }
2214  unsigned int iht;
2215  for (auto& tp : slc.tjs[itj].Pts) {
2216  for (unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
2217  if (tp.UseHit[ii]) {
2218  iht = tp.Hits[ii];
2219  if (slc.slHits[iht].InTraj == 0) { slc.slHits[iht].InTraj = slc.tjs[itj].ID; }
2220  }
2221  } // ii
2222  } // tp
2223  slc.tjs[itj].AlgMod[kKilled] = false;
2224  } // RestoreObsoleteTrajectory
2225 
2226  //////////////////////////////////////////
2227  void
2229  {
2230  // Merges short Tjs that share many hits with a longer Tj
2231  if (!tcc.useAlg[kMrgGhost]) return;
2232 
2233  for (auto& shortTj : slc.tjs) {
2234  if (shortTj.AlgMod[kKilled] || shortTj.AlgMod[kHaloTj]) continue;
2235  if (shortTj.CTP != inCTP) continue;
2236  unsigned short spts = shortTj.EndPt[1] - shortTj.EndPt[0];
2237  if (spts > 20) continue;
2238  // ignore delta rays
2239  if (shortTj.PDGCode == 11) continue;
2240  // ignore InShower Tjs
2241  if (shortTj.SSID > 0) continue;
2242  auto tjhits = PutTrajHitsInVector(shortTj, kAllHits);
2243  if (tjhits.empty()) continue;
2244  std::vector<int> tids;
2245  std::vector<unsigned short> tcnt;
2246  for (auto iht : tjhits) {
2247  auto& hit = slc.slHits[iht];
2248  if (hit.InTraj <= 0) continue;
2249  if ((unsigned int)hit.InTraj > slc.tjs.size()) continue;
2250  if (hit.InTraj == shortTj.ID) continue;
2251  unsigned short indx = 0;
2252  for (indx = 0; indx < tids.size(); ++indx)
2253  if (hit.InTraj == tids[indx]) break;
2254  if (indx == tids.size()) {
2255  tids.push_back(hit.InTraj);
2256  tcnt.push_back(1);
2257  }
2258  else {
2259  ++tcnt[indx];
2260  }
2261  } // iht
2262  if (tids.empty()) continue;
2263  // find the max count for Tjs that are longer than this one
2264  unsigned short maxcnt = 0;
2265  for (unsigned short indx = 0; indx < tids.size(); ++indx) {
2266  if (tcnt[indx] > maxcnt) {
2267  auto& ltj = slc.tjs[tids[indx] - 1];
2268  unsigned short lpts = ltj.EndPt[1] - ltj.EndPt[0];
2269  if (lpts < spts) continue;
2270  maxcnt = tcnt[indx];
2271  }
2272  } // indx
2273  float hitFrac = (float)maxcnt / (float)tjhits.size();
2274  if (hitFrac < 0.1) continue;
2275  } // shortTj
2276  } // MergeGhostTjs
2277 
2278  //////////////////////////////////////////
2279  bool
2281  TCSlice& slc,
2282  unsigned short itj,
2283  float XPos,
2284  bool makeVx2,
2285  bool prt)
2286  {
2287  // Splits the trajectory at an X position and optionally creates a 2D vertex
2288  // at the split point
2289  if (itj > slc.tjs.size() - 1) return false;
2290 
2291  auto& tj = slc.tjs[itj];
2292  geo::PlaneID planeID = DecodeCTP(tj.CTP);
2293  float atPos1 = detProp.ConvertXToTicks(XPos, planeID) * tcc.unitsPerTick;
2294  unsigned short atPt = USHRT_MAX;
2295  for (unsigned short ipt = tj.EndPt[0] + 1; ipt <= tj.EndPt[1]; ++ipt) {
2296  if (tj.Pts[ipt].Pos[1] > tj.Pts[ipt - 1].Pos[1]) {
2297  // positive slope
2298  if (tj.Pts[ipt - 1].Pos[1] < atPos1 && tj.Pts[ipt].Pos[1] >= atPos1) {
2299  atPt = ipt;
2300  break;
2301  }
2302  }
2303  else {
2304  // negative slope
2305  if (tj.Pts[ipt - 1].Pos[1] >= atPos1 && tj.Pts[ipt].Pos[1] < atPos1) {
2306  atPt = ipt;
2307  break;
2308  }
2309  } // negative slope
2310  } // ipt
2311  if (atPt == USHRT_MAX) return false;
2312  unsigned short vx2Index = USHRT_MAX;
2313  if (makeVx2) {
2314  VtxStore newVx2;
2315  newVx2.CTP = tj.CTP;
2316  newVx2.Pos[0] = 0.5 * (tj.Pts[atPt - 1].Pos[0] + tj.Pts[atPt].Pos[0]);
2317  newVx2.Pos[1] = 0.5 * (tj.Pts[atPt - 1].Pos[1] + tj.Pts[atPt].Pos[1]);
2318  newVx2.Topo = 10;
2319  newVx2.NTraj = 2;
2320  if (StoreVertex(slc, newVx2)) vx2Index = slc.vtxs.size() - 1;
2321  } // makeVx2
2322  return SplitTraj(slc, itj, atPt, vx2Index, prt);
2323  } // SplitTraj
2324 
2325  //////////////////////////////////////////
2326  bool
2327  SplitTraj(TCSlice& slc, unsigned short itj, unsigned short pos, unsigned short ivx, bool prt)
2328  {
2329  // Splits the trajectory itj in the slc.tjs vector into two trajectories at position pos. Splits
2330  // the trajectory and associates the ends to the supplied vertex.
2331  // Here is an example where itj has 9 points and we will split at pos = 4
2332  // itj (0 1 2 3 4 5 6 7 8) -> new traj (0 1 2 3) + new traj (4 5 6 7 8)
2333 
2334  if (itj > slc.tjs.size() - 1) return false;
2335  if (pos < slc.tjs[itj].EndPt[0] + 1 || pos > slc.tjs[itj].EndPt[1] - 1) return false;
2336  if (ivx != USHRT_MAX && ivx > slc.vtxs.size() - 1) return false;
2337 
2338  Trajectory& tj = slc.tjs[itj];
2339 
2340  // Reset the PDG Code if we are splitting a tagged muon
2341  bool splittingMuon = (tj.PDGCode == 13);
2342  if (splittingMuon) tj.PDGCode = 0;
2343 
2344  if (prt) {
2345  mf::LogVerbatim myprt("TC");
2346  myprt << "SplitTraj: Split T" << tj.ID << " at point " << PrintPos(slc, tj.Pts[pos]);
2347  if (ivx < slc.vtxs.size()) myprt << " with Vtx 2V" << slc.vtxs[ivx].ID;
2348  }
2349 
2350  // ensure that there will be at least 3 TPs on each trajectory
2351  unsigned short ntp = 0;
2352  for (unsigned short ipt = 0; ipt <= pos; ++ipt) {
2353  if (tj.Pts[ipt].Chg > 0) ++ntp;
2354  if (ntp > 2) break;
2355  } // ipt
2356  if (ntp < 3) {
2357  if (prt) mf::LogVerbatim("TC") << " Split point to small at begin " << ntp << " pos " << pos;
2358  return false;
2359  }
2360  ntp = 0;
2361  for (unsigned short ipt = pos + 1; ipt <= tj.EndPt[1]; ++ipt) {
2362  if (tj.Pts[ipt].Chg > 0) ++ntp;
2363  if (ntp > 2) break;
2364  } // ipt
2365  if (ntp < 3) {
2366  if (prt)
2367  mf::LogVerbatim("TC") << " Split point too small at end " << ntp << " pos " << pos
2368  << " EndPt " << tj.EndPt[1];
2369  return false;
2370  }
2371 
2372  // make a copy that will become the Tj after the split point
2373  Trajectory newTj = tj;
2374  newTj.ID = slc.tjs.size() + 1;
2375  ++evt.globalT_UID;
2376  newTj.UID = evt.globalT_UID;
2377  // make another copy in case something goes wrong
2378  Trajectory oldTj = tj;
2379 
2380  // Leave the first section of tj in place. Re-assign the hits
2381  // to the new trajectory
2382  unsigned int iht;
2383  for (unsigned short ipt = pos + 1; ipt <= tj.EndPt[1]; ++ipt) {
2384  tj.Pts[ipt].Chg = 0;
2385  for (unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
2386  if (!tj.Pts[ipt].UseHit[ii]) continue;
2387  iht = tj.Pts[ipt].Hits[ii];
2388  // This shouldn't happen but check anyway
2389  if (slc.slHits[iht].InTraj != tj.ID) continue;
2390  slc.slHits[iht].InTraj = newTj.ID;
2391  tj.Pts[ipt].UseHit[ii] = false;
2392  } // ii
2393  } // ipt
2394  SetEndPoints(tj);
2395  // Update MCSMom and charge properties
2396  tj.MCSMom = MCSMom(slc, tj);
2397  UpdateTjChgProperties("ST", slc, tj, prt);
2398  if (splittingMuon) SetPDGCode(slc, tj);
2399 
2400  // Append 3 points from the end of tj onto the
2401  // beginning of newTj so that hits can be swapped between
2402  // them later
2403  unsigned short eraseSize = pos - 2;
2404  if (eraseSize > newTj.Pts.size() - 1) {
2405  tj = oldTj;
2406  return false;
2407  }
2408 
2409  if (ivx < slc.vtxs.size()) tj.VtxID[1] = slc.vtxs[ivx].ID;
2410  tj.AlgMod[kSplit] = true;
2411  if (prt) {
2412  mf::LogVerbatim("TC") << " Splitting T" << tj.ID << " new EndPts " << tj.EndPt[0] << " to "
2413  << tj.EndPt[1];
2414  }
2415 
2416  // erase the TPs at the beginning of the new trajectory
2417  newTj.Pts.erase(newTj.Pts.begin(), newTj.Pts.begin() + eraseSize);
2418  // unset the first 3 TP hits
2419  for (unsigned short ipt = 0; ipt < 3; ++ipt) {
2420  for (unsigned short ii = 0; ii < newTj.Pts[ipt].Hits.size(); ++ii)
2421  newTj.Pts[ipt].UseHit[ii] = false;
2422  newTj.Pts[ipt].Chg = 0;
2423  } // ipt
2424  SetEndPoints(newTj);
2425  newTj.MCSMom = MCSMom(slc, newTj);
2426  UpdateTjChgProperties("ST", slc, newTj, prt);
2427  if (splittingMuon) SetPDGCode(slc, newTj);
2428  if (ivx < slc.vtxs.size()) newTj.VtxID[0] = slc.vtxs[ivx].ID;
2429  newTj.AlgMod[kSplit] = true;
2430  newTj.ParentID = 0;
2431  slc.tjs.push_back(newTj);
2432 
2433  if (prt) {
2434  mf::LogVerbatim("TC") << " newTj T" << newTj.ID << " EndPts " << newTj.EndPt[0] << " to "
2435  << newTj.EndPt[1];
2436  }
2437  return true;
2438 
2439  } // SplitTraj
2440 
2441  //////////////////////////////////////////
2442  void
2444  TrajPoint const& tp,
2445  Trajectory const& tj,
2446  unsigned short& closePt,
2447  float& minSep)
2448  {
2449  // Finds the point, ipt, on trajectory tj that is closest to trajpoint tp
2450  float best = minSep * minSep;
2451  closePt = USHRT_MAX;
2452  float dw, dt, dp2;
2453  unsigned short ipt;
2454  for (ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
2455  dw = tj.Pts[ipt].Pos[0] - tp.Pos[0];
2456  dt = tj.Pts[ipt].Pos[1] - tp.Pos[1];
2457  dp2 = dw * dw + dt * dt;
2458  if (dp2 < best) {
2459  best = dp2;
2460  closePt = ipt;
2461  }
2462  } // ipt
2463  minSep = sqrt(best);
2464  } // TrajPointTrajDOCA
2465 
2466  //////////////////////////////////////////
2467  bool
2468  TrajTrajDOCA(const TCSlice& slc,
2469  const Trajectory& tj1,
2470  const Trajectory& tj2,
2471  unsigned short& ipt1,
2472  unsigned short& ipt2,
2473  float& minSep)
2474  {
2475  return TrajTrajDOCA(slc, tj1, tj2, ipt1, ipt2, minSep, false);
2476  } // TrajTrajDOCA
2477 
2478  //////////////////////////////////////////
2479  bool
2480  TrajTrajDOCA(const TCSlice& slc,
2481  const Trajectory& tj1,
2482  const Trajectory& tj2,
2483  unsigned short& ipt1,
2484  unsigned short& ipt2,
2485  float& minSep,
2486  bool considerDeadWires)
2487  {
2488  // Find the Distance Of Closest Approach between two trajectories less than minSep
2489  // start with some rough cuts to minimize the use of the more expensive checking. This
2490  // function returns true if the DOCA is less than minSep
2491  for (unsigned short iwt = 0; iwt < 2; ++iwt) {
2492  // Apply box cuts on the ends of the trajectories
2493  // The Lo/Hi wire(time) at each end of tj1
2494  float wt0 = tj1.Pts[tj1.EndPt[0]].Pos[iwt];
2495  float wt1 = tj1.Pts[tj1.EndPt[1]].Pos[iwt];
2496  float lowt1 = wt0;
2497  float hiwt1 = wt1;
2498  if (wt1 < lowt1) {
2499  lowt1 = wt1;
2500  hiwt1 = wt0;
2501  }
2502  // The Lo/Hi wire(time) at each end of tj2
2503  wt0 = tj2.Pts[tj2.EndPt[0]].Pos[iwt];
2504  wt1 = tj2.Pts[tj2.EndPt[1]].Pos[iwt];
2505  float lowt2 = wt0;
2506  float hiwt2 = wt1;
2507  if (wt1 < lowt2) {
2508  lowt2 = wt1;
2509  hiwt2 = wt0;
2510  }
2511  // Check for this configuration
2512  // loWire1.......hiWire1 minSep loWire2....hiWire2
2513  // loTime1.......hiTime1 minSep loTime2....hiTime2
2514  if (lowt2 > hiwt1 + minSep) return false;
2515  // and the other
2516  if (lowt1 > hiwt2 + minSep) return false;
2517  } // iwt
2518 
2519  float best = minSep * minSep;
2520  ipt1 = 0;
2521  ipt2 = 0;
2522  float dwc = 0;
2523  bool isClose = false;
2524  for (unsigned short i1 = tj1.EndPt[0]; i1 < tj1.EndPt[1] + 1; ++i1) {
2525  for (unsigned short i2 = tj2.EndPt[0]; i2 < tj2.EndPt[1] + 1; ++i2) {
2526  if (considerDeadWires) dwc = DeadWireCount(slc, tj1.Pts[i1], tj2.Pts[i2]);
2527  float dw = tj1.Pts[i1].Pos[0] - tj2.Pts[i2].Pos[0] - dwc;
2528  if (std::abs(dw) > minSep) continue;
2529  float dt = tj1.Pts[i1].Pos[1] - tj2.Pts[i2].Pos[1];
2530  if (std::abs(dt) > minSep) continue;
2531  float dp2 = dw * dw + dt * dt;
2532  if (dp2 < best) {
2533  best = dp2;
2534  ipt1 = i1;
2535  ipt2 = i2;
2536  isClose = true;
2537  }
2538  } // i2
2539  } // i1
2540  minSep = sqrt(best);
2541  return isClose;
2542  } // TrajTrajDOCA
2543 
2544  //////////////////////////////////////////
2545  float
2546  HitSep2(const TCSlice& slc, unsigned int iht, unsigned int jht)
2547  {
2548  // returns the separation^2 between two hits in WSE units
2549  if (iht > slc.slHits.size() - 1 || jht > slc.slHits.size() - 1) return 1E6;
2550  auto& ihit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
2551  auto& jhit = (*evt.allHits)[slc.slHits[jht].allHitsIndex];
2552  float dw = (float)ihit.WireID().Wire - (float)jhit.WireID().Wire;
2553  float dt = (ihit.PeakTime() - jhit.PeakTime()) * tcc.unitsPerTick;
2554  return dw * dw + dt * dt;
2555  } // HitSep2
2556 
2557  //////////////////////////////////////////
2558  unsigned short
2559  CloseEnd(const TCSlice& slc, const Trajectory& tj, const Point2_t& pos)
2560  {
2561  unsigned short endPt = tj.EndPt[0];
2562  auto& tp0 = tj.Pts[endPt];
2563  endPt = tj.EndPt[1];
2564  auto& tp1 = tj.Pts[endPt];
2565  if (PosSep2(tp0.Pos, pos) < PosSep2(tp1.Pos, pos)) return 0;
2566  return 1;
2567  } // CloseEnd
2568 
2569  //////////////////////////////////////////
2570  float
2571  PointTrajSep2(float wire, float time, TrajPoint const& tp)
2572  {
2573  float dw = wire - tp.Pos[0];
2574  float dt = time - tp.Pos[1];
2575  return dw * dw + dt * dt;
2576  }
2577 
2578  //////////////////////////////////////////
2579  float
2580  PointTrajDOCA(const TCSlice& slc, unsigned int iht, TrajPoint const& tp)
2581  {
2582  if (iht > slc.slHits.size() - 1) return 1E6;
2583  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
2584  float wire = hit.WireID().Wire;
2585  float time = hit.PeakTime() * tcc.unitsPerTick;
2586  return sqrt(PointTrajDOCA2(slc, wire, time, tp));
2587  } // PointTrajDOCA
2588 
2589  //////////////////////////////////////////
2590  float
2591  PointTrajDOCA(const TCSlice& slc, float wire, float time, TrajPoint const& tp)
2592  {
2593  return sqrt(PointTrajDOCA2(slc, wire, time, tp));
2594  } // PointTrajDOCA
2595 
2596  //////////////////////////////////////////
2597  float
2598  PointTrajDOCA2(const TCSlice& slc, float wire, float time, TrajPoint const& tp)
2599  {
2600  // returns the distance of closest approach squared between a (wire, time(WSE)) point
2601  // and a trajectory point
2602 
2603  double t = (double)(wire - tp.Pos[0]) * tp.Dir[0] + (double)(time - tp.Pos[1]) * tp.Dir[1];
2604  double dw = tp.Pos[0] + t * tp.Dir[0] - wire;
2605  double dt = tp.Pos[1] + t * tp.Dir[1] - time;
2606  return (float)(dw * dw + dt * dt);
2607 
2608  } // PointTrajDOCA2
2609 
2610  //////////////////////////////////////////
2611  void
2613  {
2614  TrajIntersection(tp1, tp2, pos[0], pos[1]);
2615  } // TrajIntersection
2616  //////////////////////////////////////////
2617  void
2618  TrajIntersection(TrajPoint const& tp1, TrajPoint const& tp2, float& x, float& y)
2619  {
2620  // returns the intersection position, (x,y), of two trajectory points
2621 
2622  x = -9999;
2623  y = -9999;
2624 
2625  double arg1 = tp1.Pos[0] * tp1.Dir[1] - tp1.Pos[1] * tp1.Dir[0];
2626  double arg2 = tp2.Pos[0] * tp1.Dir[1] - tp2.Pos[1] * tp1.Dir[0];
2627  double arg3 = tp2.Dir[0] * tp1.Dir[1] - tp2.Dir[1] * tp1.Dir[0];
2628  if (arg3 == 0) return;
2629  double s = (arg1 - arg2) / arg3;
2630 
2631  x = (float)(tp2.Pos[0] + s * tp2.Dir[0]);
2632  y = (float)(tp2.Pos[1] + s * tp2.Dir[1]);
2633 
2634  } // TrajIntersection
2635 
2636  //////////////////////////////////////////
2637  float
2638  MaxTjLen(const TCSlice& slc, std::vector<int>& tjIDs)
2639  {
2640  // returns the length of the longest Tj in the supplied list
2641  if (tjIDs.empty()) return 0;
2642  float maxLen = 0;
2643  for (auto tjid : tjIDs) {
2644  if (tjid < 1 || tjid > (int)slc.tjs.size()) continue;
2645  auto& tj = slc.tjs[tjid - 1];
2646  float sep2 = PosSep2(tj.Pts[tj.EndPt[0]].Pos, tj.Pts[tj.EndPt[1]].Pos);
2647  if (sep2 > maxLen) maxLen = sep2;
2648  } // tj
2649  return sqrt(maxLen);
2650  } // MaxTjLen
2651 
2652  //////////////////////////////////////////
2653  float
2655  {
2656  float len = 0, dx, dy;
2657  unsigned short ipt;
2658  unsigned short prevPt = tj.EndPt[0];
2659  for (ipt = tj.EndPt[0] + 1; ipt < tj.EndPt[1] + 1; ++ipt) {
2660  if (tj.Pts[ipt].Chg == 0) continue;
2661  dx = tj.Pts[ipt].Pos[0] - tj.Pts[prevPt].Pos[0];
2662  dy = tj.Pts[ipt].Pos[1] - tj.Pts[prevPt].Pos[1];
2663  len += sqrt(dx * dx + dy * dy);
2664  prevPt = ipt;
2665  }
2666  return len;
2667  } // TrajLength
2668 
2669  //////////////////////////////////////////
2670  float
2671  PosSep(const Point2_t& pos1, const Point2_t& pos2)
2672  {
2673  return sqrt(PosSep2(pos1, pos2));
2674  } // PosSep
2675 
2676  //////////////////////////////////////////
2677  float
2678  PosSep2(const Point2_t& pos1, const Point2_t& pos2)
2679  {
2680  // returns the separation distance^2 between two positions
2681  float d0 = pos1[0] - pos2[0];
2682  float d1 = pos1[1] - pos2[1];
2683  return d0 * d0 + d1 * d1;
2684  } // PosSep2
2685 
2686  //////////////////////////////////////////
2687  float
2688  TrajPointSeparation(const TrajPoint& tp1, const TrajPoint& tp2)
2689  {
2690  // Returns the separation distance between two trajectory points
2691  float dx = tp1.Pos[0] - tp2.Pos[0];
2692  float dy = tp1.Pos[1] - tp2.Pos[1];
2693  return sqrt(dx * dx + dy * dy);
2694  } // TrajPointSeparation
2695 
2696  //////////////////////////////////////////
2697  bool
2698  TrajClosestApproach(Trajectory const& tj, float x, float y, unsigned short& closePt, float& DOCA)
2699  {
2700  // find the closest approach between a trajectory tj and a point (x,y). Returns
2701  // the index of the closest trajectory point and the distance. Returns false if none
2702  // of the points on the tj are within DOCA
2703 
2704  float close2 = DOCA * DOCA;
2705  closePt = 0;
2706  bool foundClose = false;
2707 
2708  for (unsigned short ipt = tj.EndPt[0]; ipt < tj.EndPt[1] + 1; ++ipt) {
2709  if (tj.Pts[ipt].Chg == 0) continue;
2710  float dx = tj.Pts[ipt].Pos[0] - x;
2711  if (std::abs(dx) > DOCA) continue;
2712  float dy = tj.Pts[ipt].Pos[1] - y;
2713  if (std::abs(dy) > DOCA) continue;
2714  float sep2 = dx * dx + dy * dy;
2715  if (sep2 < close2) {
2716  close2 = sep2;
2717  closePt = ipt;
2718  foundClose = true;
2719  }
2720  } // ipt
2721 
2722  DOCA = sqrt(close2);
2723  return foundClose;
2724 
2725  } // TrajClosestApproach
2726 
2727  /////////////////////////////////////////
2728  float
2729  TwoTPAngle(const TrajPoint& tp1, const TrajPoint& tp2)
2730  {
2731  // Calculates the angle of a line between two TPs
2732  float dw = tp2.Pos[0] - tp1.Pos[0];
2733  float dt = tp2.Pos[1] - tp1.Pos[1];
2734  return atan2(dw, dt);
2735  } // TwoTPAngle
2736 
2737  ////////////////////////////////////////////////
2738  std::vector<unsigned int>
2739  PutHitsInVector(const TCSlice& slc, PFPStruct const& pfp, HitStatus_t hitRequest)
2740  {
2741  // Put hits with the assn P -> TP3D -> TP -> Hit into a vector
2742  std::vector<unsigned int> hitVec;
2743  if (pfp.TP3Ds.empty()) return hitVec;
2744 
2745  for (auto& tp3d : pfp.TP3Ds) {
2746  if (tp3d.IsBad) continue;
2747  if (tp3d.TjID <= 0) continue;
2748  auto& tp = slc.tjs[tp3d.TjID - 1].Pts[tp3d.TPIndex];
2749  for (unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
2750  unsigned int iht = tp.Hits[ii];
2751  bool useit = (hitRequest == kAllHits);
2752  if (tp.UseHit[ii] && hitRequest == kUsedHits) useit = true;
2753  if (!tp.UseHit[ii] && hitRequest == kUnusedHits) useit = true;
2754  if (useit) hitVec.push_back(iht);
2755  }
2756  } // tp3d
2757  return hitVec;
2758  } // PutHitsInVector
2759 
2760  ////////////////////////////////////////////////
2761  std::vector<unsigned int>
2763  {
2764  // Put hits (which are indexed into slHits) in each trajectory point into a flat vector
2765  std::vector<unsigned int> hitVec;
2766 
2767  // special handling for shower trajectories. UseHit isn't valid
2768  if (tj.AlgMod[kShowerTj]) {
2769  for (auto& tp : tj.Pts)
2770  hitVec.insert(hitVec.end(), tp.Hits.begin(), tp.Hits.end());
2771  return hitVec;
2772  } // shower Tj
2773 
2774  // reserve under the assumption that there will be one hit per point
2775  hitVec.reserve(tj.Pts.size());
2776  for (unsigned short ipt = 0; ipt < tj.Pts.size(); ++ipt) {
2777  for (unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
2778  unsigned int iht = tj.Pts[ipt].Hits[ii];
2779  bool useit = (hitRequest == kAllHits);
2780  if (tj.Pts[ipt].UseHit[ii] && hitRequest == kUsedHits) useit = true;
2781  if (!tj.Pts[ipt].UseHit[ii] && hitRequest == kUnusedHits) useit = true;
2782  if (useit) hitVec.push_back(iht);
2783  } // iht
2784  } // ipt
2785  return hitVec;
2786  } // PutTrajHitsInVector
2787 
2788  //////////////////////////////////////////
2789  void
2790  TagJunkTj(TCSlice& slc, Trajectory& tj, bool prt)
2791  {
2792  // Characterizes the trajectory as a junk tj even though it may not
2793  // have been reconstructed in FindJunkTraj. The distinguishing feature is
2794  // that it is short and has many used hits in each trajectory point.
2795 
2796  // Don't bother if it is too long
2797  if (tj.Pts.size() > 10) return;
2798  if (tj.PDGCode == 111) return;
2799  // count the number of points that have many used hits
2800  unsigned short nhm = 0;
2801  unsigned short npwc = 0;
2802  for (auto& tp : tj.Pts) {
2803  if (tp.Chg == 0) continue;
2804  ++npwc;
2805  unsigned short nused = 0;
2806  for (unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
2807  if (tp.UseHit[ii]) ++nused;
2808  } // ii
2809  if (nused > 3) ++nhm;
2810  } // tp
2811  // Set the junkTj bit if most of the hits are used in most of the tps
2812  if (nhm > 0.5 * npwc) tj.AlgMod[kJunkTj] = true;
2813  if (prt)
2814  mf::LogVerbatim("TC") << "TGT: T" << tj.ID << " npwc " << npwc << " nhm " << nhm << " junk? "
2815  << tj.AlgMod[kJunkTj];
2816  } // TagJunkTj
2817 
2818  //////////////////////////////////////////
2819  bool
2820  HasDuplicateHits(const TCSlice& slc, Trajectory const& tj, bool prt)
2821  {
2822  // returns true if a hit is associated with more than one TP
2823  auto tjHits = PutTrajHitsInVector(tj, kAllHits);
2824  for (unsigned short ii = 0; ii < tjHits.size() - 1; ++ii) {
2825  for (unsigned short jj = ii + 1; jj < tjHits.size(); ++jj) {
2826  if (tjHits[ii] == tjHits[jj]) {
2827  if (prt)
2828  mf::LogVerbatim("TC") << "HDH: Hit " << PrintHit(slc.slHits[ii]) << " is a duplicate "
2829  << ii << " " << jj;
2830  return true;
2831  }
2832  } // jj
2833  } // ii
2834  return false;
2835  } // HasDuplicateHits
2836 
2837  //////////////////////////////////////////
2838  void
2839  MoveTPToWire(TrajPoint& tp, float wire)
2840  {
2841  // Project TP to a "wire position" Pos[0] and update Pos[1]
2842  if (tp.Dir[0] == 0) return;
2843  float dw = wire - tp.Pos[0];
2844  if (std::abs(dw) < 0.01) return;
2845  tp.Pos[0] = wire;
2846  tp.Pos[1] += dw * tp.Dir[1] / tp.Dir[0];
2847  } // MoveTPToWire
2848 
2849  //////////////////////////////////////////
2850  std::vector<unsigned int>
2852  std::array<int, 2> const& wireWindow,
2853  Point2_t const& timeWindow,
2854  const unsigned short plane,
2855  HitStatus_t hitRequest,
2856  bool usePeakTime,
2857  bool& hitsNear)
2858  {
2859  // returns a vector of hits that are within the Window[Pos0][Pos1] in plane.
2860  // Note that hits on wire wireWindow[1] are returned as well. The definition of close
2861  // depends on setting of usePeakTime. If UsePeakTime is true, a hit is considered nearby if
2862  // the PeakTime is within the window. This is shown schematically here where
2863  // the time is on the horizontal axis and a "-" denotes a valid entry
2864  // timeWindow -----------------
2865  // hit PeakTime + close
2866  // hit PeakTime + not close
2867  // If usePeakTime is false, a hit is considered nearby if the hit StartTick and EndTick overlap with the timeWindow
2868  // Time window ---------
2869  // Hit StartTick-EndTick -------- close
2870  // Hit StartTick - EndTick -------- not close
2871 
2872  hitsNear = false;
2873  std::vector<unsigned int> closeHits;
2874  if (plane > slc.firstWire.size() - 1) return closeHits;
2875  // window in the wire coordinate
2876  int loWire = wireWindow[0];
2877  if (loWire < (int)slc.firstWire[plane]) loWire = slc.firstWire[plane];
2878  int hiWire = wireWindow[1];
2879  if (hiWire > (int)slc.lastWire[plane] - 1) hiWire = slc.lastWire[plane] - 1;
2880  // window in the time coordinate
2881  float minTick = timeWindow[0] / tcc.unitsPerTick;
2882  float maxTick = timeWindow[1] / tcc.unitsPerTick;
2883  for (int wire = loWire; wire <= hiWire; ++wire) {
2884  // Set hitsNear if the wire is dead
2885  if (!evt.goodWire[plane][wire]) hitsNear = true;
2886  if (slc.wireHitRange[plane][wire].first == UINT_MAX) continue;
2887  unsigned int firstHit = slc.wireHitRange[plane][wire].first;
2888  unsigned int lastHit = slc.wireHitRange[plane][wire].second;
2889  for (unsigned int iht = firstHit; iht <= lastHit; ++iht) {
2890  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
2891  if (usePeakTime) {
2892  if (hit.PeakTime() < minTick) continue;
2893  if (hit.PeakTime() > maxTick) break;
2894  }
2895  else {
2896  int hiLo = minTick;
2897  if (hit.StartTick() > hiLo) hiLo = hit.StartTick();
2898  int loHi = maxTick;
2899  if (hit.EndTick() < loHi) loHi = hit.EndTick();
2900  if (loHi < hiLo) continue;
2901  if (hiLo > loHi) break;
2902  }
2903  hitsNear = true;
2904  bool takeit = (hitRequest == kAllHits);
2905  if (hitRequest == kUsedHits && slc.slHits[iht].InTraj > 0) takeit = true;
2906  if (hitRequest == kUnusedHits && slc.slHits[iht].InTraj == 0) takeit = true;
2907  if (takeit) closeHits.push_back(iht);
2908  } // iht
2909  } // wire
2910  return closeHits;
2911  } // FindCloseHits
2912 
2913  //////////////////////////////////////////
2914  bool
2915  FindCloseHits(TCSlice& slc, TrajPoint& tp, float const& maxDelta, HitStatus_t hitRequest)
2916  {
2917  // Fills tp.Hits sets tp.UseHit true for hits that are close to tp.Pos. Returns true if there are
2918  // close hits OR if the wire at this position is dead
2919 
2920  tp.Hits.clear();
2921  tp.UseHit.reset();
2922  tp.Environment.reset();
2923  if (!WireHitRangeOK(slc, tp.CTP)) { return false; }
2924 
2925  if (tp.Pos[0] < -0.4) return false;
2926  unsigned short plane = DecodeCTP(tp.CTP).Plane;
2927  unsigned int wire = std::nearbyint(tp.Pos[0]);
2928  if (wire < slc.firstWire[plane]) return false;
2929  if (wire > slc.lastWire[plane] - 1) return false;
2930 
2931  // dead wire
2932  if (!evt.goodWire[plane][wire]) {
2933  tp.Environment[kEnvNotGoodWire] = true;
2934  return true;
2935  }
2936  tp.Environment[kEnvNotGoodWire] = false;
2937  // live wire with no hits
2938  if (slc.wireHitRange[plane][wire].first == UINT_MAX) return false;
2939 
2940  unsigned int firstHit = slc.wireHitRange[plane][wire].first;
2941  unsigned int lastHit = slc.wireHitRange[plane][wire].second;
2942 
2943  float fwire = wire;
2944  for (unsigned int iht = firstHit; iht <= lastHit; ++iht) {
2945  if ((unsigned int)slc.slHits[iht].InTraj > slc.tjs.size()) continue;
2946  bool useit = (hitRequest == kAllHits);
2947  if (hitRequest == kUsedHits && slc.slHits[iht].InTraj > 0) useit = true;
2948  if (hitRequest == kUnusedHits && slc.slHits[iht].InTraj == 0) useit = true;
2949  if (!useit) continue;
2950  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
2951  float ftime = tcc.unitsPerTick * hit.PeakTime();
2952  float delta = PointTrajDOCA(slc, fwire, ftime, tp);
2953  if (delta < maxDelta) tp.Hits.push_back(iht);
2954  } // iht
2955  if (tp.Hits.size() > 16) { tp.Hits.resize(16); }
2956  // Set UseHit false. The calling routine should decide if these hits should be used
2957  tp.UseHit.reset();
2958  return (!tp.Hits.empty());
2959 
2960  } // FindCloseHits
2961 
2962  //////////////////////////////////////////
2963  unsigned short
2964  NearbyCleanPt(const TCSlice& slc, const Trajectory& tj, unsigned short end)
2965  {
2966  // Searches for a TP near the end (or beginnin) that doesn't have the kEnvOverlap bit set
2967  // with the intent that a fit of a vertex position using this tj will be minimally
2968  // biased if there are no nearby hits from other tjs. A search is done from the
2969  // supplied nearPt moving in the + direction if nearPt == tj.EndPt[0] and moving in
2970  // the - direction if nearPt == tj.EndPt[1]
2971  if (end > 1) return USHRT_MAX;
2972  short dir = 1;
2973  if (end == 1) dir = -1;
2974  for (short ii = 0; ii < (short)tj.Pts.size(); ++ii) {
2975  short ipt = tj.EndPt[end] + dir * ii;
2976  if (ipt < 0 || ipt >= (short)tj.Pts.size()) return USHRT_MAX;
2977  auto& tp = tj.Pts[ipt];
2978  if (!tp.Environment[kEnvOverlap]) return ipt;
2979  } // ii
2980  return tj.EndPt[end];
2981  } // FindCleanPt
2982 
2983  //////////////////////////////////////////
2984  std::vector<int>
2985  FindCloseTjs(const TCSlice& slc,
2986  const TrajPoint& fromTp,
2987  const TrajPoint& toTp,
2988  const float& maxDelta)
2989  {
2990  // Returns a list of Tj IDs that have hits within distance maxDelta on a line drawn between the two Tps as shown
2991  // graphically here, where a "*" is a Tp and "|" and "-" are the boundaries of the region that is checked
2992  //
2993  // ---------------
2994  // | |
2995  // * *
2996  // | |
2997  // ---------------
2998  // If the wire positions of fromTp and toTp are the same, a different region is checked as shown here
2999  //
3000  // -----------
3001  // | |
3002  // | * |
3003  // | |
3004  // -----------
3005 
3006  std::vector<int> tmp;
3007  if (fromTp.Pos[0] < -0.4 || toTp.Pos[0] < -0.4) return tmp;
3008 
3009  TrajPoint tp;
3010  // Make the tp so that stepping is positive
3011  unsigned int firstWire, lastWire;
3012  if (toTp.Pos[0] > fromTp.Pos[0]) {
3013  if (!MakeBareTrajPoint(slc, fromTp, toTp, tp)) return tmp;
3014  firstWire = std::nearbyint(fromTp.Pos[0]);
3015  lastWire = std::nearbyint(toTp.Pos[0]);
3016  }
3017  else if (toTp.Pos[0] < fromTp.Pos[0]) {
3018  if (!MakeBareTrajPoint(slc, toTp, fromTp, tp)) return tmp;
3019  firstWire = std::nearbyint(toTp.Pos[0]);
3020  lastWire = std::nearbyint(fromTp.Pos[0]);
3021  }
3022  else {
3023  tp.Pos = fromTp.Pos;
3024  float tmp = fromTp.Pos[0] - maxDelta;
3025  if (tmp < 0) tmp = 0;
3026  firstWire = std::nearbyint(tmp);
3027  tmp = fromTp.Pos[0] + maxDelta;
3028  lastWire = std::nearbyint(tmp);
3029  }
3030 
3031  unsigned short plane = DecodeCTP(tp.CTP).Plane;
3032 
3033  if (firstWire < slc.firstWire[plane]) firstWire = slc.firstWire[plane];
3034  if (firstWire > slc.lastWire[plane] - 1) return tmp;
3035  if (lastWire < slc.firstWire[plane]) return tmp;
3036  if (lastWire > slc.lastWire[plane] - 1) lastWire = slc.lastWire[plane] - 1;
3037 
3038  for (unsigned int wire = firstWire; wire <= lastWire; ++wire) {
3039  if (slc.wireHitRange[plane][wire].first == UINT_MAX) continue;
3040  MoveTPToWire(tp, (float)wire);
3041  // Find the tick range at this position
3042  float minTick = (tp.Pos[1] - maxDelta) / tcc.unitsPerTick;
3043  float maxTick = (tp.Pos[1] + maxDelta) / tcc.unitsPerTick;
3044  unsigned int firstHit = slc.wireHitRange[plane][wire].first;
3045  unsigned int lastHit = slc.wireHitRange[plane][wire].second;
3046  for (unsigned int iht = firstHit; iht <= lastHit; ++iht) {
3047  if (slc.slHits[iht].InTraj <= 0) continue;
3048  if ((unsigned int)slc.slHits[iht].InTraj > slc.tjs.size()) continue;
3049  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
3050  if (hit.PeakTime() < minTick) continue;
3051  // Hits are sorted by increasing time so we can break when maxTick is reached
3052  if (hit.PeakTime() > maxTick) break;
3053  if (std::find(tmp.begin(), tmp.end(), slc.slHits[iht].InTraj) != tmp.end()) continue;
3054  tmp.push_back(slc.slHits[iht].InTraj);
3055  } // iht
3056  } // wire
3057 
3058  return tmp;
3059 
3060  } // FindCloseTjs
3061 
3062  ////////////////////////////////////////////////
3063  float
3065  Trajectory& tj1,
3066  unsigned short end1,
3067  Trajectory& tj2,
3068  unsigned short end2,
3069  unsigned short nPtsFit,
3070  bool useChg,
3071  bool prt)
3072  {
3073  // returns the significance of a potential kink between the ends of two trajectories. This
3074  // is used when deciding to either merge trajectories or make a vertex between them
3075 
3076  if (tj1.CTP != tj2.CTP) return -1;
3077  if (end1 > 1 || end2 > 1) return -1;
3078 
3079  // construct a temporary trajectory to allow using the standard KinkSignificance function.
3080  // The first nPtsFit points are comprised of TPs from tj1 and the last nPtsFits points are from tj2
3081  Trajectory tj;
3082  tj.ID = 666;
3083  tj.CTP = tj1.CTP;
3084  short dir = 1;
3085  if (end1 == 1) dir = -1;
3086  unsigned short cnt = 0;
3087  // add tj1 points to the trajectory
3088  for (short ii = 0; ii < (short)tj1.Pts.size(); ++ii) {
3089  short ipt = tj1.EndPt[end1] + dir * ii;
3090  if (ipt < 0) break;
3091  if (ipt >= (short)tj1.Pts.size()) break;
3092  auto& tp = tj1.Pts[ipt];
3093  if (tp.Chg <= 0) continue;
3094  tj.Pts.push_back(tp);
3095  ++cnt;
3096  if (cnt == nPtsFit + 1) break;
3097  } // ipt
3098  if (cnt < nPtsFit) return -1;
3099  // add tj2 points to the trajectory
3100  dir = 1;
3101  if (end2 == 1) dir = -1;
3102  cnt = 0;
3103  for (short ii = 0; ii < (short)tj2.Pts.size(); ++ii) {
3104  short ipt = tj2.EndPt[end2] + dir * ii;
3105  if (ipt < 0) break;
3106  if (ipt >= (short)tj2.Pts.size()) break;
3107  auto& tp = tj2.Pts[ipt];
3108  if (tp.Chg <= 0) continue;
3109  tj.Pts.push_back(tp);
3110  ++cnt;
3111  if (cnt == nPtsFit + 1) break;
3112  } // ipt
3113  tj.EndPt[0] = 0;
3114  tj.EndPt[1] = tj.Pts.size() - 1;
3115  return KinkSignificance(slc, tj, nPtsFit, nPtsFit, useChg, prt);
3116  } // KinkSignificance
3117 
3118  ////////////////////////////////////////////////
3119  float
3121  Trajectory& tj,
3122  unsigned short kinkPt,
3123  unsigned short nPtsFit,
3124  bool useChg,
3125  bool prt)
3126  {
3127  // returns a kink significance in the trajectory at the presumed kink point kinkPt
3128  // using angle and (optional) charge asymmetry. The returned value is negative if there is insufficient
3129  // information.
3130  //
3131  // Check the limits
3132  if (kinkPt < tj.EndPt[0] + 2) return -1;
3133  if (kinkPt > tj.EndPt[1] - 2) return -1;
3134 
3135  // This function requires knowledge of the DOF of the line fit
3136  if (nPtsFit < 3) return -1;
3137  unsigned short npwc = NumPtsWithCharge(slc, tj, false);
3138  // need enough points to do a fit on each sideof the presumed kink point
3139  if (npwc < 2 * nPtsFit + 1) return -1;
3140 
3141  // The hit charge uncertainty is 0.12 - 0.15 (neglecting 2ndry interactions) for hadrons.
3142  // This translates into an error on the charge
3143  // asymmetry of about 0.07, or about 0.6 * the charge uncertainty
3144  double chgRMS = 0.07;
3145  // An additional contribution to the rms is the dependence on the DOF of the fit.
3146  // Apply a factor to the significance similar to (and simpler than) the Students t-distribution
3147  // This will increase the angle and charge error rms by 1.3 (1.05) when nPtsFit = 3 (8)
3148  double tFactor = 1 + 0.3 / double(nPtsFit - 2);
3149  chgRMS *= tFactor;
3150 
3151  // Fit the trajectory direction on the + side
3152  short fitDir = 1;
3153  TrajPoint tpPos;
3154  FitTraj(slc, tj, kinkPt, nPtsFit, fitDir, tpPos);
3155  if (tpPos.FitChi > 900) return -1;
3156  // repeat the trajectory fit on the - side
3157  fitDir = -1;
3158  TrajPoint tpNeg;
3159  FitTraj(slc, tj, kinkPt, nPtsFit, fitDir, tpNeg);
3160  if (tpNeg.FitChi > 900) return -1;
3161  double angErr = tpNeg.AngErr;
3162  if (tpPos.AngErr > angErr) angErr = tpPos.AngErr;
3163  angErr *= tFactor;
3164  double dang = DeltaAngle(tpPos.Ang, tpNeg.Ang);
3165  double dangSig = dang / angErr;
3166 
3167  double chgAsym = 0;
3168  double chgSig = 0;
3169  if (useChg) {
3170  // Sum the charge Neg and Pos, excluding the kinkPt
3171  double chgNeg = 0;
3172  unsigned short cntNeg = 0;
3173  for (unsigned short ipt = kinkPt - 1; ipt >= tj.EndPt[0]; --ipt) {
3174  auto& tp = tj.Pts[ipt];
3175  if (tp.Chg <= 0) continue;
3176  chgNeg += tp.Chg;
3177  ++cntNeg;
3178  if (cntNeg == nPtsFit) break;
3179  if (ipt == 0) break;
3180  } // ipt
3181  if (cntNeg != nPtsFit) {
3182  if (prt) mf::LogVerbatim("TC") << " KL: Bad cntNeg " << cntNeg << " != " << nPtsFit;
3183  return -1;
3184  }
3185  // now Pos
3186  double chgPos = 0;
3187  unsigned short cntPos = 0;
3188  for (unsigned short ipt = kinkPt + 1; ipt <= tj.EndPt[1]; ++ipt) {
3189  auto& tp = tj.Pts[ipt];
3190  if (tp.Chg <= 0) continue;
3191  chgPos += tp.Chg;
3192  ++cntPos;
3193  if (cntPos == nPtsFit) break;
3194  } // ipt
3195  if (cntPos != nPtsFit) {
3196  if (prt) mf::LogVerbatim("TC") << " KL: Bad cntPos " << cntPos << " != " << nPtsFit;
3197  return -1;
3198  }
3199  chgNeg /= (float)nPtsFit;
3200  chgPos /= (float)nPtsFit;
3201  // The charge asymmetry varies between 0 and 1;
3202  chgAsym = std::abs(chgPos - chgNeg) / (chgPos + chgNeg);
3203  // calculate the charge asymmetry significance
3204  chgSig = chgAsym / chgRMS;
3205  } // useChg
3206  double kinkSig = sqrt(dangSig * dangSig + chgSig * chgSig);
3207 
3208  if (prt) {
3209  mf::LogVerbatim myprt("TC");
3210  myprt << "KL: T" << tj.ID << " kinkPt " << PrintPos(slc, tj.Pts[kinkPt]);
3211  myprt << " nPtsFit " << nPtsFit;
3212  myprt << " dang " << std::fixed << std::setprecision(3) << dang;
3213  myprt << std::fixed << std::setprecision(3) << " angErr " << angErr;
3214  myprt << std::setprecision(2) << " sig " << dangSig;
3215  myprt << " chgAsym " << chgAsym;
3216  myprt << " chgSig " << chgSig;
3217  myprt << " kinkSig " << kinkSig;
3218  }
3219  return (float)kinkSig;
3220  } // KinkSignificance
3221 
3222  ////////////////////////////////////////////////
3223  float
3224  ElectronLikelihood(const TCSlice& slc, const Trajectory& tj)
3225  {
3226  // returns a number between 0 (not electron-like) and 1 (electron-like)
3227  if (NumPtsWithCharge(slc, tj, false) < 8) return -1;
3228  if (tj.EndFlag[0][kBragg] || tj.EndFlag[1][kBragg]) return 0;
3229 
3230  unsigned short midPt = 0.5 * (tj.EndPt[0] + tj.EndPt[1]);
3231  double rms0 = 0, rms1 = 0;
3232  unsigned short cnt;
3233  TjDeltaRMS(slc, tj, tj.EndPt[0], midPt, rms0, cnt);
3234  TjDeltaRMS(slc, tj, midPt, tj.EndPt[1], rms1, cnt);
3235  float asym = std::abs(rms0 - rms1) / (rms0 + rms1);
3236  float chgFact = (tj.ChgRMS - 0.1) * 5;
3237  float elh = 5 * asym * chgFact;
3238  if (elh > 1) elh = 1;
3239  return elh;
3240  } // ElectronLikelihood
3241 
3242  ////////////////////////////////////////////////
3243  float
3244  ChgFracNearPos(const TCSlice& slc, const Point2_t& pos, const std::vector<int>& tjIDs)
3245  {
3246  // returns the fraction of the charge in the region around pos that is associated with
3247  // the list of Tj IDs
3248  if (tjIDs.empty()) return 0;
3249  std::array<int, 2> wireWindow;
3250  Point2_t timeWindow;
3251  // 1/2 size of the region
3252  constexpr float NNDelta = 5;
3253  wireWindow[0] = pos[0] - NNDelta;
3254  wireWindow[1] = pos[0] + NNDelta;
3255  timeWindow[0] = pos[1] - NNDelta;
3256  timeWindow[1] = pos[1] + NNDelta;
3257  // do some checking
3258  for (auto& tjID : tjIDs)
3259  if (tjID <= 0 || tjID > (int)slc.tjs.size()) return 0;
3260  // Determine which plane we are in
3261  geo::PlaneID planeID = DecodeCTP(slc.tjs[tjIDs[0] - 1].CTP);
3262  // get a list of all hits in this region
3263  bool hitsNear;
3264  std::vector<unsigned int> closeHits =
3265  FindCloseHits(slc, wireWindow, timeWindow, planeID.Plane, kAllHits, true, hitsNear);
3266  if (closeHits.empty()) return 0;
3267  float chg = 0;
3268  float tchg = 0;
3269  // Add the hit charge in the box
3270  // All hits in the box, and all hits associated with the Tjs
3271  for (auto& iht : closeHits) {
3272  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
3273  chg += hit.Integral();
3274  if (slc.slHits[iht].InTraj == 0) continue;
3275  if (std::find(tjIDs.begin(), tjIDs.end(), slc.slHits[iht].InTraj) != tjIDs.end())
3276  tchg += hit.Integral();
3277  } // iht
3278  if (chg == 0) return 0;
3279  return tchg / chg;
3280  } // ChgFracNearPos
3281 
3282  ////////////////////////////////////////////////
3283  float
3285  {
3286  float delta, md = 0;
3287  unsigned short ii;
3288  unsigned int iht;
3289  for (auto& tp : tj.Pts) {
3290  for (ii = 0; ii < tp.Hits.size(); ++ii) {
3291  if (!tp.UseHit[ii]) continue;
3292  iht = tp.Hits[ii];
3293  delta = PointTrajDOCA(slc, iht, tp);
3294  if (delta > md) md = delta;
3295  } // ii
3296  } // pts
3297  return md;
3298  } // MaxHitDelta
3299 
3300  //////////////////////////////////////////
3301  void
3303  {
3304  // reverse the trajectory
3305  if (tj.Pts.empty()) return;
3306  // reverse the crawling direction flag
3307  tj.StepDir = -tj.StepDir;
3308  // Vertices
3309  std::swap(tj.VtxID[0], tj.VtxID[1]);
3310  // trajectory points
3311  std::reverse(tj.Pts.begin(), tj.Pts.end());
3312  // reverse the stop flag
3313  std::reverse(tj.EndFlag.begin(), tj.EndFlag.end());
3314  std::swap(tj.dEdx[0], tj.dEdx[1]);
3315  // reverse the direction vector on all points
3316  for (unsigned short ipt = 0; ipt < tj.Pts.size(); ++ipt) {
3317  if (tj.Pts[ipt].Dir[0] != 0) tj.Pts[ipt].Dir[0] = -tj.Pts[ipt].Dir[0];
3318  if (tj.Pts[ipt].Dir[1] != 0) tj.Pts[ipt].Dir[1] = -tj.Pts[ipt].Dir[1];
3319  if (tj.Pts[ipt].Ang > 0) { tj.Pts[ipt].Ang -= M_PI; }
3320  else {
3321  tj.Pts[ipt].Ang += M_PI;
3322  }
3323  } // ipt
3324  if (tj.StartEnd == 0 || tj.StartEnd == 1) tj.StartEnd = 1 - tj.StartEnd;
3325  SetEndPoints(tj);
3326  // UpdateMatchStructs(slc, tj.ID, tj.ID);
3327  } // ReverseTraj
3328 
3329  //////////////////////////////////////////
3330  bool
3331  PointInsideEnvelope(const Point2_t& Point, const std::vector<Point2_t>& Envelope)
3332  {
3333  // returns true if the Point is within the Envelope polygon. Entries in Envelope are the
3334  // Pos[0], Pos[1] locations of the polygon vertices. This is based on the algorithm that the
3335  // sum of the angles of a vector between a point and the vertices will be 2 * pi for an interior
3336  // point and 0 for an exterior point
3337 
3338  Point2_t p1, p2;
3339  unsigned short nvx = Envelope.size();
3340  double angleSum = 0;
3341  for (unsigned short ii = 0; ii < Envelope.size(); ++ii) {
3342  p1[0] = Envelope[ii][0] - Point[0];
3343  p1[1] = Envelope[ii][1] - Point[1];
3344  p2[0] = Envelope[(ii + 1) % nvx][0] - Point[0];
3345  p2[1] = Envelope[(ii + 1) % nvx][1] - Point[1];
3346  angleSum += DeltaAngle(p1, p2);
3347  }
3348  if (abs(angleSum) < M_PI) return false;
3349  return true;
3350 
3351  } // InsideEnvelope
3352 
3353  //////////////////////////////////////////
3354  bool
3355  SetMag(Vector2_t& v1, double mag)
3356  {
3357  double den = v1[0] * v1[0] + v1[1] * v1[1];
3358  if (den == 0) return false;
3359  den = sqrt(den);
3360 
3361  v1[0] *= mag / den;
3362  v1[1] *= mag / den;
3363  return true;
3364  } // SetMag
3365 
3366  ////////////////////////////////////////////////
3367  void
3368  FindAlongTrans(Point2_t pos1, Vector2_t dir1, Point2_t pos2, Point2_t& alongTrans)
3369  {
3370  // Calculate the distance along and transverse to the direction vector dir1 from pos1 to pos2
3371  alongTrans[0] = 0;
3372  alongTrans[1] = 0;
3373  if (pos1[0] == pos2[0] && pos1[1] == pos2[1]) return;
3374  pos1[0] = pos2[0] - pos1[0];
3375  pos1[1] = pos2[1] - pos1[1];
3376  double sep = sqrt(pos1[0] * pos1[0] + pos1[1] * pos1[1]);
3377  if (sep < 1E-6) return;
3378  Vector2_t ptDir;
3379  ptDir[0] = pos1[0] / sep;
3380  ptDir[1] = pos1[1] / sep;
3381  SetMag(dir1, 1.0);
3382  double costh = DotProd(dir1, ptDir);
3383  if (costh > 1.0 || costh < -1.0) return;
3384  alongTrans[0] = costh * sep;
3385  double sinth = sqrt(1 - costh * costh);
3386  alongTrans[1] = sinth * sep;
3387  } // FindAlongTrans
3388 
3389  //////////////////////////////////////////
3390  double
3391  DeltaAngle(const Point2_t& p1, const Point2_t& p2)
3392  {
3393  // angle between two points
3394  double ang1 = atan2(p1[1], p1[0]);
3395  double ang2 = atan2(p2[1], p2[0]);
3396  return DeltaAngle2(ang1, ang2);
3397  } // DeltaAngle
3398 
3399  //////////////////////////////////////////
3400  double
3401  DeltaAngle2(double Ang1, double Ang2)
3402  {
3403  constexpr double twopi = 2 * M_PI;
3404  double dang = Ang1 - Ang2;
3405  while (dang > M_PI)
3406  dang -= twopi;
3407  while (dang < -M_PI)
3408  dang += twopi;
3409  return dang;
3410  }
3411 
3412  //////////////////////////////////////////
3413  double
3414  DeltaAngle(double Ang1, double Ang2)
3415  {
3416  return std::abs(std::remainder(Ang1 - Ang2, M_PI));
3417  }
3418 
3419  ////////////////////////////////////////////////
3420  void
3422  {
3423  // Find the first (last) TPs, EndPt[0] (EndPt[1], that have charge
3424 
3425  // don't mess with showerTjs or halo tjs
3426  if (tj.AlgMod[kShowerTj] || tj.AlgMod[kHaloTj]) return;
3427 
3428  tj.EndPt[0] = 0;
3429  tj.EndPt[1] = 0;
3430  if (tj.Pts.size() == 0) return;
3431 
3432  // check the end point pointers
3433  for (unsigned short ipt = 0; ipt < tj.Pts.size(); ++ipt) {
3434  if (tj.Pts[ipt].Chg != 0) {
3435  tj.EndPt[0] = ipt;
3436  break;
3437  }
3438  }
3439  for (unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
3440  unsigned short ipt = tj.Pts.size() - 1 - ii;
3441  if (tj.Pts[ipt].Chg != 0) {
3442  tj.EndPt[1] = ipt;
3443  break;
3444  }
3445  }
3446  } // SetEndPoints
3447 
3448  ////////////////////////////////////////////////
3449  bool
3450  TrajIsClean(TCSlice& slc, Trajectory& tj, bool prt)
3451  {
3452  // Returns true if the trajectory has low hit multiplicity and is in a
3453  // clean environment
3454  unsigned short nUsed = 0;
3455  unsigned short nTotHits = 0;
3456  for (unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
3457  TrajPoint& tp = tj.Pts[ipt];
3458  nTotHits += tp.Hits.size();
3459  for (unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
3460  if (tp.UseHit[ii]) ++nUsed;
3461  } // ii
3462  } // ipt
3463  if (nTotHits == 0) return false;
3464  float fracUsed = (float)nUsed / (float)nTotHits;
3465  if (prt)
3466  mf::LogVerbatim("TC") << "TrajIsClean: nTotHits " << nTotHits << " nUsed " << nUsed
3467  << " fracUsed " << fracUsed;
3468 
3469  if (fracUsed > 0.9) return true;
3470  return false;
3471 
3472  } // TrajIsClean
3473 
3474  ////////////////////////////////////////////////
3475  short
3476  MCSMom(const TCSlice& slc, const std::vector<int>& tjIDs)
3477  {
3478  // Find the average MCSMom of the trajectories
3479  if (tjIDs.empty()) return 0;
3480  float summ = 0;
3481  float suml = 0;
3482  for (auto tjid : tjIDs) {
3483  auto& tj = slc.tjs[tjid - 1];
3484  float npts = tj.EndPt[1] - tj.EndPt[0] + 1;
3485  summ += npts * tj.MCSMom;
3486  suml += npts;
3487  } // tjid
3488  return (short)(summ / suml);
3489  } // MCSMom
3490 
3491  ////////////////////////////////////////////////
3492  short
3493  MCSMom(const TCSlice& slc, const Trajectory& tj)
3494  {
3495  return MCSMom(slc, tj, tj.EndPt[0], tj.EndPt[1]);
3496  } // MCSMom
3497 
3498  ////////////////////////////////////////////////
3499  short
3500  MCSMom(const TCSlice& slc, const Trajectory& tj, unsigned short firstPt, unsigned short lastPt)
3501  {
3502  // Estimate the trajectory momentum using Multiple Coulomb Scattering ala PDG RPP
3503 
3504  if (firstPt == lastPt) return 0;
3505  if (firstPt > lastPt) std::swap(firstPt, lastPt);
3506 
3507  firstPt = NearestPtWithChg(slc, tj, firstPt);
3508  lastPt = NearestPtWithChg(slc, tj, lastPt);
3509  if (firstPt >= lastPt) return 0;
3510 
3511  if (firstPt < tj.EndPt[0]) return 0;
3512  if (lastPt > tj.EndPt[1]) return 0;
3513  // Can't do this with only 2 points
3514  if (NumPtsWithCharge(slc, tj, false, firstPt, lastPt) < 3) return 0;
3515  // Ignore junk Tjs
3516  if (tj.AlgMod[kJunkTj]) return 0;
3517 
3518  double tjLen = TrajPointSeparation(tj.Pts[firstPt], tj.Pts[lastPt]);
3519  if (tjLen < 1) return 0;
3520  // mom calculated in MeV
3521  double thetaRMS = MCSThetaRMS(slc, tj, firstPt, lastPt);
3522  if (thetaRMS < 0.001) return 999;
3523  double mom = 13.8 * sqrt(tjLen / 14) / thetaRMS;
3524  if (mom > 999) mom = 999;
3525  return (short)mom;
3526  } // MCSMom
3527 
3528  ////////////////////////////////////////////////
3529  unsigned short
3530  NearestPtWithChg(const TCSlice& slc, const Trajectory& tj, unsigned short thePt)
3531  {
3532  // returns a point near thePt which has charge
3533  if (thePt > tj.EndPt[1]) return thePt;
3534  if (tj.Pts[thePt].Chg > 0) return thePt;
3535 
3536  short endPt0 = tj.EndPt[0];
3537  short endPt1 = tj.EndPt[1];
3538  for (short off = 1; off < 10; ++off) {
3539  short ipt = thePt + off;
3540  if (ipt <= endPt1 && tj.Pts[ipt].Chg > 0) return (unsigned short)ipt;
3541  ipt = thePt - off;
3542  if (ipt >= endPt0 && tj.Pts[ipt].Chg > 0) return (unsigned short)ipt;
3543  } // off
3544  return thePt;
3545  } // NearestPtWithChg
3546 
3547  /////////////////////////////////////////
3548  float
3549  MCSThetaRMS(const TCSlice& slc, const Trajectory& tj)
3550  {
3551  // This returns the MCS scattering angle expected for one WSE unit of travel along the trajectory.
3552  // It is used to define kink and vertex cuts. This should probably be named something different to
3553  // prevent confusion
3554 
3555  float tps = TrajPointSeparation(tj.Pts[tj.EndPt[0]], tj.Pts[tj.EndPt[1]]);
3556  if (tps < 1) return 1;
3557 
3558  return MCSThetaRMS(slc, tj, tj.EndPt[0], tj.EndPt[1]) / sqrt(tps);
3559 
3560  } // MCSThetaRMS
3561 
3562  /////////////////////////////////////////
3563  double
3564  MCSThetaRMS(const TCSlice& slc,
3565  const Trajectory& tj,
3566  unsigned short firstPt,
3567  unsigned short lastPt)
3568  {
3569  // This returns the MCS scattering angle expected for the length of the trajectory
3570  // spanned by firstPt to lastPt. It is used primarily to calculate MCSMom
3571 
3572  if (firstPt < tj.EndPt[0]) return 1;
3573  if (lastPt > tj.EndPt[1]) return 1;
3574 
3575  firstPt = NearestPtWithChg(slc, tj, firstPt);
3576  lastPt = NearestPtWithChg(slc, tj, lastPt);
3577  if (firstPt >= lastPt) return 1;
3578 
3579  double sigmaS;
3580  unsigned short cnt;
3581  TjDeltaRMS(slc, tj, firstPt, lastPt, sigmaS, cnt);
3582  if (sigmaS < 0) return 1;
3583  double tjLen = TrajPointSeparation(tj.Pts[firstPt], tj.Pts[lastPt]);
3584  if (tjLen < 1) return 1;
3585  // Theta_o = 4 * sqrt(3) * sigmaS / path
3586  return (6.8 * sigmaS / tjLen);
3587 
3588  } // MCSThetaRMS
3589 
3590  /////////////////////////////////////////
3591  void
3592  TjDeltaRMS(const TCSlice& slc,
3593  const Trajectory& tj,
3594  unsigned short firstPt,
3595  unsigned short lastPt,
3596  double& rms,
3597  unsigned short& cnt)
3598  {
3599  // returns the rms scatter of points around a line formed by the firstPt and lastPt of the trajectory
3600 
3601  rms = -1;
3602  if (firstPt < tj.EndPt[0]) return;
3603  if (lastPt > tj.EndPt[1]) return;
3604 
3605  firstPt = NearestPtWithChg(slc, tj, firstPt);
3606  lastPt = NearestPtWithChg(slc, tj, lastPt);
3607  if (firstPt >= lastPt) return;
3608 
3609  TrajPoint tmp;
3610  // make a bare trajectory point to define a line between firstPt and lastPt.
3611  // Use the position of the hits at these points
3612  TrajPoint firstTP = tj.Pts[firstPt];
3613  firstTP.Pos = firstTP.HitPos;
3614  TrajPoint lastTP = tj.Pts[lastPt];
3615  lastTP.Pos = lastTP.HitPos;
3616  if (!MakeBareTrajPoint(slc, firstTP, lastTP, tmp)) return;
3617  // sum up the deviations^2
3618  double dsum = 0;
3619  cnt = 0;
3620  for (unsigned short ipt = firstPt + 1; ipt < lastPt; ++ipt) {
3621  if (tj.Pts[ipt].Chg == 0) continue;
3622  // ignore points with large error
3623  if (tj.Pts[ipt].HitPosErr2 > 4) continue;
3624  dsum += PointTrajDOCA2(slc, tj.Pts[ipt].HitPos[0], tj.Pts[ipt].HitPos[1], tmp);
3625  ++cnt;
3626  } // ipt
3627  if (cnt < 2) return;
3628  rms = sqrt(dsum / (double)cnt);
3629 
3630  } // TjDeltaRMS
3631 
3632  /////////////////////////////////////////
3633  void
3635  {
3636  // This function is called after tj reconstruction is completed to set TP Environment
3637  // bits that are dependent on reconstruction, just kEnvNearMuon for now. This bit is
3638  // set for all TPs that are within 5 wire-equivalents of a muon
3639 
3640  std::array<int, 2> wireWindow;
3641  Point2_t timeWindow;
3642  unsigned short plane = DecodeCTP(inCTP).Plane;
3643  //
3644  float delta = 5;
3645 
3646  for (auto& mutj : slc.tjs) {
3647  if (mutj.AlgMod[kKilled]) continue;
3648  if (mutj.CTP != inCTP) continue;
3649  if (mutj.PDGCode != 13) continue;
3650  unsigned short nnear = 0;
3651  for (unsigned short ipt = mutj.EndPt[0]; ipt <= mutj.EndPt[1]; ++ipt) {
3652  auto& tp = mutj.Pts[ipt];
3653  wireWindow[0] = tp.Pos[0];
3654  wireWindow[1] = tp.Pos[0];
3655  timeWindow[0] = tp.Pos[1] - delta;
3656  timeWindow[1] = tp.Pos[1] + delta;
3657  // get a list of all hits in this region
3658  bool hitsNear;
3659  auto closeHits =
3660  FindCloseHits(slc, wireWindow, timeWindow, plane, kAllHits, true, hitsNear);
3661  if (closeHits.empty()) continue;
3662  for (auto iht : closeHits) {
3663  auto inTraj = slc.slHits[iht].InTraj;
3664  if (inTraj <= 0) continue;
3665  if (inTraj == mutj.ID) continue;
3666  auto& dtj = slc.tjs[inTraj - 1];
3667  if (dtj.PDGCode == 13) continue;
3668  for (unsigned short jpt = dtj.EndPt[0]; jpt <= dtj.EndPt[1]; ++jpt) {
3669  auto& dtp = dtj.Pts[jpt];
3670  if (std::find(dtp.Hits.begin(), dtp.Hits.end(), iht) == dtp.Hits.end()) continue;
3671  dtp.Environment[kEnvNearMuon] = true;
3672  ++nnear;
3673  } // jpt
3674  } // iht
3675  } // ipt
3676  } // mutj
3677  } // SetTPEnvironment
3678 
3679  /////////////////////////////////////////
3680  void
3681  UpdateTjChgProperties(std::string inFcnLabel, TCSlice& slc, Trajectory& tj, bool prt)
3682  {
3683  // Updates properties of the tj that are affected when the TP environment
3684  // is changed. The most likely reason for a change is when the tj is attached to a
3685  // vertex in which case the Environment kEnvOverlap bit may be set by the UpdateVxEnvironment
3686  // function in which case this function is called.
3687  if (tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) return;
3688 
3689  // first (un)set some bits
3690  for (auto& tp : tj.Pts) {
3691  if (tp.Chg <= 0) continue;
3692  tp.Environment[kEnvUnusedHits] = false;
3693  for (unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
3694  if (tp.UseHit[ii]) continue;
3695  unsigned int iht = tp.Hits[ii];
3696  if (slc.slHits[iht].InTraj == 0) tp.Environment[kEnvUnusedHits] = true;
3697  } // ii
3698  } // tp
3699 
3700  // Update the tj charge variables. The concept is explained by this graphic where
3701  // each column is a wire, Q = a TP with charge, q = a TP with charge that is an
3702  // EnvOverlap region, x = a wire that has a TP with Chg = 0 or a wire that has no TP
3703  // because the wire is dead, o = an EnvOverlap region, V = vertex attached to end. You should
3704  // imagine that all 3 tjs come from the same vertex
3705  // 01234567890123456789 npwc cnt range
3706  // VooooQQQQxxxQQQ 7 7 0 - 14
3707  // VqqqqQQQQxxxQQQQQQQQ 16 12 0 - 19
3708  // VooQQQ 3 3 0 - 5
3709  // The average is first calculated using Ave = sum(Q) / npwc
3710  // TotChg is calculated using
3711  tj.TotChg = 0;
3712  tj.AveChg = 0;
3713  tj.ChgRMS = 0.5;
3714 
3715  // These variables are used to calculate the average and rms using valid points with charge
3716  double vcnt = 0;
3717  double vsum = 0;
3718  double vsum2 = 0;
3719  // Reject a single large charge TP
3720  float bigChg = 0;
3721  for (unsigned short ipt = tj.EndPt[0] + 1; ipt < tj.EndPt[1]; ++ipt) {
3722  auto& tp = tj.Pts[ipt];
3723  if (tp.Chg > bigChg) bigChg = tp.Chg;
3724  } // ipt
3725  // variables for calculating the backup quanties. These are only used if npwc < 3
3726  double bcnt = 0;
3727  double bsum = 0;
3728  double bsum2 = 0;
3729  // don't include the end points
3730  for (unsigned short ipt = tj.EndPt[0] + 1; ipt < tj.EndPt[1]; ++ipt) {
3731  auto& tp = tj.Pts[ipt];
3732  if (tp.Chg <= 0) continue;
3733  // ignore the single large charge TP
3734  if (tp.Chg == bigChg) continue;
3735  // accumulate a backup sum in case most of the points are overlapped. Note that
3736  // tp.Chg has an angle correction, which is why the hit integral is summed
3737  // below. We don't care about this detail for the backup sum
3738  bsum += tp.Chg;
3739  bsum2 += tp.Chg * tp.Chg;
3740  if (tp.Chg > bigChg) bigChg = tp.Chg;
3741  ++bcnt;
3742  // Skip TPs that overlap with TPs on other Tjs. A correction will be made below
3743  if (tj.Pts[ipt].Environment[kEnvOverlap]) continue;
3744  ++vcnt;
3745  double tpchg = 0;
3746  for (unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
3747  if (!tp.UseHit[ii]) continue;
3748  unsigned int iht = tp.Hits[ii];
3749  tpchg += (*evt.allHits)[slc.slHits[iht].allHitsIndex].Integral();
3750  } // ii
3751  vsum += tpchg;
3752  vsum2 += tpchg * tpchg;
3753  } // ipt
3754 
3755  if (bcnt == 0) return;
3756 
3757  if (vcnt < 3) {
3758  // use the backup sum
3759  tj.TotChg = bsum;
3760  tj.AveChg = bsum / bcnt;
3761  if (vcnt > 2) {
3762  double arg = bsum2 - bcnt * tj.AveChg * tj.AveChg;
3763  if (arg > 0) tj.ChgRMS = sqrt(arg / (bcnt - 1));
3764  }
3765  for (auto& tp : tj.Pts)
3766  tp.AveChg = tj.AveChg;
3767  if (prt)
3768  mf::LogVerbatim("TC") << inFcnLabel << ".UpdateTjChgProperties: backup sum Set tj.AveChg "
3769  << (int)tj.AveChg << " ChgRMS " << tj.ChgRMS;
3770  return;
3771  } // low npwc
3772 
3773  double nWires = tj.EndPt[1] - tj.EndPt[0] + 1;
3774  if (nWires < 2) return;
3775  // correct for wires missing near vertices.
3776  // Count the number of wires between vertices at the ends and the first wire
3777  // that has charge. This code assumes that there should be one TP on each wire
3778  if (!tj.AlgMod[kPhoton]) {
3779  for (unsigned short end = 0; end < 2; ++end) {
3780  if (tj.VtxID[end] == 0) continue;
3781  auto& tp = tj.Pts[tj.EndPt[end]];
3782  auto& vx2 = slc.vtxs[tj.VtxID[end] - 1];
3783  int dw = std::abs(tp.Pos[0] - vx2.Pos[0]);
3784  // This assumes that the vertex is not inside the wire boundaries of the tj
3785  nWires += dw;
3786  } // end
3787  } // not a photon Tj
3788 
3789  tj.AveChg = vsum / vcnt;
3790  // calculate the total charge using the tj wire range
3791  tj.TotChg = nWires * tj.AveChg;
3792  // calculate the rms
3793  double arg = vsum2 - vcnt * tj.AveChg * tj.AveChg;
3794  double rms = 0.5;
3795  if (arg > 0) rms = sqrt(arg / (vcnt - 1));
3796  rms /= tj.AveChg;
3797  // don't let it be an unrealistically low value. It could be crazy large however.
3798  if (rms < 0.1) rms = 0.1;
3799  // Don't let the calculated charge RMS dominate until it is well known; after there are 5 - 10 valid TPs.
3800  // Set the starting charge rms = 0.5
3801  if (vcnt < 10) {
3802  double defFrac = 1 / vcnt;
3803  rms = defFrac * 0.5 + (1 - defFrac) * rms;
3804  }
3805  tj.ChgRMS = rms;
3806  if (prt)
3807  mf::LogVerbatim("TC") << inFcnLabel << ".UpdateTjChgProperties: Set tj.AveChg "
3808  << (int)tj.AveChg << " ChgRMS " << tj.ChgRMS;
3809 
3810  // Update the TP charge pulls.
3811  // Don't let the calculated charge RMS dominate the default
3812  // RMS until it is well known. Start with 50% error on the
3813  // charge RMS
3814  for (unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
3815  auto& tp = tj.Pts[ipt];
3816  if (tp.Chg <= 0) continue;
3817  tp.ChgPull = (tp.Chg / tj.AveChg - 1) / tj.ChgRMS;
3818  } // ipt
3819 
3820  // update the local charge average using NPtsAve of the preceding points.
3821  // Handle short Tjs first.
3822  if (vcnt < tcc.nPtsAve) {
3823  for (auto& tp : tj.Pts)
3824  tp.AveChg = tj.AveChg;
3825  return;
3826  }
3827 
3828  // Set the local average to 0 first
3829  for (auto& tp : tj.Pts)
3830  tp.AveChg = 0;
3831  // Enter the local average on the points where an average can be calculated
3832  unsigned short nptsave = tcc.nPtsAve;
3833  unsigned short minPt = tj.EndPt[0] + nptsave;
3834  float lastAve = 0;
3835  for (unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
3836  unsigned short ipt = tj.EndPt[1] - ii;
3837  if (ipt < minPt) break;
3838  float cnt = 0;
3839  float sum = 0;
3840  for (unsigned short iii = 0; iii < nptsave; ++iii) {
3841  unsigned short iipt = ipt - iii;
3842  // Don't include the charge of the first point
3843  if (iipt == tj.EndPt[0]) break;
3844  auto& tp = tj.Pts[iipt];
3845  if (tp.Chg <= 0) continue;
3846  sum += tp.Chg;
3847  ++cnt;
3848  } // iii
3849  if (cnt > 2) {
3850  tj.Pts[ipt].AveChg = sum / cnt;
3851  lastAve = tj.Pts[ipt].AveChg;
3852  }
3853  } // ii
3854  // Fill in the points where no average was calculated
3855  for (unsigned short ii = tj.EndPt[0]; ii <= tj.EndPt[1]; ++ii) {
3856  unsigned short ipt = tj.EndPt[1] - ii;
3857  auto& tp = tj.Pts[ipt];
3858  if (tp.AveChg == 0) { tp.AveChg = lastAve; }
3859  else {
3860  lastAve = tp.AveChg;
3861  }
3862  } // ii
3863 
3864  tj.NeedsUpdate = false;
3865 
3866  } // UpdateTjChgProperties
3867 
3868  /////////////////////////////////////////
3869  void
3871  {
3872  // Set the kEnvOverlap bit true for all TPs that are close to other
3873  // trajectories that are close to vertices. The positions of TPs that
3874  // overlap are biased and shouldn't be used in a vertex fit. Also, these
3875  // TPs shouldn't be used to calculate dE/dx. The kEnvOverlap bit is first cleared
3876  // for ALL TPs and then set for ALL 2D vertices
3877 
3878  for (auto& tj : slc.tjs) {
3879  if (tj.AlgMod[kKilled]) continue;
3880  for (auto& tp : tj.Pts)
3881  tp.Environment[kEnvOverlap] = false;
3882  } // tj
3883 
3884  for (auto& vx : slc.vtxs) {
3885  if (vx.ID <= 0) continue;
3886  UpdateVxEnvironment(slc, vx, false);
3887  } // vx
3888 
3889  } // UpdateVxEnvironment
3890 
3891  /////////////////////////////////////////
3892  void
3893  UpdateVxEnvironment(TCSlice& slc, VtxStore& vx2, bool prt)
3894  {
3895  // Update the Environment each TP on trajectories near the vertex
3896 
3897  if (vx2.ID == 0) return;
3898  if (vx2.Stat[kOnDeadWire]) return;
3899 
3900  if (prt) mf::LogVerbatim("TC") << "UpdateVxEnvironment check Tjs attached to vx2 " << vx2.ID;
3901 
3902  std::vector<int> tjlist;
3903  std::vector<unsigned short> tjends;
3904  if (vx2.Pos[0] < -0.4) return;
3905  unsigned int vxWire = std::nearbyint(vx2.Pos[0]);
3906  unsigned int loWire = vxWire;
3907  unsigned int hiWire = vxWire;
3908  for (auto& tj : slc.tjs) {
3909  if (tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) continue;
3910  if (tj.CTP != vx2.CTP) continue;
3911  // ignore photon Tjs
3912  if (tj.AlgMod[kPhoton]) continue;
3913  for (unsigned short end = 0; end < 2; ++end) {
3914  if (tj.VtxID[end] != vx2.ID) continue;
3915  tjlist.push_back(tj.ID);
3916  tjends.push_back(end);
3917  if (tj.Pts[tj.EndPt[end]].Pos[0] < -0.4) return;
3918  unsigned int endWire = std::nearbyint(tj.Pts[tj.EndPt[end]].Pos[0]);
3919  if (endWire < loWire) loWire = endWire;
3920  if (endWire > hiWire) hiWire = endWire;
3921  } // end
3922  } // tj
3923  if (tjlist.size() < 2) return;
3924  if (hiWire < loWire + 1) return;
3925  if (prt)
3926  mf::LogVerbatim("TC") << " check Tjs on wires in the range " << loWire << " to " << hiWire;
3927 
3928  // create a vector of TPs between loWire and hiWire for every tj in the list
3929  // wire TP
3930  std::vector<std::vector<TrajPoint>> wire_tjpt;
3931  // companion vector of IDs
3932  std::vector<int> tjids;
3933  // populate this vector with TPs on Tjs that are in this range
3934  unsigned short nwires = hiWire - loWire + 1;
3935  for (unsigned short itj = 0; itj < tjlist.size(); ++itj) {
3936  auto& tj = slc.tjs[tjlist[itj] - 1];
3937  unsigned short end = tjends[itj];
3938  std::vector<TrajPoint> tjpt(nwires);
3939  // first enter valid TPs in the range
3940  for (unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
3941  unsigned short ipt;
3942  if (end == 0) { ipt = tj.EndPt[0] + ii; }
3943  else {
3944  ipt = tj.EndPt[1] - ii;
3945  }
3946  if (ipt > tj.Pts.size() - 1) break;
3947  // Make a copy of the TP so we can alter it
3948  auto tp = tj.Pts[ipt];
3949  if (tp.Chg <= 0) continue;
3950  tp.Chg = 1;
3951  tp.Hits.clear();
3952  if (tp.Pos[0] < -0.4) continue;
3953  unsigned int wire = std::nearbyint(tp.Pos[0]);
3954  unsigned short indx = wire - loWire;
3955  if (indx > nwires - 1) break;
3956  tp.Step = ipt;
3957  // We will use NTPsFit to count the number of neighboring TPs
3958  tp.NTPsFit = 0;
3959  tjpt[indx] = tp;
3960  } // ii
3961  // next make TPs on the wires that don't have real TPs
3962  TrajPoint ltp;
3963  // put ltp at the vertex position with direction towards the end point
3964  MakeBareTrajPoint(vx2.Pos, tj.Pts[tj.EndPt[end]].Pos, ltp);
3965  if (ltp.Dir[0] == 0) continue;
3966  if (ltp.Pos[0] < -0.4) continue;
3967  unsigned int wire = std::nearbyint(ltp.Pos[0]);
3968  ltp.Chg = 0;
3969  unsigned short indx = wire - loWire;
3970  // Break if we found a real TP
3971  if (tjpt[indx].Chg == 0) tjpt[indx] = ltp;
3972  double stepSize = std::abs(1 / ltp.Dir[0]);
3973  for (unsigned short ii = 0; ii < nwires; ++ii) {
3974  // move the local TP position by one step in the right direction
3975  for (unsigned short iwt = 0; iwt < 2; ++iwt)
3976  ltp.Pos[iwt] += ltp.Dir[iwt] * stepSize;
3977  if (ltp.Pos[0] < -0.4) break;
3978  wire = std::nearbyint(ltp.Pos[0]);
3979  if (wire < loWire || wire > hiWire) break;
3980  indx = wire - loWire;
3981  if (tjpt[indx].Chg > 0) continue;
3982  tjpt[indx] = ltp;
3983  } // ii
3984  if (prt) {
3985  mf::LogVerbatim myprt("TC");
3986  myprt << " T" << tj.ID;
3987  for (auto& tp : tjpt)
3988  myprt << " " << PrintPos(slc, tp.Pos) << "_" << tp.Step << "_" << (int)tp.Chg;
3989  }
3990  wire_tjpt.push_back(tjpt);
3991  tjids.push_back(tj.ID);
3992  } // itj
3993 
3994  // iterate over the wires in the range
3995  for (unsigned short indx = 0; indx < nwires; ++indx) {
3996  // count the number of valid points on this wire
3997  unsigned short npts = 0;
3998  // count the number of points on this wire that have charge
3999  unsigned short npwc = 0;
4000  for (unsigned short itj = 0; itj < wire_tjpt.size(); ++itj) {
4001  if (wire_tjpt[itj][indx].Pos[0] == 0) continue;
4002  // found a valid point
4003  ++npts;
4004  if (wire_tjpt[itj][indx].Chg > 0) ++npwc;
4005  } // itj
4006  // no valid points
4007  if (npts == 0) continue;
4008  // all valid points have charge
4009  if (npwc == npts) continue;
4010  // re-find the valid points with charge and set the kEnvOverlap bit
4011  for (unsigned short itj = 0; itj < wire_tjpt.size(); ++itj) {
4012  if (wire_tjpt[itj][indx].Pos[0] == 0) continue;
4013  if (wire_tjpt[itj][indx].Chg == 0) continue;
4014  auto& tj = slc.tjs[tjids[itj] - 1];
4015  unsigned short ipt = wire_tjpt[itj][indx].Step;
4016  tj.Pts[ipt].Environment[kEnvOverlap] = true;
4017  tj.NeedsUpdate = true;
4018  if (prt) mf::LogVerbatim("TC") << " Set kEnvOverlap bit on T" << tj.ID << " ipt " << ipt;
4019  } // itj
4020  } // indx
4021 
4022  // update the charge rms for those tjs whose environment was changed above
4023  // (or elsewhere)
4024  for (auto tjid : tjids) {
4025  auto& tj = slc.tjs[tjid - 1];
4026  if (!tj.NeedsUpdate) continue;
4027  if (tj.CTP != vx2.CTP) continue;
4028  UpdateTjChgProperties("UVxE", slc, tj, prt);
4029  } // tjid
4030 
4031  } // UpdateVxEnvironment
4032 
4033  /////////////////////////////////////////
4034  TrajPoint
4036  const TCSlice& slc,
4037  const Point3_t& pos,
4038  CTP_t inCTP)
4039  {
4040  // A version to use when the 2D direction isn't required
4041  TrajPoint tp;
4042  tp.Pos = {{0, 0}};
4043  tp.Dir = {{0, 1}};
4044  tp.CTP = inCTP;
4045  geo::PlaneID planeID = DecodeCTP(inCTP);
4046 
4047  tp.Pos[0] = tcc.geom->WireCoordinate(pos[1], pos[2], planeID);
4048  tp.Pos[1] = detProp.ConvertXToTicks(pos[0], planeID) * tcc.unitsPerTick;
4049  return tp;
4050  } // MakeBareTP
4051 
4052  /////////////////////////////////////////
4053  TrajPoint
4055  const TCSlice& slc,
4056  const Point3_t& pos,
4057  const Vector3_t& dir,
4058  CTP_t inCTP)
4059  {
4060  // Projects the space point defined by pos and dir into the CTP and returns
4061  // it in the form of a trajectory point. The TP Pos[0] is set to a negative
4062  // number if the point has an invalid wire position but doesn't return an
4063  // error if the position is on a dead wire. The projection of the direction
4064  // vector in CTP is stored in tp.Delta.
4065  TrajPoint tp;
4066  tp.Pos = {{-1, 0}};
4067  tp.Dir = {{0, 1}};
4068  tp.CTP = inCTP;
4069  geo::PlaneID planeID = DecodeCTP(inCTP);
4070 
4071  tp.Pos[0] = tcc.geom->WireCoordinate(pos[1], pos[2], planeID);
4072  tp.Pos[1] = detProp.ConvertXToTicks(pos[0], planeID) * tcc.unitsPerTick;
4073 
4074  // now find the direction if dir is defined
4075  if (dir[0] == 0 && dir[1] == 0 && dir[2] == 0) return tp;
4076 
4077  // Make a point at the origin and one 100 units away
4078  Point3_t ori3 = {{0.0, 0.0, 0.0}};
4079  Point3_t pos3 = {{100 * dir[0], 100 * dir[1], 100 * dir[2]}};
4080  // 2D position of ori3 and the pos3 projection
4081  std::array<double, 2> ori2;
4082  std::array<double, 2> pos2;
4083  std::array<double, 2> dir2;
4084  // the wire coordinates
4085  ori2[0] = tcc.geom->WireCoordinate(ori3[1], ori3[2], planeID);
4086  pos2[0] = tcc.geom->WireCoordinate(pos3[1], pos3[2], planeID);
4087  // the time coordinates
4088  ori2[1] = detProp.ConvertXToTicks(ori3[0], planeID) * tcc.unitsPerTick;
4089  pos2[1] = detProp.ConvertXToTicks(pos3[0], planeID) * tcc.unitsPerTick;
4090 
4091  dir2[0] = pos2[0] - ori2[0];
4092  dir2[1] = pos2[1] - ori2[1];
4093 
4094  double norm = sqrt(dir2[0] * dir2[0] + dir2[1] * dir2[1]);
4095  tp.Dir[0] = dir2[0] / norm;
4096  tp.Dir[1] = dir2[1] / norm;
4097  tp.Ang = atan2(dir2[1], dir2[0]);
4098  tp.Delta = norm / 100;
4099 
4100  // The Orth vectors are not unit normalized so we need to correct for this
4101  double w0 = tcc.geom->WireCoordinate(0, 0, planeID);
4102  // cosine-like component
4103  double cs = tcc.geom->WireCoordinate(1, 0, planeID) - w0;
4104  // sine-like component
4105  double sn = tcc.geom->WireCoordinate(0, 1, planeID) - w0;
4106  norm = sqrt(cs * cs + sn * sn);
4107  tp.Delta /= norm;
4108 
4109  // Stasb dt/dWire in DeltaRMS. This is used in PFPUtils/FitSection to find the
4110  // distance along a 3D line given the wire number in a plane
4111  tp.DeltaRMS = 100 / (pos2[0] - ori2[0]);
4112  return tp;
4113 
4114  } // MakeBareTP
4115 
4116  /////////////////////////////////////////
4117  bool
4118  MakeBareTrajPoint(const TCSlice& slc, unsigned int fromHit, unsigned int toHit, TrajPoint& tp)
4119  {
4120  if (fromHit > slc.slHits.size() - 1) return false;
4121  if (toHit > slc.slHits.size() - 1) return false;
4122  auto& fhit = (*evt.allHits)[slc.slHits[fromHit].allHitsIndex];
4123  auto& thit = (*evt.allHits)[slc.slHits[toHit].allHitsIndex];
4124  CTP_t tCTP = EncodeCTP(fhit.WireID());
4125  return MakeBareTrajPoint(slc,
4126  (float)fhit.WireID().Wire,
4127  fhit.PeakTime(),
4128  (float)thit.WireID().Wire,
4129  thit.PeakTime(),
4130  tCTP,
4131  tp);
4132 
4133  } // MakeBareTrajPoint
4134 
4135  /////////////////////////////////////////
4136  bool
4138  float fromWire,
4139  float fromTick,
4140  float toWire,
4141  float toTick,
4142  CTP_t tCTP,
4143  TrajPoint& tp)
4144  {
4145  tp.CTP = tCTP;
4146  tp.Pos[0] = fromWire;
4147  tp.Pos[1] = tcc.unitsPerTick * fromTick;
4148  tp.Dir[0] = toWire - fromWire;
4149  tp.Dir[1] = tcc.unitsPerTick * (toTick - fromTick);
4150  double norm = sqrt(tp.Dir[0] * tp.Dir[0] + tp.Dir[1] * tp.Dir[1]);
4151  if (norm == 0) return false;
4152  tp.Dir[0] /= norm;
4153  tp.Dir[1] /= norm;
4154  tp.Ang = atan2(tp.Dir[1], tp.Dir[0]);
4155  return true;
4156  } // MakeBareTrajPoint
4157 
4158  /////////////////////////////////////////
4159  bool
4160  MakeBareTrajPoint(const Point2_t& fromPos, const Point2_t& toPos, TrajPoint& tpOut)
4161  {
4162  tpOut.Pos = fromPos;
4163  tpOut.Dir = PointDirection(fromPos, toPos);
4164  tpOut.Ang = atan2(tpOut.Dir[1], tpOut.Dir[0]);
4165  return true;
4166 
4167  } // MakeBareTrajPoint
4168 
4169  /////////////////////////////////////////
4170  bool
4172  const TrajPoint& tpIn1,
4173  const TrajPoint& tpIn2,
4174  TrajPoint& tpOut)
4175  {
4176  tpOut.CTP = tpIn1.CTP;
4177  tpOut.Pos = tpIn1.Pos;
4178  tpOut.Dir = PointDirection(tpIn1.Pos, tpIn2.Pos);
4179  tpOut.Ang = atan2(tpOut.Dir[1], tpOut.Dir[0]);
4180  return true;
4181  } // MakeBareTrajPoint
4182 
4183  ////////////////////////////////////////////////
4184  unsigned short
4185  FarEnd(TCSlice& slc, const Trajectory& tj, const Point2_t& pos)
4186  {
4187  // Returns the end (0 or 1) of the Tj that is furthest away from the position pos
4188  if (tj.ID == 0) return 0;
4189  if (PosSep2(tj.Pts[tj.EndPt[1]].Pos, pos) > PosSep2(tj.Pts[tj.EndPt[0]].Pos, pos)) return 1;
4190  return 0;
4191  } // FarEnd
4192 
4193  ////////////////////////////////////////////////
4194  Vector2_t
4195  PointDirection(const Point2_t p1, const Point2_t p2)
4196  {
4197  // Finds the direction vector between the two points from p1 to p2
4198  Vector2_t dir;
4199  for (unsigned short xyz = 0; xyz < 2; ++xyz)
4200  dir[xyz] = p2[xyz] - p1[xyz];
4201  if (dir[0] == 0 && dir[1] == 0) return dir;
4202  double norm = sqrt(dir[0] * dir[0] + dir[1] * dir[1]);
4203  dir[0] /= norm;
4204  dir[1] /= norm;
4205  return dir;
4206  } // PointDirection
4207 
4208  ////////////////////////////////////////////////
4209  float
4210  TPHitsRMSTime(const TCSlice& slc, const TrajPoint& tp, HitStatus_t hitRequest)
4211  {
4212  return tcc.unitsPerTick * TPHitsRMSTick(slc, tp, hitRequest);
4213  } // TPHitsRMSTime
4214 
4215  ////////////////////////////////////////////////
4216  float
4217  TPHitsRMSTick(const TCSlice& slc, const TrajPoint& tp, HitStatus_t hitRequest)
4218  {
4219  // Estimate the RMS of all hits associated with a trajectory point
4220  // without a lot of calculation. Note that this returns a value that is
4221  // closer to a FWHM, not the RMS
4222  if (tp.Hits.empty()) return 0;
4223  float minVal = 9999;
4224  float maxVal = 0;
4225  for (unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
4226  bool useit = (hitRequest == kAllHits);
4227  if (hitRequest == kUsedHits && tp.UseHit[ii]) useit = true;
4228  if (hitRequest == kUnusedHits && !tp.UseHit[ii]) useit = true;
4229  if (!useit) continue;
4230  unsigned int iht = tp.Hits[ii];
4231  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
4232  float cv = hit.PeakTime();
4233  float rms = hit.RMS();
4234  float arg = cv - rms;
4235  if (arg < minVal) minVal = arg;
4236  arg = cv + rms;
4237  if (arg > maxVal) maxVal = arg;
4238  } // ii
4239  if (maxVal == 0) return 0;
4240  return (maxVal - minVal) / 2;
4241  } // TPHitsRMSTick
4242 
4243  ////////////////////////////////////////////////
4244  float
4245  HitsRMSTime(const TCSlice& slc,
4246  const std::vector<unsigned int>& hitsInMultiplet,
4247  HitStatus_t hitRequest)
4248  {
4249  return tcc.unitsPerTick * HitsRMSTick(slc, hitsInMultiplet, hitRequest);
4250  } // HitsRMSTick
4251 
4252  ////////////////////////////////////////////////
4253  float
4254  HitsRMSTick(const TCSlice& slc,
4255  const std::vector<unsigned int>& hitsInMultiplet,
4256  HitStatus_t hitRequest)
4257  {
4258  if (hitsInMultiplet.empty()) return 0;
4259 
4260  if (hitsInMultiplet.size() == 1) {
4261  auto& hit = (*evt.allHits)[slc.slHits[hitsInMultiplet[0]].allHitsIndex];
4262  return hit.RMS();
4263  }
4264 
4265  float minVal = 9999;
4266  float maxVal = 0;
4267  for (unsigned short ii = 0; ii < hitsInMultiplet.size(); ++ii) {
4268  unsigned int iht = hitsInMultiplet[ii];
4269  bool useit = (hitRequest == kAllHits);
4270  if (hitRequest == kUsedHits && slc.slHits[iht].InTraj > 0) useit = true;
4271  if (hitRequest == kUnusedHits && slc.slHits[iht].InTraj == 0) useit = true;
4272  if (!useit) continue;
4273  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
4274  float cv = hit.PeakTime();
4275  float rms = hit.RMS();
4276  float arg = cv - rms;
4277  if (arg < minVal) minVal = arg;
4278  arg = cv + rms;
4279  if (arg > maxVal) maxVal = arg;
4280  } // ii
4281  if (maxVal == 0) return 0;
4282  return (maxVal - minVal) / 2;
4283  } // HitsRMSTick
4284 
4285  ////////////////////////////////////////////////
4286  float
4287  HitsPosTime(const TCSlice& slc,
4288  const std::vector<unsigned int>& hitsInMultiplet,
4289  float& sum,
4290  HitStatus_t hitRequest)
4291  {
4292  return tcc.unitsPerTick * HitsPosTick(slc, hitsInMultiplet, sum, hitRequest);
4293  } // HitsPosTime
4294 
4295  ////////////////////////////////////////////////
4296  float
4297  HitsPosTick(const TCSlice& slc,
4298  const std::vector<unsigned int>& hitsInMultiplet,
4299  float& sum,
4300  HitStatus_t hitRequest)
4301  {
4302  // returns the position and the charge
4303  float pos = 0;
4304  sum = 0;
4305  for (unsigned short ii = 0; ii < hitsInMultiplet.size(); ++ii) {
4306  unsigned int iht = hitsInMultiplet[ii];
4307  bool useit = (hitRequest == kAllHits);
4308  if (hitRequest == kUsedHits && slc.slHits[iht].InTraj > 0) useit = true;
4309  if (hitRequest == kUnusedHits && slc.slHits[iht].InTraj == 0) useit = true;
4310  if (!useit) continue;
4311  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
4312  float chg = hit.Integral();
4313  pos += chg * hit.PeakTime();
4314  sum += chg;
4315  } // ii
4316  if (sum <= 0) return -1;
4317  return pos / sum;
4318  } // HitsPosTick
4319 
4320  //////////////////////////////////////////
4321  unsigned short
4322  NumUsedHitsInTj(const TCSlice& slc, const Trajectory& tj)
4323  {
4324  if (tj.AlgMod[kKilled]) return 0;
4325  if (tj.Pts.empty()) return 0;
4326  unsigned short nhits = 0;
4327  for (auto& tp : tj.Pts) {
4328  for (unsigned short ii = 0; ii < tp.Hits.size(); ++ii)
4329  if (tp.UseHit[ii]) ++nhits;
4330  } // tp
4331  return nhits;
4332  } // NumHitsInTj
4333 
4334  //////////////////////////////////////////
4335  unsigned short
4336  NumHitsInTP(const TrajPoint& tp, HitStatus_t hitRequest)
4337  {
4338  // Counts the number of hits of the specified type in tp
4339  if (tp.Hits.empty()) return 0;
4340 
4341  if (hitRequest == kAllHits) return tp.Hits.size();
4342 
4343  unsigned short nhits = 0;
4344  for (unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
4345  if (hitRequest == kUsedHits) {
4346  if (tp.UseHit[ii]) ++nhits;
4347  }
4348  else {
4349  // looking for unused hits
4350  if (!tp.UseHit[ii]) ++nhits;
4351  }
4352  } // ii
4353  return nhits;
4354  } // NumHitsInTP
4355 
4356  ////////////////////////////////////////////////
4357  void
4358  SetPDGCode(TCSlice& slc, unsigned short itj)
4359  {
4360  if (itj > slc.tjs.size() - 1) return;
4361  SetPDGCode(slc, slc.tjs[itj]);
4362  }
4363 
4364  ////////////////////////////////////////////////
4365  void
4367  {
4368  // Sets the PDG code for the supplied trajectory. Note that the existing
4369  // PDG code is left unchanged if these cuts are not met
4370 
4371  short npwc = NumPtsWithCharge(slc, tj, false);
4372  if (npwc < 6) {
4373  tj.PDGCode = 0;
4374  return;
4375  }
4376 
4377  if (tj.Strategy[kStiffEl] && ElectronLikelihood(slc, tj) > tcc.showerTag[6]) {
4378  tj.PDGCode = 111;
4379  return;
4380  }
4381  if (tj.Strategy[kStiffMu]) {
4382  tj.PDGCode = 13;
4383  return;
4384  }
4385 
4386  if (tcc.showerTag[6] > 0 && ElectronLikelihood(slc, tj) > tcc.showerTag[6]) {
4387  tj.PDGCode = 11;
4388  return;
4389  }
4390 
4391  if (tcc.muonTag[0] <= 0) return;
4392  // Special handling of very long straight trajectories, e.g. uB cosmic rays
4393  bool isAMuon = (npwc > (unsigned short)tcc.muonTag[0] && tj.MCSMom > tcc.muonTag[1]);
4394  // anything really really long must be a muon
4395  if (npwc > 500) isAMuon = true;
4396  if (isAMuon) tj.PDGCode = 13;
4397 
4398  } // SetPDGCode
4399 
4400  ////////////////////////////////////////////////
4401  bool
4403  {
4404  // Find the average hit rms by analyzing the full hit collection. This
4405  // only needs to be done once per job.
4406 
4407  if ((*evt.allHits).empty()) return true;
4408  // no sense re-calculating it if it's been done
4409  if (evt.aveHitRMSValid) return true;
4410 
4411  unsigned short cstat = (*evt.allHits)[0].WireID().Cryostat;
4412  unsigned short tpc = (*evt.allHits)[0].WireID().TPC;
4413 
4414  unsigned short nplanes = tcc.geom->Nplanes(tpc, cstat);
4415  evt.aveHitRMS.resize(nplanes);
4416  std::vector<float> cnt(nplanes, 0);
4417  for (unsigned short iht = 0; iht < (*evt.allHits).size(); ++iht) {
4418  auto& hit = (*evt.allHits)[iht];
4419  unsigned short plane = hit.WireID().Plane;
4420  if (plane > nplanes - 1) return false;
4421  if (cnt[plane] > 200) continue;
4422  // require multiplicity one
4423  if (hit.Multiplicity() != 1) continue;
4424  // not-crazy Chisq/DOF
4425  if (hit.GoodnessOfFit() < 0 || hit.GoodnessOfFit() > 500) continue;
4426  // don't let a lot of runt hits screw up the calculation
4427  if (hit.PeakAmplitude() < 1) continue;
4428  evt.aveHitRMS[plane] += hit.RMS();
4429  ++cnt[plane];
4430  // quit if enough hits are found
4431  bool allDone = true;
4432  for (unsigned short plane = 0; plane < nplanes; ++plane)
4433  if (cnt[plane] < 200) allDone = false;
4434  if (allDone) break;
4435  } // iht
4436 
4437  // assume there are enough hits in each plane
4438  evt.aveHitRMSValid = true;
4439  for (unsigned short plane = 0; plane < nplanes; ++plane) {
4440  if (cnt[plane] > 4) { evt.aveHitRMS[plane] /= cnt[plane]; }
4441  else {
4442  evt.aveHitRMS[plane] = 10;
4443  evt.aveHitRMSValid = false;
4444  } // cnt too low
4445  } // plane
4446 
4447  if (tcc.modes[kDebug]) {
4448  std::cout << "Analyze hits aveHitRMS";
4449  std::cout << std::fixed << std::setprecision(1);
4450  for (auto rms : evt.aveHitRMS)
4451  std::cout << " " << rms;
4452  std::cout << " aveHitRMSValid? " << evt.aveHitRMSValid << "\n";
4453  }
4454 
4455  return true;
4456  } // Analyze hits
4457 
4458  ////////////////////////////////////////////////
4459  bool
4461  {
4462  // return true if the hit is in a long pulse indicating that it's position
4463  // and charge are not well known
4464  return ((hit.GoodnessOfFit() < 0 || hit.GoodnessOfFit() > 50) && hit.Multiplicity() > 5);
4465  }
4466 
4467  ////////////////////////////////////////////////
4468  void
4470  {
4471  // Defines the local vector of dead wires and the low-high range of hits in each wire in
4472  // the TPCID in TCEvent. Note that there is no requirement that the allHits collection is sorted. Care should
4473  // be taken when looping over hits using this range - see SignalAtTp
4474 
4475  // see if this function was called in the current TPCID. There is nothing that needs to
4476  // be done if that is the case
4477  if (inTPCID == evt.TPCID) return;
4478 
4479  evt.TPCID = inTPCID;
4480  unsigned short nplanes = tcc.geom->Nplanes(inTPCID);
4481  unsigned int cstat = inTPCID.Cryostat;
4482  unsigned int tpc = inTPCID.TPC;
4483  if (tcc.useChannelStatus) {
4484  lariov::ChannelStatusProvider const& channelStatus =
4486  evt.goodWire.resize(nplanes);
4487  for (unsigned short pln = 0; pln < nplanes; ++pln) {
4488  unsigned int nwires = tcc.geom->Nwires(pln, tpc, cstat);
4489  // set all wires dead
4490  evt.goodWire[pln].resize(nwires, false);
4491  for (unsigned int wire = 0; wire < nwires; ++wire) {
4492  raw::ChannelID_t chan =
4493  tcc.geom->PlaneWireToChannel((int)pln, (int)wire, (int)tpc, (int)cstat);
4494  evt.goodWire[pln][wire] = channelStatus.IsGood(chan);
4495  } // wire
4496  } // pln
4497  }
4498  else {
4499  // resize and set every channel good
4500  evt.goodWire.resize(nplanes);
4501  for (unsigned short pln = 0; pln < nplanes; ++pln) {
4502  unsigned int nwires = tcc.geom->Nwires(pln, tpc, cstat);
4503  evt.goodWire[pln].resize(nwires, true);
4504  } // pln
4505  } // don't use channelStatus
4506 
4507  // there is no need to define evt.wireHitRange if the hit collection is not sliced. The function
4508  // SignalAtTP will then use the (smaller) slc.WireHitRange instead of evt.wireHitRange
4509  if (!evt.expectSlicedHits) return;
4510 
4511  // define the size of evt.wireHitRange
4512  evt.wireHitRange.resize(nplanes);
4513  for (unsigned short pln = 0; pln < nplanes; ++pln) {
4514  unsigned int nwires = tcc.geom->Nwires(pln, tpc, cstat);
4515  evt.wireHitRange[pln].resize(nwires);
4516  for (unsigned int wire = 0; wire < nwires; ++wire)
4517  evt.wireHitRange[pln][wire] = {UINT_MAX, UINT_MAX};
4518  } // pln
4519 
4520  // next define the wireHitRange values. Make one loop through the allHits collection
4521  unsigned int nBadWireFix = 0;
4522  for (unsigned int iht = 0; iht < (*evt.allHits).size(); ++iht) {
4523  auto& hit = (*evt.allHits)[iht];
4524  auto wid = hit.WireID();
4525  if (wid.Cryostat != cstat) continue;
4526  if (wid.TPC != tpc) continue;
4527  unsigned short pln = wid.Plane;
4528  unsigned int wire = wid.Wire;
4529  // Check the goodWire status and correct it if it's wrong
4530  if (!evt.goodWire[pln][wire]) {
4531  evt.goodWire[pln][wire] = true;
4532  ++nBadWireFix;
4533  } // not goodWire
4534  if (evt.wireHitRange[pln][wire].first == UINT_MAX) evt.wireHitRange[pln][wire].first = iht;
4535  evt.wireHitRange[pln][wire].second = iht;
4536  } // iht
4537  if (nBadWireFix > 0 && tcc.modes[kDebug]) {
4538  std::cout << "FillWireHitRange found hits on " << nBadWireFix
4539  << " wires that were declared not-good by the ChannelStatus service. Fixed it...\n";
4540  }
4541  } // FillWireHitRange
4542 
4543  ////////////////////////////////////////////////
4544  bool
4546  detinfo::DetectorPropertiesData const& detProp,
4547  TCSlice& slc)
4548  {
4549  // fills the WireHitRange vector. Slightly modified version of the one in ClusterCrawlerAlg.
4550  // Returns false if there was a serious error
4551 
4552  // determine the number of planes
4553  unsigned int cstat = slc.TPCID.Cryostat;
4554  unsigned int tpc = slc.TPCID.TPC;
4555  unsigned short nplanes = tcc.geom->Nplanes(tpc, cstat);
4556  slc.nPlanes = nplanes;
4557  if (nplanes > 3) return false;
4558 
4559  // Y,Z limits of the detector
4560  double local[3] = {0., 0., 0.};
4561  double world[3] = {0., 0., 0.};
4562  const geo::TPCGeo& thetpc = tcc.geom->TPC(tpc, cstat);
4563  thetpc.LocalToWorld(local, world);
4564  // reduce the active area of the TPC by 1 cm to prevent wire boundary issues
4565  slc.xLo = world[0] - tcc.geom->DetHalfWidth(tpc, cstat) + 1;
4566  slc.xHi = world[0] + tcc.geom->DetHalfWidth(tpc, cstat) - 1;
4567  slc.yLo = world[1] - tcc.geom->DetHalfHeight(tpc, cstat) + 1;
4568  slc.yHi = world[1] + tcc.geom->DetHalfHeight(tpc, cstat) - 1;
4569  slc.zLo = world[2] - tcc.geom->DetLength(tpc, cstat) / 2 + 1;
4570  slc.zHi = world[2] + tcc.geom->DetLength(tpc, cstat) / 2 - 1;
4571 
4572  // initialize everything
4573  slc.wireHitRange.resize(nplanes);
4574  slc.firstWire.resize(nplanes);
4575  slc.lastWire.resize(nplanes);
4576  slc.nWires.resize(nplanes);
4577  tcc.maxPos0.resize(nplanes);
4578  tcc.maxPos1.resize(nplanes);
4579  evt.aveHitRMS.resize(nplanes, nplanes);
4580 
4581  std::pair<unsigned int, unsigned int> flag;
4582  flag.first = UINT_MAX;
4583  flag.second = UINT_MAX;
4584 
4585  // Calculate tcc.unitsPerTick, the scale factor to convert a tick into
4586  // Wire Spacing Equivalent (WSE) units where the wire spacing in this plane = 1.
4587  // Strictly speaking this factor should be calculated for each plane to handle the
4588  // case where the wire spacing is different in each plane. Deal with this later if
4589  // the approximation used here fails.
4590 
4591  raw::ChannelID_t channel = tcc.geom->PlaneWireToChannel(0, 0, (int)tpc, (int)cstat);
4592  tcc.wirePitch = tcc.geom->WirePitch(tcc.geom->View(channel));
4593  float tickToDist = detProp.DriftVelocity(detProp.Efield(), detProp.Temperature());
4594  tickToDist *= 1.e-3 * sampling_rate(clockData); // 1e-3 is conversion of 1/us to 1/ns
4595  tcc.unitsPerTick = tickToDist / tcc.wirePitch;
4596  for (unsigned short plane = 0; plane < nplanes; ++plane) {
4597  slc.firstWire[plane] = UINT_MAX;
4598  slc.lastWire[plane] = 0;
4599  slc.nWires[plane] = tcc.geom->Nwires(plane, tpc, cstat);
4600  slc.wireHitRange[plane].resize(slc.nWires[plane], flag);
4601  tcc.maxPos0[plane] = (float)slc.nWires[plane] - 0.5;
4602  tcc.maxPos1[plane] = (float)detProp.NumberTimeSamples() * tcc.unitsPerTick;
4603  }
4604 
4605  unsigned int lastWire = 0, lastPlane = 0;
4606  for (unsigned int iht = 0; iht < slc.slHits.size(); ++iht) {
4607  unsigned int ahi = slc.slHits[iht].allHitsIndex;
4608  if (ahi > (*evt.allHits).size() - 1) return false;
4609  auto& hit = (*evt.allHits)[ahi];
4610  if (hit.WireID().Cryostat != cstat) continue;
4611  if (hit.WireID().TPC != tpc) continue;
4612  unsigned short plane = hit.WireID().Plane;
4613  unsigned int wire = hit.WireID().Wire;
4614  if (wire > slc.nWires[plane] - 1) {
4615  mf::LogWarning("TC") << "FillWireHitRange: Invalid wire number " << wire << " > "
4616  << slc.nWires[plane] - 1 << " in plane " << plane << " Quitting";
4617  return false;
4618  } // too large wire number
4619  if (plane == lastPlane && wire < lastWire) {
4620  mf::LogWarning("TC")
4621  << "FillWireHitRange: Hits are not in increasing wire order. Quitting ";
4622  return false;
4623  } // hits out of order
4624  lastWire = wire;
4625  lastPlane = plane;
4626  if (slc.firstWire[plane] == UINT_MAX) slc.firstWire[plane] = wire;
4627  if (slc.wireHitRange[plane][wire].first == UINT_MAX)
4628  slc.wireHitRange[plane][wire].first = iht;
4629  slc.wireHitRange[plane][wire].second = iht;
4630  slc.lastWire[plane] = wire + 1;
4631  } // iht
4632  // check
4633  unsigned int slhitsSize = slc.slHits.size();
4634  for (unsigned short plane = 0; plane < nplanes; ++plane) {
4635  for (unsigned int wire = slc.firstWire[plane]; wire < slc.lastWire[plane]; ++wire) {
4636  if (slc.wireHitRange[plane][wire].first == UINT_MAX) continue;
4637  if (slc.wireHitRange[plane][wire].first > slhitsSize - 1 &&
4638  slc.wireHitRange[plane][wire].second > slhitsSize)
4639  return false;
4640  } // wire
4641  } // plane
4642 
4643  // Find the average multiplicity 1 hit RMS and calculate the expected max RMS for each range
4644  if (tcc.modes[kDebug] && (int)tpc == debug.TPC) {
4645  // Note that this function is called before the slice is pushed into slices so the index
4646  // isn't decremented by 1
4647  std::cout << "Slice ID/Index " << slc.ID << "/" << slices.size() << " tpc " << tpc
4648  << " tcc.unitsPerTick " << std::setprecision(3) << tcc.unitsPerTick;
4649  std::cout << " Active volume (";
4650  std::cout << std::fixed << std::setprecision(1) << slc.xLo << " < X < " << slc.xHi << ") (";
4651  std::cout << std::fixed << std::setprecision(1) << slc.yLo << " < Y < " << slc.yHi << ") (";
4652  std::cout << std::fixed << std::setprecision(1) << slc.zLo << " < Z < " << slc.zHi << ")\n";
4653  }
4654 
4655  return true;
4656 
4657  } // FillWireHitRange
4658 
4659  ////////////////////////////////////////////////
4660  bool
4661  WireHitRangeOK(TCSlice& slc, const CTP_t& inCTP)
4662  {
4663  // returns true if the passed CTP code is consistent with the CT code of the WireHitRangeVector
4664  geo::PlaneID planeID = DecodeCTP(inCTP);
4665  if (planeID.Cryostat != slc.TPCID.Cryostat) return false;
4666  if (planeID.TPC != slc.TPCID.TPC) return false;
4667  return true;
4668  }
4669 
4670  ////////////////////////////////////////////////
4671  bool
4672  MergeAndStore(TCSlice& slc, unsigned int itj1, unsigned int itj2, bool doPrt)
4673  {
4674  // Merge the two trajectories in allTraj and store them. Returns true if it was successfull.
4675  // Merging is done between the end (end = 1) of tj1 and the beginning (end = 0) of tj2. This function preserves the
4676  // AlgMod state of itj1.
4677  // The itj1 -> itj2 merge order is reversed if end1 of itj2 is closer to end0 of itj1
4678 
4679  if (itj1 > slc.tjs.size() - 1) return false;
4680  if (itj2 > slc.tjs.size() - 1) return false;
4681  if (slc.tjs[itj1].AlgMod[kKilled] || slc.tjs[itj2].AlgMod[kKilled]) return false;
4682  if (slc.tjs[itj1].AlgMod[kHaloTj] || slc.tjs[itj2].AlgMod[kHaloTj]) return false;
4683 
4684  // Merging shower Tjs requires merging the showers as well.
4685  if (slc.tjs[itj1].AlgMod[kShowerTj] || slc.tjs[itj2].AlgMod[kShowerTj])
4686  return MergeShowerTjsAndStore(slc, itj1, itj2, doPrt);
4687 
4688  // Ensure that the order of 3D-matched Tjs is consistent with the convention that
4689  unsigned short pfp1 = GetPFPIndex(slc, slc.tjs[itj1].ID);
4690  unsigned short pfp2 = GetPFPIndex(slc, slc.tjs[itj2].ID);
4691  if (pfp1 != USHRT_MAX || pfp2 != USHRT_MAX) {
4692  if (pfp1 != USHRT_MAX && pfp2 != USHRT_MAX) return false;
4693  // Swap so that the order of tj1 is preserved. Tj2 may be reversed to be consistent
4694  if (pfp1 == USHRT_MAX) std::swap(itj1, itj2);
4695  } // one or both used in a PFParticle
4696 
4697  // make copies so they can be trimmed as needed
4698  Trajectory tj1 = slc.tjs[itj1];
4699  Trajectory tj2 = slc.tjs[itj2];
4700 
4701  // ensure that these are in the same step order
4702  if (tj2.StepDir != tj1.StepDir) ReverseTraj(slc, tj2);
4703 
4704  Point2_t tp1e0 = tj1.Pts[tj1.EndPt[0]].Pos;
4705  Point2_t tp1e1 = tj1.Pts[tj1.EndPt[1]].Pos;
4706  Point2_t tp2e0 = tj2.Pts[tj2.EndPt[0]].Pos;
4707  Point2_t tp2e1 = tj2.Pts[tj2.EndPt[1]].Pos;
4708 
4709  if (doPrt) {
4710  mf::LogVerbatim("TC") << "MergeAndStore: T" << tj1.ID << " and T" << tj2.ID
4711  << " at merge points " << PrintPos(slc, tp1e1) << " "
4712  << PrintPos(slc, tp2e0);
4713  }
4714 
4715  // swap the order so that abs(tj1end1 - tj2end0) is less than abs(tj2end1 - tj1end0)
4716  if (PosSep2(tp1e1, tp2e0) > PosSep2(tp2e1, tp1e0)) {
4717  std::swap(tj1, tj2);
4718  std::swap(tp1e0, tp2e0);
4719  std::swap(tp1e1, tp2e1);
4720  if (doPrt)
4721  mf::LogVerbatim("TC") << " swapped the order. Merge points " << PrintPos(slc, tp1e1) << " "
4722  << PrintPos(slc, tp2e0);
4723  }
4724 
4725  // Here is what we are looking for, where - indicates a TP with charge.
4726  // Note that this graphic is in the stepping direction (+1 = +wire direction)
4727  // tj1: 0------------1
4728  // tj2: 0-----------1
4729  // Another possibility with overlap
4730  // tj1: 0-------------1
4731  // tj2: 0--------------1
4732 
4733  if (tj1.StepDir > 1) {
4734  // Not allowed
4735  // tj1: 0---------------------------1
4736  // tj2: 0------1
4737  if (tp2e0[0] > tp1e0[0] && tp2e1[0] < tp1e1[0]) return false;
4738  /// Not allowed
4739  // tj1: 0------1
4740  // tj2: 0---------------------------1
4741  if (tp1e0[0] > tp2e0[0] && tp1e1[0] < tp2e1[0]) return false;
4742  }
4743  else {
4744  // same as above but with ends reversed
4745  if (tp2e1[0] > tp1e1[0] && tp2e0[0] < tp1e0[0]) return false;
4746  if (tp1e1[0] > tp2e1[0] && tp1e0[0] < tp2e0[0]) return false;
4747  }
4748 
4749  if (tj1.VtxID[1] > 0 && tj2.VtxID[0] == tj1.VtxID[1]) {
4750  auto& vx = slc.vtxs[tj1.VtxID[1] - 1];
4751  if (!MakeVertexObsolete("MAS", slc, vx, false)) {
4752  if (doPrt)
4753  mf::LogVerbatim("TC") << "MergeAndStore: Found a good vertex between Tjs " << tj1.VtxID[1]
4754  << " No merging";
4755  return false;
4756  }
4757  }
4758 
4759  if (tj1.EndFlag[1][kBragg]) {
4760  if (doPrt)
4761  mf::LogVerbatim("TC") << "MergeAndStore: You are merging the end of trajectory T" << tj1.ID
4762  << " with a Bragg peak. Not merging\n";
4763  return false;
4764  }
4765 
4766  // remove any points at the end of tj1 that don't have used hits
4767  tj1.Pts.resize(tj1.EndPt[1] + 1);
4768 
4769  // determine if they overlap by finding the point on tj2 that is closest
4770  // to the end point of tj1.
4771  TrajPoint& endtj1TP = tj1.Pts[tj1.EndPt[1]];
4772  // Set minSep large so that dead wire regions are accounted for
4773  float minSep = 1000;
4774  unsigned short tj2ClosePt = 0;
4775  // Note that TrajPointTrajDOCA only considers TPs that have charge
4776  TrajPointTrajDOCA(slc, endtj1TP, tj2, tj2ClosePt, minSep);
4777  if (doPrt)
4778  mf::LogVerbatim("TC") << " Merge point tj1 " << PrintPos(slc, endtj1TP) << " tj2ClosePt "
4779  << tj2ClosePt << " Pos " << PrintPos(slc, tj2.Pts[tj2ClosePt]);
4780  // check for full overlap
4781  if (tj2ClosePt > tj2.EndPt[1]) return false;
4782 
4783  // The approach is to append tj2 to tj1, store tj1 as a new trajectory,
4784  // and re-assign all hits to the new trajectory
4785 
4786  // First ensure that any hit will appear only once in the merged trajectory in the overlap region
4787  // whether it is used or unused. The point on tj2 where the merge will begin, tj2ClosePt, will be
4788  // increased until this condition is met.
4789  // Make a temporary vector of tj1 hits in the end points for simpler searching
4790  std::vector<unsigned int> tj1Hits;
4791  for (unsigned short ii = 0; ii < tj1.Pts.size(); ++ii) {
4792  // only go back a few points in tj1
4793  if (ii > 10) break;
4794  unsigned short ipt = tj1.Pts.size() - 1 - ii;
4795  tj1Hits.insert(tj1Hits.end(), tj1.Pts[ipt].Hits.begin(), tj1.Pts[ipt].Hits.end());
4796  if (ipt == 0) break;
4797  } // ii
4798 
4799  bool bumpedPt = true;
4800  while (bumpedPt) {
4801  bumpedPt = false;
4802  for (unsigned short ii = 0; ii < tj2.Pts[tj2ClosePt].Hits.size(); ++ii) {
4803  unsigned int iht = tj2.Pts[tj2ClosePt].Hits[ii];
4804  if (std::find(tj1Hits.begin(), tj1Hits.end(), iht) != tj1Hits.end()) bumpedPt = true;
4805  } // ii
4806  if (bumpedPt && tj2ClosePt < tj2.EndPt[1]) { ++tj2ClosePt; }
4807  else {
4808  break;
4809  }
4810  } // bumpedPt
4811  if (doPrt) mf::LogVerbatim("TC") << " revised tj2ClosePt " << tj2ClosePt;
4812  // append tj2 hits to tj1
4813 
4814  tj1.Pts.insert(tj1.Pts.end(), tj2.Pts.begin() + tj2ClosePt, tj2.Pts.end());
4815  // re-define the end points
4816  SetEndPoints(tj1);
4817  tj1.EndFlag[1] = tj2.EndFlag[1];
4818 
4819  // A more exhaustive check that hits only appear once
4820  if (HasDuplicateHits(slc, tj1, doPrt)) return false;
4821  if (tj2.VtxID[1] > 0) {
4822  // move the end vertex of tj2 to the end of tj1
4823  tj1.VtxID[1] = tj2.VtxID[1];
4824  }
4825  // Transfer some of the AlgMod bits
4826  if (tj2.AlgMod[kMichel]) tj1.AlgMod[kMichel] = true;
4827  if (tj2.AlgMod[kDeltaRay]) {
4828  tj1.AlgMod[kDeltaRay] = true;
4829  tj1.ParentID = tj2.ParentID;
4830  }
4831  // keep track of the IDs before they are clobbered
4832  int tj1ID = tj1.ID;
4833  int tj2ID = tj2.ID;
4834  // kill the original trajectories
4835  MakeTrajectoryObsolete(slc, itj1);
4836  MakeTrajectoryObsolete(slc, itj2);
4837  // Do this so that StoreTraj keeps the correct WorkID (of itj1)
4838  tj1.ID = tj1.WorkID;
4839  SetPDGCode(slc, tj1);
4840  tj1.NeedsUpdate = true;
4841  if (!StoreTraj(slc, tj1)) return false;
4842  int newTjID = slc.tjs.size();
4843  // Use the ParentID to trace which new Tj is superseding the merged ones
4844  tj1.ParentID = newTjID;
4845  tj2.ParentID = newTjID;
4846  if (doPrt) mf::LogVerbatim("TC") << " MAS success. Created T" << newTjID;
4847  // Transfer the ParentIDs of any other Tjs that refer to Tj1 and Tj2 to the new Tj
4848  for (auto& tj : slc.tjs)
4849  if (tj.ParentID == tj1ID || tj.ParentID == tj2ID) tj.ParentID = newTjID;
4850  // try to attach it to a vertex
4851  AttachAnyVertexToTraj(slc, newTjID, doPrt);
4852  return true;
4853  } // MergeAndStore
4854 
4855  ////////////////////////////////////////////////
4856  std::vector<int>
4857  GetAssns(TCSlice& slc, std::string type1Name, int id, std::string type2Name)
4858  {
4859  // returns a list of IDs of objects (slc, vertices, pfps, etc) with type1Name that are in slc with
4860  // type2Name. This is intended to be a general purpose replacement for specific functions like GetVtxTjIDs, etc
4861 
4862  std::vector<int> tmp;
4863  if (id <= 0) return tmp;
4864  unsigned int uid = id;
4865 
4866  if (type1Name == "T" && uid <= slc.tjs.size() && type2Name == "P") {
4867  // return a list of PFPs that have the tj in TjIDs, P -> T<ID>
4868  for (auto& pfp : slc.pfps) {
4869  if (pfp.ID <= 0) continue;
4870  if (std::find(pfp.TjIDs.begin(), pfp.TjIDs.end(), id) != pfp.TjIDs.end())
4871  tmp.push_back(pfp.ID);
4872  } // pf
4873  return tmp;
4874  } // P -> T
4875 
4876  if (type1Name == "P" && uid <= slc.pfps.size() && (type2Name == "2S" || type2Name == "3S")) {
4877  // return a list of 3D or 2D showers with the assn 3S -> 2S -> T -> P<ID> or 2S -> T -> P.
4878  auto& pfp = slc.pfps[uid - 1];
4879  // First form a list of 2S -> T -> P<ID>
4880  std::vector<int> ssid;
4881  for (auto& ss : slc.cots) {
4882  if (ss.ID <= 0) continue;
4883  auto shared = SetIntersection(ss.TjIDs, pfp.TjIDs);
4884  if (!shared.empty() && std::find(ssid.begin(), ssid.end(), ss.ID) == ssid.end())
4885  ssid.push_back(ss.ID);
4886  } // ss
4887  if (type2Name == "2S") return ssid;
4888  for (auto& ss3 : slc.showers) {
4889  if (ss3.ID <= 0) continue;
4890  auto shared = SetIntersection(ss3.CotIDs, ssid);
4891  if (!shared.empty() && std::find(tmp.begin(), tmp.end(), ss3.ID) == tmp.end())
4892  tmp.push_back(ss3.ID);
4893  } // ss3
4894  return tmp;
4895  } // 3S -> 2S -> T -> P
4896 
4897  if (type1Name == "2V" && uid <= slc.vtxs.size() && type2Name == "T") {
4898  // 2V -> T
4899  for (auto& tj : slc.tjs) {
4900  if (tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) continue;
4901  for (unsigned short end = 0; end < 2; ++end) {
4902  if (tj.VtxID[end] != id) continue;
4903  if (std::find(tmp.begin(), tmp.end(), tj.ID) == tmp.end()) tmp.push_back(tj.ID);
4904  } // end
4905  } // tj
4906  return tmp;
4907  } // 2V -> T
4908 
4909  if (type1Name == "3V" && uid <= slc.vtx3s.size() && type2Name == "P") {
4910  for (auto& pfp : slc.pfps) {
4911  if (pfp.ID == 0) continue;
4912  for (unsigned short end = 0; end < 2; ++end) {
4913  if (pfp.Vx3ID[end] != id) continue;
4914  // encode the end with the ID
4915  if (std::find(tmp.begin(), tmp.end(), pfp.ID) == tmp.end()) tmp.push_back(pfp.ID);
4916  } // end
4917  } // pfp
4918  return tmp;
4919  } // 3V -> P
4920 
4921  if (type1Name == "3V" && uid <= slc.vtx3s.size() && type2Name == "T") {
4922  // 3V -> T
4923  for (auto& tj : slc.tjs) {
4924  if (tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) continue;
4925  for (unsigned short end = 0; end < 2; ++end) {
4926  if (tj.VtxID[end] > 0 && tj.VtxID[end] <= slc.vtxs.size()) {
4927  auto& vx2 = slc.vtxs[tj.VtxID[end] - 1];
4928  if (vx2.Vx3ID != id) continue;
4929  if (std::find(tmp.begin(), tmp.end(), tj.ID) == tmp.end()) tmp.push_back(tj.ID);
4930  }
4931  } // end
4932  } // tj
4933  return tmp;
4934  } // 3V -> T
4935 
4936  if (type1Name == "3V" && uid <= slc.vtx3s.size() && type2Name == "2V") {
4937  // 3V -> 2V
4938  for (auto& vx2 : slc.vtxs) {
4939  if (vx2.ID == 0) continue;
4940  if (vx2.Vx3ID == id) tmp.push_back(vx2.ID);
4941  } // vx2
4942  return tmp;
4943  } // 3V -> 2V
4944 
4945  if (type1Name == "3S" && uid <= slc.showers.size() && type2Name == "T") {
4946  // 3S -> T
4947  auto& ss3 = slc.showers[uid - 1];
4948  if (ss3.ID == 0) return tmp;
4949  for (auto cid : ss3.CotIDs) {
4950  auto& ss = slc.cots[cid - 1];
4951  if (ss.ID == 0) continue;
4952  tmp.insert(tmp.end(), ss.TjIDs.begin(), ss.TjIDs.end());
4953  } // cid
4954  return tmp;
4955  } // 3S -> T
4956 
4957  // This isn't strictly necessary but do it for consistency
4958  if (type1Name == "2S" && uid <= slc.cots.size() && type2Name == "T") {
4959  // 2S -> T
4960  auto& ss = slc.cots[uid - 1];
4961  return ss.TjIDs;
4962  } // 2S -> T
4963 
4964  if (type1Name == "3S" && uid <= slc.showers.size() && type2Name == "P") {
4965  // 3S -> P
4966  auto& ss3 = slc.showers[uid - 1];
4967  if (ss3.ID == 0) return tmp;
4968  for (auto cid : ss3.CotIDs) {
4969  auto& ss = slc.cots[cid - 1];
4970  if (ss.ID == 0) continue;
4971  for (auto tid : ss.TjIDs) {
4972  auto& tj = slc.tjs[tid - 1];
4973  if (tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) continue;
4974  if (!tj.AlgMod[kMat3D]) continue;
4975  for (auto& pfp : slc.pfps) {
4976  if (pfp.ID <= 0) continue;
4977  if (std::find(pfp.TjIDs.begin(), pfp.TjIDs.end(), tj.ID) == pfp.TjIDs.end()) continue;
4978  if (std::find(tmp.begin(), tmp.end(), pfp.ID) == tmp.end()) tmp.push_back(pfp.ID);
4979  } // pf
4980  } // tid
4981  } // cid
4982  return tmp;
4983  } // 3S -> P
4984 
4985  if (type1Name == "T" && uid <= slc.tjs.size() && type2Name == "2S") {
4986  // T -> 2S
4987  for (auto& ss : slc.cots) {
4988  if (ss.ID == 0) continue;
4989  if (std::find(ss.TjIDs.begin(), ss.TjIDs.end(), id) != ss.TjIDs.end()) tmp.push_back(ss.ID);
4990  } // ss
4991  return tmp;
4992  } // T -> 2S
4993 
4994  if (type1Name == "T" && uid <= slc.tjs.size() && type2Name == "3S") {
4995  // T -> 3S
4996  for (auto& ss : slc.cots) {
4997  if (ss.ID == 0) continue;
4998  if (std::find(ss.TjIDs.begin(), ss.TjIDs.end(), id) == ss.TjIDs.end()) continue;
4999  if (ss.SS3ID > 0) tmp.push_back(ss.SS3ID);
5000  } // ss
5001  return tmp;
5002  } // T -> 3S
5003 
5004  return tmp;
5005  } // GetAssns
5006 
5007  ////////////////////////////////////////////////
5008  bool
5010  Trajectory& tj,
5011  unsigned int fromhit,
5012  unsigned int tohit,
5013  unsigned short pass)
5014  {
5015  // Start a trajectory located at fromHit with direction pointing to toHit
5016 
5017  auto& fromHit = (*evt.allHits)[slc.slHits[fromhit].allHitsIndex];
5018  auto& toHit = (*evt.allHits)[slc.slHits[tohit].allHitsIndex];
5019  float fromWire = fromHit.WireID().Wire;
5020  float fromTick = fromHit.PeakTime();
5021  float toWire = toHit.WireID().Wire;
5022  float toTick = toHit.PeakTime();
5023  CTP_t tCTP = EncodeCTP(fromHit.WireID());
5024  bool success = StartTraj(slc, tj, fromWire, fromTick, toWire, toTick, tCTP, pass);
5025  if (!success) return false;
5026  // turn on debugging using the WorkID?
5027  if (tcc.modes[kDebug] && !tcc.dbgStp && !tcc.dbgDump && tcc.dbgSlc && tj.ID == debug.WorkID)
5028  tcc.dbgStp = true;
5029  if (tcc.dbgStp) {
5030  auto& tp = tj.Pts[0];
5031  mf::LogVerbatim("TC") << "StartTraj T" << tj.ID << " from " << (int)fromWire << ":"
5032  << (int)fromTick << " -> " << (int)toWire << ":" << (int)toTick
5033  << " StepDir " << tj.StepDir << " dir " << tp.Dir[0] << " " << tp.Dir[1]
5034  << " ang " << tp.Ang << " AngleCode " << tp.AngleCode << " angErr "
5035  << tp.AngErr << " ExpectedHitsRMS " << ExpectedHitsRMS(slc, tp);
5036  } // tcc.dbgStp
5037  return true;
5038  } // StartTraj
5039 
5040  ////////////////////////////////////////////////
5041  bool
5043  Trajectory& tj,
5044  float fromWire,
5045  float fromTick,
5046  float toWire,
5047  float toTick,
5048  CTP_t& tCTP,
5049  unsigned short pass)
5050  {
5051  // Start a simple (seed) trajectory going from (fromWire, toTick) to (toWire, toTick).
5052 
5053  // decrement the work ID so we can use it for debugging problems
5054  --evt.WorkID;
5055  if (evt.WorkID == INT_MIN) evt.WorkID = -1;
5056  tj.ID = evt.WorkID;
5057  tj.Pass = pass;
5058  // Assume we are stepping in the positive WSE units direction
5059  short stepdir = 1;
5060  int fWire = std::nearbyint(fromWire);
5061  int tWire = std::nearbyint(toWire);
5062  if (tWire < fWire) { stepdir = -1; }
5063  else if (tWire == fWire) {
5064  // on the same wire
5065  if (toTick < fromTick) stepdir = -1;
5066  }
5067  tj.StepDir = stepdir;
5068  tj.CTP = tCTP;
5069  tj.ParentID = -1;
5070  tj.Strategy.reset();
5071  tj.Strategy[kNormal] = true;
5072 
5073  // create a trajectory point
5074  TrajPoint tp;
5075  if (!MakeBareTrajPoint(slc, fromWire, fromTick, toWire, toTick, tCTP, tp)) return false;
5076  SetAngleCode(tp);
5077  tp.AngErr = 0.1;
5078  tj.Pts.push_back(tp);
5079  // turn on debugging using the WorkID?
5080  if (tcc.modes[kDebug] && !tcc.dbgStp && !tcc.dbgDump && tcc.dbgSlc && tj.ID == debug.WorkID)
5081  tcc.dbgStp = true;
5082  if (tcc.dbgStp) {
5083  auto& tp = tj.Pts[0];
5084  mf::LogVerbatim("TC") << "StartTraj T" << tj.ID << " from " << (int)fromWire << ":"
5085  << (int)fromTick << " -> " << (int)toWire << ":" << (int)toTick
5086  << " StepDir " << tj.StepDir << " dir " << tp.Dir[0] << " " << tp.Dir[1]
5087  << " ang " << tp.Ang << " AngleCode " << tp.AngleCode << " angErr "
5088  << tp.AngErr << " ExpectedHitsRMS " << ExpectedHitsRMS(slc, tp);
5089  } // tcc.dbgStp
5090  return true;
5091 
5092  } // StartTraj
5093 
5094  ////////////////////////////////////////////////
5095  std::pair<unsigned short, unsigned short>
5096  GetSliceIndex(std::string typeName, int uID)
5097  {
5098  // returns the slice index and product index of a data product having typeName and unique ID uID
5099  for (unsigned short isl = 0; isl < slices.size(); ++isl) {
5100  auto& slc = slices[isl];
5101  if (typeName == "T") {
5102  for (unsigned short indx = 0; indx < slc.tjs.size(); ++indx) {
5103  if (slc.tjs[indx].UID == uID) { return std::make_pair(isl, indx); }
5104  }
5105  } // T
5106  if (typeName == "P") {
5107  for (unsigned short indx = 0; indx < slc.pfps.size(); ++indx) {
5108  if (slc.pfps[indx].UID == uID) { return std::make_pair(isl, indx); }
5109  }
5110  } // P
5111  if (typeName == "2V") {
5112  for (unsigned short indx = 0; indx < slc.vtxs.size(); ++indx) {
5113  if (slc.vtxs[indx].UID == uID) { return std::make_pair(isl, indx); }
5114  }
5115  } // 2V
5116  if (typeName == "3V") {
5117  for (unsigned short indx = 0; indx < slc.vtx3s.size(); ++indx) {
5118  if (slc.vtx3s[indx].UID == uID) { return std::make_pair(isl, indx); }
5119  }
5120  } // 3V
5121  if (typeName == "2S") {
5122  for (unsigned short indx = 0; indx < slc.cots.size(); ++indx) {
5123  if (slc.cots[indx].UID == uID) { return std::make_pair(isl, indx); }
5124  }
5125  } // 2S
5126  if (typeName == "3S") {
5127  for (unsigned short indx = 0; indx < slc.showers.size(); ++indx) {
5128  if (slc.showers[indx].UID == uID) { return std::make_pair(isl, indx); }
5129  }
5130  } // T
5131  } // isl
5132  return std::make_pair(USHRT_MAX, USHRT_MAX);
5133  } // GetSliceIndex
5134 
5135  ////////////////////////////////////////////////
5136  bool
5137  Fit2D(short mode,
5138  Point2_t inPt,
5139  float& inPtErr,
5140  Vector2_t& outVec,
5141  Vector2_t& outVecErr,
5142  float& chiDOF)
5143  {
5144  // Fit points to a 2D line.
5145  // Mode = 0: Initialize
5146  // Mode = 1: Accumulate
5147  // Mode = 2: Accumulate and store to calculate chiDOF
5148  // Mode = -1: Fit and put results in outVec and chiDOF
5149 
5150  static double sum, sumx, sumy, sumx2, sumy2, sumxy;
5151  static unsigned short cnt;
5152  static std::vector<Point2_t> fitPts;
5153  static std::vector<double> fitWghts;
5154 
5155  if (mode == 0) {
5156  // initialize
5157  cnt = 0;
5158  sum = 0.;
5159  sumx = 0.;
5160  sumy = 0.;
5161  sumx2 = 0.;
5162  sumy2 = 0.;
5163  sumxy = 0;
5164  fitPts.resize(0);
5165  fitWghts.resize(0);
5166  return true;
5167  } // mode == 0
5168 
5169  if (mode > 0) {
5170  if (inPtErr <= 0.) return false;
5171  ++cnt;
5172  double wght = 1 / (inPtErr * inPtErr);
5173  sum += wght;
5174  sumx += wght * inPt[0];
5175  sumx2 += wght * inPt[0] * inPt[0];
5176  sumy += wght * inPt[1];
5177  sumy2 += wght * inPt[1] * inPt[1];
5178  sumxy += wght * inPt[0] * inPt[1];
5179  if (mode == 1) return true;
5180  fitPts.push_back(inPt);
5181  fitWghts.push_back(wght);
5182  return true;
5183  } // Accumulate
5184 
5185  if (cnt < 2) return false;
5186  // do the fit
5187  double delta = sum * sumx2 - sumx * sumx;
5188  if (delta == 0.) return false;
5189  double A = (sumx2 * sumy - sumx * sumxy) / delta;
5190  double B = (sumxy * sum - sumx * sumy) / delta;
5191  outVec[0] = A;
5192  outVec[1] = B;
5193  chiDOF = 0;
5194  if (cnt == 2 || fitPts.empty()) return true;
5195 
5196  // calculate errors and chiDOF
5197  if (fitPts.size() != cnt) return false;
5198  double ndof = cnt - 2;
5199  double varnce =
5200  (sumy2 + A * A * sum + B * B * sumx2 - 2 * (A * sumy + B * sumxy - A * B * sumx)) / ndof;
5201  if (varnce > 0.) {
5202  outVecErr[0] = sqrt(varnce * sumx2 / delta);
5203  outVecErr[1] = sqrt(varnce * sum / delta);
5204  }
5205  else {
5206  outVecErr[0] = 0.;
5207  outVecErr[1] = 0.;
5208  }
5209  sum = 0.;
5210  // calculate chisq
5211  for (unsigned short ii = 0; ii < fitPts.size(); ++ii) {
5212  double arg = fitPts[ii][1] - A - B * fitPts[ii][0];
5213  sum += fitWghts[ii] * arg * arg;
5214  }
5215  chiDOF = sum / ndof;
5216  fitPts.resize(0);
5217  fitWghts.resize(0);
5218  return true;
5219 
5220  } // Fit2D
5221 
5222  ////////////////////////////////////////////////
5223  bool
5225  {
5226  // try to unpack the string as Cryostat:TPC:Plane:Wire:Tick or something
5227  // like Slice:<slice index>
5228 
5229  if (strng == "instruct") {
5230  std::cout << "****** Unrecognized DebugConfig. Here are your options\n";
5231  std::cout << " 'C:T:P:W:Tick' where C = cryostat, T = TPC, W = wire, Tick (+/-5) to debug "
5232  "stepping (DUNE)\n";
5233  std::cout << " 'P:W:Tick' for single cryostat/TPC detectors (uB, LArIAT, etc)\n";
5234  std::cout << " 'WorkID <id> <slice index>' where <id> is a tj work ID (< 0) in slice <slice "
5235  "index> (default = 0)\n";
5236  std::cout << " 'Merge <CTP>' to debug trajectory merging\n";
5237  std::cout << " '2V <CTP>' to debug 2D vertex finding\n";
5238  std::cout << " '3V' to debug 3D vertex finding\n";
5239  std::cout << " 'VxMerge' to debug 2D vertex merging\n";
5240  std::cout << " 'JunkVx' to debug 2D junk vertex finder\n";
5241  std::cout << " 'PFP' to debug 3D matching and PFParticles\n";
5242  std::cout << " 'MVI <MVI> <MVI Iteration>' for detailed debugging of one PFP MatchVecIndex\n";
5243  std::cout << " 'DeltaRay' to debug delta ray tagging\n";
5244  std::cout << " 'Muon' to debug muon tagging\n";
5245  std::cout << " '2S <CTP>' to debug a 2D shower in CTP\n";
5246  std::cout << " 'Reco TPC <TPC>' to only reconstruct hits in the specified TPC\n";
5247  std::cout << " 'Reco Slice <ID>' to reconstruct all sub-slices in the recob::Slice with the "
5248  "specified ID\n";
5249  std::cout << " 'SubSlice <sub-slice index>' where <slice index> restricts output to the "
5250  "specified sub-slice index\n";
5251  std::cout << " 'Stitch' to debug PFParticle stitching between TPCs\n";
5252  std::cout << " 'Sum' or 'Summary' to print a debug summary report\n";
5253  std::cout << " 'Dump <WorkID>' or 'Dump <UID>' to print all TPs in the trajectory to "
5254  "tcdump<UID>.csv\n";
5255  std::cout << " Note: Algs with debug printing include HamVx, HamVx2, SplitTjCVx, Comp3DVx, "
5256  "Comp3DVxIG, VtxHitsSwap\n";
5257  std::cout << " Set SkipAlgs: [\"bogusText\"] to print a list of algorithm names\n";
5258  return false;
5259  } // instruct
5260 
5261  // handle the simple cases that don't need decoding
5262  if (strng.find("3V") != std::string::npos) {
5263  tcc.dbg3V = true;
5264  tcc.modes[kDebug] = true;
5265  return true;
5266  }
5267  if (strng.find("3S") != std::string::npos) {
5268  tcc.dbg3S = true;
5269  tcc.modes[kDebug] = true;
5270  return true;
5271  }
5272  if (strng.find("VxMerge") != std::string::npos) {
5273  tcc.dbgVxMerge = true;
5274  tcc.modes[kDebug] = true;
5275  return true;
5276  }
5277  if (strng.find("JunkVx") != std::string::npos) {
5278  tcc.dbgVxJunk = true;
5279  tcc.modes[kDebug] = true;
5280  return true;
5281  }
5282  if (strng.find("DeltaRay") != std::string::npos) {
5283  tcc.dbgDeltaRayTag = true;
5284  tcc.modes[kDebug] = true;
5285  return true;
5286  }
5287  if (strng.find("Muon") != std::string::npos) {
5288  tcc.dbgMuonTag = true;
5289  tcc.modes[kDebug] = true;
5290  return true;
5291  }
5292  if (strng.find("Stitch") != std::string::npos) {
5293  tcc.dbgStitch = true;
5294  tcc.modes[kDebug] = true;
5295  return true;
5296  }
5297  if (strng.find("HamVx") != std::string::npos) {
5298  tcc.dbgAlg[kHamVx] = true;
5299  tcc.modes[kDebug] = true;
5300  return true;
5301  }
5302  if (strng.find("HamVx2") != std::string::npos) {
5303  tcc.dbgAlg[kHamVx2] = true;
5304  tcc.modes[kDebug] = true;
5305  return true;
5306  }
5307  if (strng.find("Sum") != std::string::npos) {
5308  tcc.dbgSummary = true;
5309  tcc.modes[kDebug] = true;
5310  return true;
5311  }
5312 
5313  std::vector<std::string> words;
5314  boost::split(words, strng, boost::is_any_of(" :"), boost::token_compress_on);
5315  if (words.size() == 5) {
5316  // configure for DUNE
5317  debug.Cryostat = std::stoi(words[0]);
5318  debug.TPC = std::stoi(words[1]);
5319  debug.Plane = std::stoi(words[2]);
5320  debug.Wire = std::stoi(words[3]);
5321  debug.Tick = std::stoi(words[4]);
5322  tcc.modes[kDebug] = true;
5323  tcc.dbgStp = true;
5324  // also dump this tj
5325  tcc.dbgDump = true;
5326  return true;
5327  } // nums.size() == 5
5328  if (words[0] == "PFP" || words[0] == "MVI") {
5329  tcc.dbgPFP = true;
5330  tcc.modes[kDebug] = true;
5331  // Use debug.Hit to identify the matchVec index
5332  if (words.size() > 2) {
5333  debug.MVI = std::stoi(words[1]);
5334  if (words.size() == 3) debug.MVI_Iter = std::stoi(words[2]);
5335  }
5336  return true;
5337  } // PFP
5338  if (words.size() == 2 && words[0] == "Dump") {
5339  debug.WorkID = std::stoi(words[1]);
5340  debug.Slice = 0;
5341  tcc.modes[kDebug] = true;
5342  tcc.dbgDump = true;
5343  return true;
5344  }
5345  if (words.size() > 1 && words[0] == "WorkID") {
5346  debug.WorkID = std::stoi(words[1]);
5347  if (debug.WorkID >= 0) return false;
5348  // default to sub-slice index 0
5349  debug.Slice = 0;
5350  if (words.size() > 2) debug.Slice = std::stoi(words[2]);
5351  tcc.modes[kDebug] = true;
5352  // dbgStp is set true after debug.WorkID is found
5353  tcc.dbgStp = false;
5354  return true;
5355  } // words.size() == 3 && words[0] == "WorkID"
5356  if (words.size() == 3 && words[0] == "Reco" && words[1] == "TPC") {
5357  tcc.recoTPC = std::stoi(words[2]);
5358  tcc.modes[kDebug] = true;
5359  std::cout << "Reconstructing only in TPC " << tcc.recoTPC << "\n";
5360  return true;
5361  }
5362  if (words.size() == 3 && words[0] == "Reco" && words[1] == "Slice") {
5363  tcc.recoSlice = std::stoi(words[1]);
5364  std::cout << "Reconstructing Slice " << tcc.recoSlice << "\n";
5365  return true;
5366  }
5367  if (words.size() == 3) {
5368  // configure for uB, LArIAT, etc
5369  debug.Cryostat = 0;
5370  debug.TPC = 0;
5371  debug.Plane = std::stoi(words[0]);
5372  debug.Wire = std::stoi(words[1]);
5373  debug.Tick = std::stoi(words[2]);
5374  debug.CTP = EncodeCTP(debug.Cryostat, debug.TPC, debug.Plane);
5375  tcc.modes[kDebug] = true;
5376  tcc.dbgStp = true;
5377  return true;
5378  }
5379  if (words.size() == 2 && words[0] == "Merge") {
5380  debug.CTP = std::stoi(words[1]);
5381  tcc.dbgMrg = true;
5382  tcc.modes[kDebug] = true;
5383  return true;
5384  }
5385  if (words.size() == 2 && words[0] == "2V") {
5386  debug.CTP = std::stoi(words[1]);
5387  tcc.dbg2V = true;
5388  tcc.modes[kDebug] = true;
5389  return true;
5390  }
5391  if (words.size() == 2 && words[0] == "2S") {
5392  debug.CTP = std::stoi(words[1]);
5393  tcc.dbg2S = true;
5394  tcc.modes[kDebug] = true;
5395  return true;
5396  }
5397  // Slice could apply to several debug options.
5398  if (words.size() == 2 && words[0] == "SubSlice") {
5399  debug.Slice = std::stoi(words[1]);
5400  return true;
5401  }
5402  return false;
5403  } // DecodeDebugString
5404 
5405  // ****************************** Printing ******************************
5406 
5407  void
5409  {
5410  // Dump all of the points in a trajectory to the output in a form that can
5411  // be imported by another application, e.g. Excel
5412  // Search for the trajectory with the specified WorkID or Unique ID
5413 
5414  for (auto& slc : slices) {
5415  for (auto& tj : slc.tjs) {
5416  if (tj.WorkID != debug.WorkID && tj.UID != debug.WorkID) continue;
5417  // print a header
5418  std::ofstream outfile;
5419  std::string fname = "tcdump" + std::to_string(tj.UID) + ".csv";
5420  outfile.open(fname, std::ios::out | std::ios::trunc);
5421  outfile << "Dump trajectory T" << tj.UID << " WorkID " << tj.WorkID;
5422  outfile << " ChgRMS " << std::setprecision(2) << tj.ChgRMS;
5423  outfile << "\n";
5424  outfile << "Wire, Chg T" << tj.UID
5425  << ", totChg, Tick, Delta, NTPsFit, Ang, ChiDOF, KinkSig, HitPosErr\n";
5426  for (unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
5427  auto& tp = tj.Pts[ipt];
5428  outfile << std::fixed;
5429  outfile << std::setprecision(0) << std::nearbyint(tp.Pos[0]);
5430  outfile << "," << (int)tp.Chg;
5431  // total charge near the TP
5432  float totChg = 0;
5433  for (auto iht : tp.Hits) {
5434  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
5435  totChg += hit.Integral();
5436  }
5437  outfile << "," << (int)totChg;
5438  outfile << "," << std::setprecision(0) << std::nearbyint(tp.Pos[1] / tcc.unitsPerTick);
5439  outfile << "," << std::setprecision(2) << tp.Delta;
5440  outfile << "," << tp.NTPsFit;
5441  outfile << "," << std::setprecision(3) << tp.Ang;
5442  outfile << "," << std::setprecision(2) << tp.FitChi;
5443  outfile << "," << std::setprecision(2) << tp.KinkSig;
5444  outfile << "," << std::setprecision(2) << sqrt(tp.HitPosErr2);
5445  outfile << "\n";
5446  } // ipt
5447  outfile.close();
5448  std::cout << "Points on T" << tj.UID << " dumped to " << fname << "\n";
5449  tcc.dbgDump = false;
5450  return;
5451  } // tj
5452  } // slc
5453 
5454  } // DumpTj
5455 
5456  ////////////////////////////////////////////////
5457  void
5459  {
5460  // print the debug mode configuration to the screen
5461  std::cout << "*** TrajCluster debug mode configuration in";
5462  std::cout << " CTP=";
5463  if (debug.CTP == UINT_MAX) { std::cout << "NA"; }
5464  else {
5465  std::cout << debug.CTP;
5466  }
5467  std::cout << " Cryostat=" << debug.Cryostat;
5468  std::cout << " TPC=" << debug.TPC;
5469  std::cout << " Plane=" << debug.Plane;
5470  std::cout << " Wire=" << debug.Wire;
5471  std::cout << " Tick=" << debug.Tick;
5472  std::cout << " Hit=";
5473  if (debug.Hit == UINT_MAX) { std::cout << "NA"; }
5474  else {
5475  std::cout << debug.Hit;
5476  }
5477  std::cout << " WorkID=";
5478  if (debug.WorkID == 0) { std::cout << "NA"; }
5479  else {
5480  std::cout << debug.WorkID;
5481  }
5482  std::cout << " Slice=";
5483  if (debug.Slice == -1) { std::cout << "All"; }
5484  else {
5485  std::cout << debug.Slice;
5486  }
5487  std::cout << "\n";
5488  std::cout << "*** tcc.dbg modes:";
5489  if (tcc.dbgSlc) std::cout << " dbgSlc";
5490  if (tcc.dbgStp) std::cout << " dbgStp";
5491  if (tcc.dbgMrg) std::cout << " dbgMrg";
5492  if (tcc.dbg2V) std::cout << " dbg2V";
5493  if (tcc.dbg2S) std::cout << " dbg2S";
5494  if (tcc.dbgVxNeutral) std::cout << " dbgVxNeutral";
5495  if (tcc.dbgVxMerge) std::cout << " dbgVxMerge";
5496  if (tcc.dbgVxJunk) std::cout << " dbgVxJunk";
5497  if (tcc.dbg3V) std::cout << " dbg3V";
5498  if (tcc.dbgPFP) std::cout << " dbgPFP";
5499  if (tcc.dbgDeltaRayTag) std::cout << " dbgDeltaRayTag";
5500  if (tcc.dbgMuonTag) std::cout << " dbgMuonTag";
5501  if (tcc.dbgStitch) std::cout << " dbgStitch";
5502  if (tcc.dbgSummary) std::cout << " dbgSummary";
5503  if (tcc.dbgDump) std::cout << " dbgDump";
5504  std::cout << "\n";
5505  std::cout << "*** Using algs:";
5506  unsigned short cnt = 0;
5507  for (unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) {
5508  if (tcc.useAlg[ib] && ib != kKilled) {
5509  ++cnt;
5510  if (cnt % 10 == 0) std::cout << "\n ";
5511  std::cout << " " << AlgBitNames[ib];
5512  }
5513  }
5514  std::cout << "\n";
5515  std::cout << "*** Skipping algs:";
5516  cnt = 0;
5517  for (unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) {
5518  if (!tcc.useAlg[ib] && ib != kKilled) {
5519  ++cnt;
5520  if (cnt % 10 == 0) std::cout << "\n ";
5521  std::cout << " " << AlgBitNames[ib];
5522  }
5523  }
5524  std::cout << "\n";
5525  } // PrintDebugMode
5526 
5527  ////////////////////////////////////////////////
5528  void
5530  {
5531  // print everything in all slices
5532  bool prt3V = false;
5533  bool prt2V = false;
5534  bool prtT = false;
5535  bool prtP = false;
5536  bool prtS3 = false;
5537  for (size_t isl = 0; isl < slices.size(); ++isl) {
5538  if (debug.Slice >= 0 && int(isl) != debug.Slice) continue;
5539  auto& slc = slices[isl];
5540  if (!slc.vtx3s.empty()) prt3V = true;
5541  if (!slc.vtxs.empty()) prt2V = true;
5542  if (!slc.tjs.empty()) prtT = true;
5543  if (!slc.pfps.empty()) prtP = true;
5544  if (!slc.showers.empty()) prtS3 = true;
5545  } // slc
5546  mf::LogVerbatim myprt("TC");
5547  myprt << "Debug report from caller " << someText << "\n";
5548  myprt << " 'prodID' = <sliceID>:<subSliceIndex>:<productID>/<productUID>\n";
5549  if (prtS3) {
5550  myprt << "************ Showers ************\n";
5551  myprt << " prodID Vtx parUID ___ChgPos____ ______Dir_____ ____posInPln____ "
5552  "___projInPln____ 2D shower UIDs\n";
5553  for (size_t isl = 0; isl < slices.size(); ++isl) {
5554  if (debug.Slice >= 0 && int(isl) != debug.Slice) continue;
5555  auto& slc = slices[isl];
5556  if (slc.showers.empty()) continue;
5557  for (auto& ss3 : slc.showers)
5558  Print3S(detProp, someText, myprt, ss3);
5559  } // slc
5560  } // prtS3
5561  if (prtP) {
5562  bool printHeader = true;
5563  for (size_t isl = 0; isl < slices.size(); ++isl) {
5564  if (debug.Slice >= 0 && int(isl) != debug.Slice) continue;
5565  auto& slc = slices[isl];
5566  if (slc.pfps.empty()) continue;
5567  for (auto& pfp : slc.pfps)
5568  PrintP(someText, myprt, pfp, printHeader);
5569  } // slc
5570  } // prtS3
5571  if (prt3V) {
5572  bool printHeader = true;
5573  myprt << "****** 3D vertices "
5574  "******************************************__2DVtx_UID__*******\n";
5575  myprt << " prodID Cstat TPC X Y Z XEr YEr "
5576  "ZEr pln0 pln1 pln2 Wire score Prim? Nu? nTru";
5577  myprt << " ___________2D_Pos____________ _____Tj UIDs________\n";
5578  for (size_t isl = 0; isl < slices.size(); ++isl) {
5579  if (debug.Slice >= 0 && int(isl) != debug.Slice) continue;
5580  auto& slc = slices[isl];
5581  if (slc.vtx3s.empty()) continue;
5582  for (auto& vx3 : slc.vtx3s)
5583  Print3V(detProp, someText, myprt, vx3, printHeader);
5584  } // slc
5585  } // prt3V
5586  if (prt2V) {
5587  bool printHeader = true;
5588  myprt << "************ 2D vertices ************\n";
5589  myprt << " prodID CTP wire err tick err ChiDOF NTj Pass "
5590  " Topo ChgFrac Score v3D Tj UIDs\n";
5591  for (size_t isl = 0; isl < slices.size(); ++isl) {
5592  if (debug.Slice >= 0 && int(isl) != debug.Slice) continue;
5593  auto& slc = slices[isl];
5594  if (slc.vtxs.empty()) continue;
5595  for (auto& vx2 : slc.vtxs)
5596  Print2V(someText, myprt, vx2, printHeader);
5597  } // slc
5598  } // prt2V
5599  if (prtT) {
5600  bool printHeader = true;
5601  for (size_t isl = 0; isl < slices.size(); ++isl) {
5602  if (debug.Slice >= 0 && int(isl) != debug.Slice) continue;
5603  auto& slc = slices[isl];
5604  if (slc.tjs.empty()) continue;
5605  for (auto& tj : slc.tjs)
5606  PrintT(someText, myprt, tj, printHeader);
5607  } // slc
5608  } // prtT
5609  } // PrintAll
5610 
5611  ////////////////////////////////////////////////
5612  void
5613  PrintP(std::string someText, mf::LogVerbatim& myprt, PFPStruct& pfp, bool& printHeader)
5614  {
5615  if (pfp.ID <= 0) return;
5616  if (printHeader) {
5617  myprt << "************ PFParticles ************\n";
5618  myprt << " prodID sVx _____sPos____ CS _______sDir______ ____sdEdx_____ eVx "
5619  "_____ePos____ CS ____edEdx_____ MVI MCSMom Len nTP3 nSec SLk? PDG Par \n";
5620  printHeader = false;
5621  } // printHeader
5622  auto sIndx = GetSliceIndex("P", pfp.UID);
5623  if (sIndx.first == USHRT_MAX) return;
5624  auto& slc = slices[sIndx.first];
5625  std::string str =
5626  std::to_string(slc.ID) + ":" + std::to_string(sIndx.first) + ":" + std::to_string(pfp.ID);
5627  str += "/" + std::to_string(pfp.UID);
5628  myprt << std::setw(12) << str;
5629  // start and end stuff
5630  for (unsigned short end = 0; end < 2; ++end) {
5631  str = "--";
5632  if (pfp.Vx3ID[end] > 0) str = "3V" + std::to_string(slc.vtx3s[pfp.Vx3ID[end] - 1].UID);
5633  myprt << std::setw(6) << str;
5634  myprt << std::fixed << std::right << std::setprecision(0);
5635  auto pos = PosAtEnd(pfp, end);
5636  myprt << std::setw(5) << pos[0];
5637  myprt << std::setw(5) << pos[1];
5638  myprt << std::setw(5) << pos[2];
5639  // print character for Outside or Inside the FV
5640  if (InsideFV(slc, pfp, end)) { myprt << " I"; }
5641  else {
5642  myprt << " O";
5643  }
5644  // only print the starting direction
5645  if (end == 0) {
5646  myprt << std::fixed << std::right << std::setprecision(2);
5647  auto dir = DirAtEnd(pfp, end);
5648  myprt << std::setw(6) << dir[0];
5649  myprt << std::setw(6) << dir[1];
5650  myprt << std::setw(6) << dir[2];
5651  } // end == 0
5652  for (auto& dedx : pfp.dEdx[end]) {
5653  if (dedx < 50) { myprt << std::setw(5) << std::setprecision(1) << dedx; }
5654  else {
5655  myprt << std::setw(5) << std::setprecision(0) << dedx;
5656  }
5657  } // dedx
5658  if (pfp.dEdx[end].size() < 3) {
5659  for (size_t i = 0; i < 3 - pfp.dEdx[end].size(); ++i) {
5660  myprt << std::setw(6) << ' ';
5661  }
5662  }
5663  } // startend
5664  myprt << std::setw(6) << pfp.MVI;
5665  // global stuff
5666  myprt << std::setw(7) << MCSMom(slc, pfp.TjIDs);
5667  float length = Length(pfp);
5668  if (length < 100) { myprt << std::setw(5) << std::setprecision(1) << length; }
5669  else {
5670  myprt << std::setw(5) << std::setprecision(0) << length;
5671  }
5672  myprt << std::setw(5) << pfp.TP3Ds.size();
5673  myprt << std::setw(5) << pfp.SectionFits.size();
5674  myprt << std::setw(5) << IsShowerLike(slc, pfp.TjIDs);
5675  myprt << std::setw(5) << pfp.PDGCode;
5676  myprt << std::setw(4) << pfp.ParentUID;
5677  if (!pfp.TjIDs.empty()) {
5678  if (pfp.TjUIDs.empty()) {
5679  // print Tjs in one TPC
5680  for (auto tjid : pfp.TjIDs)
5681  myprt << " TU" << slc.tjs[tjid - 1].UID;
5682  }
5683  else {
5684  // print Tjs in all TPCs (if this is called after FinishEvent)
5685  for (auto tjuid : pfp.TjUIDs)
5686  myprt << " TU" << tjuid;
5687  }
5688  } // TjIDs exist
5689  if (!pfp.DtrUIDs.empty()) {
5690  myprt << " dtrs";
5691  for (auto dtruid : pfp.DtrUIDs)
5692  myprt << " PU" << dtruid;
5693  } // dtr ids exist
5694  myprt << "\n";
5695  } // PrintP
5696 
5697  ////////////////////////////////////////////////
5698  void
5700  std::string someText,
5701  mf::LogVerbatim& myprt,
5702  Vtx3Store& vx3,
5703  bool& printHeader)
5704  {
5705  // print a 3D vertex on one line
5706  if (vx3.ID <= 0) return;
5707  auto sIndx = GetSliceIndex("3V", vx3.UID);
5708  if (sIndx.first == USHRT_MAX) return;
5709  auto& slc = slices[sIndx.first];
5710  if (printHeader) {
5711  myprt
5712  << "****** 3D vertices ******************************************__2DVtx_UID__*******\n";
5713  myprt << " prodID Cstat TPC X Y Z pln0 pln1 pln2 Wire score "
5714  "Prim? Nu? nTru";
5715  myprt << " ___________2D_Pos____________ _____Tj UIDs________\n";
5716  printHeader = false;
5717  }
5718  std::string str = "3V" + std::to_string(vx3.ID) + "/3VU" + std::to_string(vx3.UID);
5719  myprt << std::right << std::setw(12) << std::fixed << str;
5720  myprt << std::setprecision(0);
5721  myprt << std::right << std::setw(7) << vx3.TPCID.Cryostat;
5722  myprt << std::right << std::setw(5) << vx3.TPCID.TPC;
5723  myprt << std::right << std::setw(8) << vx3.X;
5724  myprt << std::right << std::setw(8) << vx3.Y;
5725  myprt << std::right << std::setw(8) << vx3.Z;
5726  for (auto vx2id : vx3.Vx2ID) {
5727  if (vx2id > 0) {
5728  str = "2VU" + std::to_string(slc.vtxs[vx2id - 1].UID);
5729  myprt << std::right << std::setw(7) << str;
5730  }
5731  else {
5732  myprt << " --";
5733  }
5734  } // vx2id
5735  myprt << std::right << std::setw(5) << vx3.Wire;
5736  unsigned short nTruMatch = 0;
5737  for (unsigned short ipl = 0; ipl < slc.nPlanes; ++ipl) {
5738  if (vx3.Vx2ID[ipl] == 0) continue;
5739  unsigned short iv2 = vx3.Vx2ID[ipl] - 1;
5740  if (slc.vtxs[iv2].Stat[kVxTruMatch]) ++nTruMatch;
5741  } // ipl
5742  myprt << std::right << std::setw(6) << std::setprecision(1) << vx3.Score;
5743  myprt << std::setw(6) << vx3.Primary;
5744  myprt << std::setw(4) << vx3.Neutrino;
5745  myprt << std::right << std::setw(5) << nTruMatch;
5746  Point2_t pos;
5747  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
5748  PosInPlane(detProp, slc, vx3, plane, pos);
5749  myprt << " " << PrintPos(slc, pos);
5750  } // plane
5751  if (vx3.Wire == -2) {
5752  // find the Tjs that are attached to it
5753  for (unsigned short end = 0; end < 2; ++end) {
5754  for (auto& pfp : slc.pfps) {
5755  if (pfp.Vx3ID[end] == vx3.ID) {
5756  for (auto tjID : pfp.TjIDs) {
5757  auto& tj = slc.tjs[tjID - 1];
5758  myprt << " T" << tj.UID;
5759  } // tjID
5760  } // pfp.Vx3ID[0] == vx3.ID
5761  } // pfp
5762  } // end
5763  }
5764  else {
5765  auto vxtjs = GetAssns(slc, "3V", vx3.ID, "T");
5766  for (auto tjid : vxtjs) {
5767  auto& tj = slc.tjs[tjid - 1];
5768  myprt << " TU" << tj.UID;
5769  }
5770  } // vx3.Wire != -2
5771  myprt << "\n";
5772  } // Print3V
5773 
5774  ////////////////////////////////////////////////
5775  void
5776  Print2V(std::string someText, mf::LogVerbatim& myprt, VtxStore& vx2, bool& printHeader)
5777  {
5778  // print a 2D vertex on one line
5779  if (vx2.ID <= 0) return;
5780  if (debug.CTP != UINT_MAX && vx2.CTP != debug.CTP) return;
5781  auto sIndx = GetSliceIndex("2V", vx2.UID);
5782  if (sIndx.first == USHRT_MAX) return;
5783  auto& slc = slices[sIndx.first];
5784  if (printHeader) {
5785  myprt << "************ 2D vertices ************\n";
5786  myprt << " prodID CTP wire err tick err ChiDOF NTj Pass Topo ChgFrac Score "
5787  " v3D Tj UIDs\n";
5788  printHeader = false;
5789  }
5790  std::string str = "2V" + std::to_string(vx2.ID) + "/2VU" + std::to_string(vx2.UID);
5791  myprt << std::right << std::setw(12) << std::fixed << str;
5792  myprt << std::right << std::setw(6) << vx2.CTP;
5793  myprt << std::right << std::setw(8) << std::setprecision(0) << std::nearbyint(vx2.Pos[0]);
5794  myprt << std::right << std::setw(5) << std::setprecision(1) << vx2.PosErr[0];
5795  myprt << std::right << std::setw(8) << std::setprecision(0)
5796  << std::nearbyint(vx2.Pos[1] / tcc.unitsPerTick);
5797  myprt << std::right << std::setw(5) << std::setprecision(1) << vx2.PosErr[1] / tcc.unitsPerTick;
5798  myprt << std::right << std::setw(7) << vx2.ChiDOF;
5799  myprt << std::right << std::setw(5) << vx2.NTraj;
5800  myprt << std::right << std::setw(5) << vx2.Pass;
5801  myprt << std::right << std::setw(6) << vx2.Topo;
5802  myprt << std::right << std::setw(9) << std::setprecision(2) << vx2.TjChgFrac;
5803  myprt << std::right << std::setw(6) << std::setprecision(1) << vx2.Score;
5804  int v3id = 0;
5805  if (vx2.Vx3ID > 0) v3id = slc.vtx3s[vx2.Vx3ID - 1].UID;
5806  myprt << std::right << std::setw(5) << v3id;
5807  myprt << " ";
5808  // display the traj IDs
5809  for (unsigned short ii = 0; ii < slc.tjs.size(); ++ii) {
5810  auto const& tj = slc.tjs[ii];
5811  if (tj.AlgMod[kKilled]) continue;
5812  for (unsigned short end = 0; end < 2; ++end) {
5813  if (tj.VtxID[end] != (short)vx2.ID) continue;
5814  std::string tid = " TU" + std::to_string(tj.UID) + "_" + std::to_string(end);
5815  myprt << std::right << std::setw(6) << tid;
5816  } // end
5817  } // ii
5818  myprt << " Stat:";
5819  // Special flags. Ignore the first flag bit (0 = kVxTrjTried) which is done for every vertex
5820  for (unsigned short ib = 1; ib < VtxBitNames.size(); ++ib)
5821  if (vx2.Stat[ib]) myprt << " " << VtxBitNames[ib];
5822  myprt << "\n";
5823  } // Print2V
5824 
5825  ////////////////////////////////////////////////
5826  void
5828  std::string someText,
5829  mf::LogVerbatim& myprt,
5830  ShowerStruct3D& ss3)
5831  {
5832  if (ss3.ID <= 0) return;
5833  auto sIndx = GetSliceIndex("3S", ss3.UID);
5834  if (sIndx.first == USHRT_MAX) return;
5835  auto& slc = slices[sIndx.first];
5836  std::string str =
5837  std::to_string(slc.ID) + ":" + std::to_string(sIndx.first) + ":" + std::to_string(ss3.ID);
5838  str += "/" + std::to_string(ss3.UID);
5839  myprt << std::fixed << std::setw(12) << str;
5840  str = "--";
5841  if (ss3.Vx3ID > 0) str = "3V" + std::to_string(slc.vtx3s[ss3.Vx3ID - 1].UID);
5842  myprt << std::setw(6) << str;
5843  for (unsigned short xyz = 0; xyz < 3; ++xyz)
5844  myprt << std::setprecision(0) << std::setw(5) << ss3.ChgPos[xyz];
5845  for (unsigned short xyz = 0; xyz < 3; ++xyz)
5846  myprt << std::setprecision(2) << std::setw(5) << ss3.Dir[xyz];
5847  std::vector<float> projInPlane(slc.nPlanes);
5848  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
5849  CTP_t inCTP = EncodeCTP(ss3.TPCID.Cryostat, ss3.TPCID.TPC, plane);
5850  auto tp = MakeBareTP(detProp, slc, ss3.ChgPos, ss3.Dir, inCTP);
5851  myprt << " " << PrintPos(slc, tp.Pos);
5852  projInPlane[plane] = tp.Delta;
5853  } // plane
5854  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
5855  myprt << std::setprecision(2) << std::setw(5) << projInPlane[plane];
5856  } // plane
5857  for (auto cid : ss3.CotIDs) {
5858  auto& ss = slc.cots[cid - 1];
5859  str = "2SU" + std::to_string(ss.UID);
5860  myprt << std::setw(5) << str;
5861  } // ci
5862  if (ss3.NeedsUpdate) myprt << " *** Needs update";
5863  myprt << "\n";
5864  } // Print3S
5865 
5866  ////////////////////////////////////////////////
5867  void
5868  PrintT(std::string someText, mf::LogVerbatim& myprt, Trajectory& tj, bool& printHeader)
5869  {
5870  // print a 2D vertex on one line
5871  if (tj.ID <= 0) return;
5872  if (debug.CTP != UINT_MAX && tj.CTP != debug.CTP) return;
5873  if (printHeader) {
5874  myprt << "************ Trajectories ************\n";
5875  myprt << "Tj AngleCode-EndFlag decoder (EF): <AngleCode> + <reason for stopping>";
5876  myprt << " (B=Bragg Peak, V=Vertex, A=AngleKink, C=ChargeKink, T=Trajectory)\n";
5877  myprt << " prodID CTP Pass Pts W:T Ang EF AveQ W:T Ang EF AveQ "
5878  "Chg(k) chgRMS Mom __Vtx__ PDG eLike Par Pri NuPar WorkID \n";
5879  printHeader = false;
5880  }
5881  auto sIndx = GetSliceIndex("T", tj.UID);
5882  if (sIndx.first == USHRT_MAX) return;
5883  auto& slc = slices[sIndx.first];
5884  std::string str = "T" + std::to_string(tj.ID) + "/TU" + std::to_string(tj.UID);
5885  myprt << std::fixed << std::setw(12) << str;
5886  myprt << std::setw(6) << tj.CTP;
5887  myprt << std::setw(5) << tj.Pass;
5888  myprt << std::setw(5) << tj.EndPt[1] - tj.EndPt[0] + 1;
5889  unsigned short endPt0 = tj.EndPt[0];
5890  auto& tp0 = tj.Pts[endPt0];
5891  int itick = tp0.Pos[1] / tcc.unitsPerTick;
5892  if (itick < 0) itick = 0;
5893  myprt << std::setw(6) << (int)(tp0.Pos[0] + 0.5) << ":" << itick; // W:T
5894  if (itick < 10) { myprt << " "; }
5895  if (itick < 100) { myprt << " "; }
5896  if (itick < 1000) { myprt << " "; }
5897  myprt << std::setw(6) << std::setprecision(2) << tp0.Ang;
5898  myprt << std::setw(2) << tp0.AngleCode;
5899  if (tj.EndFlag[0][kBragg]) { myprt << "B"; }
5900  else if (tj.EndFlag[0][kAtVtx]) {
5901  myprt << "V";
5902  }
5903  else if (tj.EndFlag[0][kAtKink]) {
5904  myprt << "K";
5905  }
5906  else if (tj.EndFlag[0][kAtTj]) {
5907  myprt << "T";
5908  }
5909  else {
5910  myprt << " ";
5911  }
5912  myprt << std::setw(5) << (int)tp0.AveChg;
5913  unsigned short endPt1 = tj.EndPt[1];
5914  auto& tp1 = tj.Pts[endPt1];
5915  itick = tp1.Pos[1] / tcc.unitsPerTick;
5916  myprt << std::setw(6) << (int)(tp1.Pos[0] + 0.5) << ":" << itick; // W:T
5917  if (itick < 10) { myprt << " "; }
5918  if (itick < 100) { myprt << " "; }
5919  if (itick < 1000) { myprt << " "; }
5920  myprt << std::setw(6) << std::setprecision(2) << tp1.Ang;
5921  myprt << std::setw(2) << tp1.AngleCode;
5922  if (tj.EndFlag[1][kBragg]) { myprt << "B"; }
5923  else if (tj.EndFlag[1][kAtVtx]) {
5924  myprt << "V";
5925  }
5926  else if (tj.EndFlag[1][kAtKink]) {
5927  myprt << "K";
5928  }
5929  else if (tj.EndFlag[1][kAtTj]) {
5930  myprt << "T";
5931  }
5932  else {
5933  myprt << " ";
5934  }
5935  myprt << std::setw(5) << (int)tp1.AveChg;
5936  myprt << std::setw(7) << std::setprecision(1) << tj.TotChg / 1000;
5937  myprt << std::setw(7) << std::setprecision(2) << tj.ChgRMS;
5938  myprt << std::setw(5) << tj.MCSMom;
5939  int vxid = 0;
5940  if (tj.VtxID[0] > 0) vxid = slc.vtxs[tj.VtxID[0] - 1].UID;
5941  myprt << std::setw(4) << vxid;
5942  vxid = 0;
5943  if (tj.VtxID[1] > 0) vxid = slc.vtxs[tj.VtxID[1] - 1].UID;
5944  myprt << std::setw(4) << vxid;
5945  myprt << std::setw(5) << tj.PDGCode;
5946  myprt << std::setw(7) << std::setprecision(2) << ElectronLikelihood(slc, tj);
5947  myprt << std::setw(5) << tj.ParentID;
5948  myprt << std::setw(5) << PrimaryID(slc, tj);
5949  myprt << std::setw(6) << NeutrinoPrimaryTjID(slc, tj);
5950  myprt << std::setw(7) << tj.WorkID;
5951  for (unsigned short ib = 0; ib < AlgBitNames.size(); ++ib)
5952  if (tj.AlgMod[ib]) myprt << " " << AlgBitNames[ib];
5953  for (unsigned short ib = 0; ib < StrategyBitNames.size(); ++ib)
5954  if (tj.Strategy[ib]) myprt << " " << StrategyBitNames[ib];
5955  myprt << "\n";
5956  } // PrintT
5957 
5958  ////////////////////////////////////////////////
5959  void
5961  std::string someText,
5962  TCSlice& slc,
5963  unsigned short itj,
5964  unsigned short ipt,
5965  bool prtVtx)
5966  {
5967 
5968  mf::LogVerbatim myprt("TC");
5969 
5970  if (prtVtx) {
5971  if (!slc.vtx3s.empty()) {
5972  // print out 3D vertices
5973  myprt
5974  << someText
5975  << "****** 3D vertices ******************************************__2DVtx_ID__*******\n";
5976  myprt << someText
5977  << " Vtx Cstat TPC X Y Z XEr YEr ZEr pln0 pln1 pln2 Wire "
5978  "score Prim? Nu? nTru";
5979  myprt << " ___________2D_Pos____________ _____Tjs________\n";
5980  for (unsigned short iv = 0; iv < slc.vtx3s.size(); ++iv) {
5981  if (slc.vtx3s[iv].ID == 0) continue;
5982  const Vtx3Store& vx3 = slc.vtx3s[iv];
5983  myprt << someText;
5984  std::string vid = "3v" + std::to_string(vx3.ID);
5985  myprt << std::right << std::setw(5) << std::fixed << vid;
5986  myprt << std::setprecision(1);
5987  myprt << std::right << std::setw(7) << vx3.TPCID.Cryostat;
5988  myprt << std::right << std::setw(5) << vx3.TPCID.TPC;
5989  myprt << std::right << std::setw(8) << vx3.X;
5990  myprt << std::right << std::setw(8) << vx3.Y;
5991  myprt << std::right << std::setw(8) << vx3.Z;
5992  myprt << std::right << std::setw(5) << vx3.XErr;
5993  myprt << std::right << std::setw(5) << vx3.YErr;
5994  myprt << std::right << std::setw(5) << vx3.ZErr;
5995  myprt << std::right << std::setw(5) << vx3.Vx2ID[0];
5996  myprt << std::right << std::setw(5) << vx3.Vx2ID[1];
5997  myprt << std::right << std::setw(5) << vx3.Vx2ID[2];
5998  myprt << std::right << std::setw(5) << vx3.Wire;
5999  unsigned short nTruMatch = 0;
6000  for (unsigned short ipl = 0; ipl < slc.nPlanes; ++ipl) {
6001  if (vx3.Vx2ID[ipl] == 0) continue;
6002  unsigned short iv2 = vx3.Vx2ID[ipl] - 1;
6003  if (slc.vtxs[iv2].Stat[kVxTruMatch]) ++nTruMatch;
6004  } // ipl
6005  myprt << std::right << std::setw(6) << std::setprecision(1) << vx3.Score;
6006  myprt << std::setw(6) << vx3.Primary;
6007  myprt << std::setw(4) << vx3.Neutrino;
6008  myprt << std::right << std::setw(5) << nTruMatch;
6009  Point2_t pos;
6010  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
6011  PosInPlane(detProp, slc, vx3, plane, pos);
6012  myprt << " " << PrintPos(slc, pos);
6013  } // plane
6014  if (vx3.Wire == -2) {
6015  // find the Tjs that are attached to it
6016  for (auto& pfp : slc.pfps) {
6017  if (pfp.Vx3ID[0] == slc.vtx3s[iv].ID) {
6018  for (auto& tjID : pfp.TjIDs)
6019  myprt << " t" << tjID;
6020  }
6021  if (pfp.Vx3ID[1] == slc.vtx3s[iv].ID) {
6022  for (auto& tjID : pfp.TjIDs)
6023  myprt << " t" << tjID;
6024  }
6025  } // ipfp
6026  }
6027  else {
6028  auto vxtjs = GetAssns(slc, "3V", vx3.ID, "T");
6029  for (auto tjid : vxtjs)
6030  myprt << " t" << tjid;
6031  }
6032  myprt << "\n";
6033  }
6034  } // slc.vtx3s.size
6035  if (!slc.vtxs.empty()) {
6036  bool foundOne = false;
6037  for (unsigned short iv = 0; iv < slc.vtxs.size(); ++iv) {
6038  auto& vx2 = slc.vtxs[iv];
6039  if (debug.Plane < 3 && debug.Plane != (int)DecodeCTP(vx2.CTP).Plane) continue;
6040  if (vx2.NTraj == 0) continue;
6041  foundOne = true;
6042  } // iv
6043  if (foundOne) {
6044  // print out 2D vertices
6045  myprt << someText << "************ 2D vertices ************\n";
6046  myprt << someText
6047  << " ID CTP wire err tick err ChiDOF NTj Pass Topo ChgFrac Score v3D "
6048  "TjIDs\n";
6049  for (auto& vx2 : slc.vtxs) {
6050  if (vx2.ID == 0) continue;
6051  if (debug.Plane < 3 && debug.Plane != (int)DecodeCTP(vx2.CTP).Plane) continue;
6052  myprt << someText;
6053  std::string vid = "2v" + std::to_string(vx2.ID);
6054  myprt << std::right << std::setw(5) << std::fixed << vid;
6055  myprt << std::right << std::setw(6) << vx2.CTP;
6056  myprt << std::right << std::setw(8) << std::setprecision(0)
6057  << std::nearbyint(vx2.Pos[0]);
6058  myprt << std::right << std::setw(5) << std::setprecision(1) << vx2.PosErr[0];
6059  myprt << std::right << std::setw(8) << std::setprecision(0)
6060  << std::nearbyint(vx2.Pos[1] / tcc.unitsPerTick);
6061  myprt << std::right << std::setw(5) << std::setprecision(1)
6062  << vx2.PosErr[1] / tcc.unitsPerTick;
6063  myprt << std::right << std::setw(7) << vx2.ChiDOF;
6064  myprt << std::right << std::setw(5) << vx2.NTraj;
6065  myprt << std::right << std::setw(5) << vx2.Pass;
6066  myprt << std::right << std::setw(6) << vx2.Topo;
6067  myprt << std::right << std::setw(9) << std::setprecision(2) << vx2.TjChgFrac;
6068  myprt << std::right << std::setw(6) << std::setprecision(1) << vx2.Score;
6069  myprt << std::right << std::setw(5) << vx2.Vx3ID;
6070  myprt << " ";
6071  // display the traj IDs
6072  for (unsigned short ii = 0; ii < slc.tjs.size(); ++ii) {
6073  auto const& aTj = slc.tjs[ii];
6074  if (debug.Plane < 3 && debug.Plane != (int)DecodeCTP(aTj.CTP).Plane) continue;
6075  if (aTj.AlgMod[kKilled]) continue;
6076  for (unsigned short end = 0; end < 2; ++end) {
6077  if (aTj.VtxID[end] != (short)vx2.ID) continue;
6078  std::string tid = " t" + std::to_string(aTj.ID) + "_" + std::to_string(end);
6079  myprt << std::right << std::setw(6) << tid;
6080  } // end
6081  } // ii
6082  // Special flags. Ignore the first flag bit (0 = kVxTrjTried) which is done for every vertex
6083  for (unsigned short ib = 1; ib < VtxBitNames.size(); ++ib)
6084  if (vx2.Stat[ib]) myprt << " " << VtxBitNames[ib];
6085  myprt << "\n";
6086  } // iv
6087  }
6088  } // slc.vtxs.size
6089  }
6090 
6091  if (slc.tjs.empty()) {
6092  mf::LogVerbatim("TC") << someText << " No allTraj trajectories to print";
6093  return;
6094  }
6095 
6096  // Print all trajectories in slc.tjs if itj == USHRT_MAX
6097  // Print a single traj (itj) and a single TP (ipt) or all TPs (USHRT_MAX)
6098  if (itj == USHRT_MAX) {
6099  // Print summary trajectory information
6100  myprt << "Tj AngleCode-EndFlag (EF) decoder: <AngleCode> + <reason for stopping>";
6101  myprt << " (B=Bragg Peak, V=Vertex, A=AngleKink, C=ChargeKink, T=Trajectory)\n";
6102  std::vector<unsigned int> tmp;
6103  myprt << someText
6104  << " UID CTP Pass Pts W:T Ang EF AveQ W:T Ang EF AveQ Chg(k) "
6105  "chgRMS Mom SDr __Vtx__ PDG Par Pri NuPar WorkID \n";
6106  for (unsigned short ii = 0; ii < slc.tjs.size(); ++ii) {
6107  auto& aTj = slc.tjs[ii];
6108  if (debug.CTP != UINT_MAX && aTj.CTP != debug.CTP) continue;
6109  myprt << someText << " ";
6110  std::string tid;
6111  if (aTj.AlgMod[kKilled]) { tid = "k" + std::to_string(aTj.UID); }
6112  else {
6113  tid = "t" + std::to_string(aTj.UID);
6114  }
6115  myprt << std::fixed << std::setw(5) << tid;
6116  myprt << std::setw(6) << aTj.CTP;
6117  myprt << std::setw(5) << aTj.Pass;
6118  myprt << std::setw(5) << aTj.EndPt[1] - aTj.EndPt[0] + 1;
6119  unsigned short endPt0 = aTj.EndPt[0];
6120  auto& tp0 = aTj.Pts[endPt0];
6121  int itick = tp0.Pos[1] / tcc.unitsPerTick;
6122  if (itick < 0) itick = 0;
6123  myprt << std::setw(6) << (int)(tp0.Pos[0] + 0.5) << ":" << itick; // W:T
6124  if (itick < 10) { myprt << " "; }
6125  if (itick < 100) { myprt << " "; }
6126  if (itick < 1000) { myprt << " "; }
6127  myprt << std::setw(6) << std::setprecision(2) << tp0.Ang;
6128  myprt << std::setw(2) << tp0.AngleCode;
6129  if (aTj.EndFlag[0][kBragg]) { myprt << "B"; }
6130  else if (aTj.EndFlag[0][kAtVtx]) {
6131  myprt << "V";
6132  }
6133  else if (aTj.EndFlag[0][kAtKink]) {
6134  myprt << "K";
6135  }
6136  else if (aTj.EndFlag[0][kAtTj]) {
6137  myprt << "T";
6138  }
6139  else {
6140  myprt << " ";
6141  }
6142  myprt << std::setw(5) << (int)tp0.AveChg;
6143  unsigned short endPt1 = aTj.EndPt[1];
6144  auto& tp1 = aTj.Pts[endPt1];
6145  itick = tp1.Pos[1] / tcc.unitsPerTick;
6146  myprt << std::setw(6) << (int)(tp1.Pos[0] + 0.5) << ":" << itick; // W:T
6147  if (itick < 10) { myprt << " "; }
6148  if (itick < 100) { myprt << " "; }
6149  if (itick < 1000) { myprt << " "; }
6150  myprt << std::setw(6) << std::setprecision(2) << tp1.Ang;
6151  myprt << std::setw(2) << tp1.AngleCode;
6152  if (aTj.EndFlag[1][kBragg]) { myprt << "B"; }
6153  else if (aTj.EndFlag[1][kAtVtx]) {
6154  myprt << "V";
6155  }
6156  else {
6157  myprt << " ";
6158  }
6159  myprt << std::setw(5) << (int)tp1.AveChg;
6160  myprt << std::setw(7) << std::setprecision(1) << aTj.TotChg / 1000;
6161  myprt << std::setw(7) << std::setprecision(2) << aTj.ChgRMS;
6162  myprt << std::setw(5) << aTj.MCSMom;
6163  myprt << std::setw(4) << aTj.StepDir;
6164  myprt << std::setw(4) << aTj.VtxID[0];
6165  myprt << std::setw(4) << aTj.VtxID[1];
6166  myprt << std::setw(5) << aTj.PDGCode;
6167  myprt << std::setw(5) << aTj.ParentID;
6168  myprt << std::setw(5) << PrimaryID(slc, aTj);
6169  myprt << std::setw(6) << NeutrinoPrimaryTjID(slc, aTj);
6170  myprt << std::setw(7) << aTj.WorkID;
6171  for (unsigned short ib = 0; ib < AlgBitNames.size(); ++ib)
6172  if (aTj.AlgMod[ib]) myprt << " " << AlgBitNames[ib];
6173  myprt << "\n";
6174  } // ii
6175  return;
6176  } // itj > slc.tjs.size()-1
6177 
6178  if (itj > slc.tjs.size() - 1) return;
6179 
6180  auto const& aTj = slc.tjs[itj];
6181 
6182  mf::LogVerbatim("TC") << "Print slc.tjs[" << itj << "] Vtx[0] " << aTj.VtxID[0] << " Vtx[1] "
6183  << aTj.VtxID[1];
6184  myprt << "AlgBits";
6185  for (unsigned short ib = 0; ib < AlgBitNames.size(); ++ib)
6186  if (aTj.AlgMod[ib]) myprt << " " << AlgBitNames[ib];
6187  myprt << "\n";
6188 
6189  PrintTPHeader(someText);
6190  if (ipt == USHRT_MAX) {
6191  // print all points
6192  for (unsigned short ii = 0; ii < aTj.Pts.size(); ++ii)
6193  PrintTP(someText, slc, ii, aTj.StepDir, aTj.Pass, aTj.Pts[ii]);
6194  }
6195  else {
6196  // print just one
6197  PrintTP(someText, slc, ipt, aTj.StepDir, aTj.Pass, aTj.Pts[ipt]);
6198  }
6199  } // PrintAllTraj
6200 
6201  //////////////////////////////////////////
6202  void
6204  const TCSlice& slc,
6205  const Trajectory& tj,
6206  unsigned short tPoint)
6207  {
6208  // prints one or all trajectory points on tj
6209 
6210  if (tPoint == USHRT_MAX) {
6211  if (tj.ID < 0) {
6212  mf::LogVerbatim myprt("TC");
6213  myprt << someText << " ";
6214  myprt << "Work: UID " << tj.UID << " CTP " << tj.CTP << " StepDir " << tj.StepDir
6215  << " PDG " << tj.PDGCode << " slc.vtxs " << tj.VtxID[0] << " " << tj.VtxID[1]
6216  << " nPts " << tj.Pts.size() << " EndPts " << tj.EndPt[0] << " " << tj.EndPt[1];
6217  myprt << " MCSMom " << tj.MCSMom;
6218  myprt << " EndFlags " << PrintEndFlag(tj, 0) << " " << PrintEndFlag(tj, 1);
6219  myprt << " AlgMods:";
6220  for (unsigned short ib = 0; ib < AlgBitNames.size(); ++ib)
6221  if (tj.AlgMod[ib]) myprt << " " << AlgBitNames[ib];
6222  }
6223  else {
6224  mf::LogVerbatim myprt("TC");
6225  myprt << someText << " ";
6226  myprt << "slcID " << slc.ID << " T" << tj.ID << " uT" << tj.UID << " WorkID " << tj.WorkID
6227  << " StepDir " << tj.StepDir << " PDG " << tj.PDGCode << " VtxID " << tj.VtxID[0]
6228  << " " << tj.VtxID[1] << " nPts " << tj.Pts.size() << " EndPts " << tj.EndPt[0] << " "
6229  << tj.EndPt[1];
6230  myprt << " MCSMom " << tj.MCSMom;
6231  myprt << " EndFlags " << PrintEndFlag(tj, 0) << " " << PrintEndFlag(tj, 1);
6232  myprt << " AlgMods:";
6233  for (unsigned short ib = 0; ib < AlgBitNames.size(); ++ib)
6234  if (tj.AlgMod[ib]) myprt << " " << AlgBitNames[ib];
6235  }
6236  PrintTPHeader(someText);
6237  for (unsigned short ipt = 0; ipt < tj.Pts.size(); ++ipt)
6238  PrintTP(someText, slc, ipt, tj.StepDir, tj.Pass, tj.Pts[ipt]);
6239  // See if this trajectory is a shower Tj
6240  if (tj.AlgMod[kShowerTj]) {
6241  for (unsigned short ic = 0; ic < slc.cots.size(); ++ic) {
6242  if (slc.cots[ic].TjIDs.empty()) continue;
6243  // only print out the info for the correct Tj
6244  if (slc.cots[ic].ShowerTjID != tj.ID) continue;
6245  const ShowerStruct& ss = slc.cots[ic];
6246  mf::LogVerbatim myprt("TC");
6247  myprt << "cots index " << ic << " ";
6248  myprt << someText << " Envelope";
6249  if (ss.Envelope.empty()) { myprt << " NA"; }
6250  else {
6251  for (auto& vtx : ss.Envelope)
6252  myprt << " " << (int)vtx[0] << ":" << (int)(vtx[1] / tcc.unitsPerTick);
6253  }
6254  myprt << " Energy " << (int)ss.Energy;
6255  myprt << " Area " << std::fixed << std::setprecision(1) << (int)ss.EnvelopeArea
6256  << " ChgDensity " << ss.ChgDensity;
6257  myprt << "\nInShower TjIDs";
6258  for (auto& tjID : ss.TjIDs) {
6259  myprt << " " << tjID;
6260  } // tjID
6261 
6262  myprt << "\n";
6263  myprt << "NearTjIDs";
6264  for (auto& tjID : ss.NearTjIDs) {
6265  myprt << " " << tjID;
6266  } // tjID
6267  myprt << "\n";
6268  myprt << "\n";
6269  myprt << "Angle " << std::fixed << std::setprecision(2) << ss.Angle << " +/- "
6270  << ss.AngleErr;
6271  myprt << " AspectRatio " << std::fixed << std::setprecision(2) << ss.AspectRatio;
6272  myprt << " DirectionFOM " << std::fixed << std::setprecision(2) << ss.DirectionFOM;
6273  if (ss.ParentID > 0) { myprt << " Parent Tj " << ss.ParentID << " FOM " << ss.ParentFOM; }
6274  else {
6275  myprt << " No parent";
6276  }
6277  myprt << " TruParentID " << ss.TruParentID << " SS3ID " << ss.SS3ID << "\n";
6278  if (ss.NeedsUpdate) myprt << "*********** This shower needs to be updated ***********";
6279  myprt << "................................................";
6280  } // ic
6281  } // Shower Tj
6282  }
6283  else {
6284  // just print one traj point
6285  if (tPoint > tj.Pts.size() - 1) {
6286  mf::LogVerbatim("TC") << "Can't print non-existent traj point " << tPoint;
6287  return;
6288  }
6289  PrintTP(someText, slc, tPoint, tj.StepDir, tj.Pass, tj.Pts[tPoint]);
6290  }
6291  } // PrintTrajectory
6292 
6293  //////////////////////////////////////////
6294  void
6296  {
6297  mf::LogVerbatim("TC") << someText
6298  << " TRP CTP Ind Stp Delta RMS Ang C Err Dir0 Dir1 Q "
6299  " AveQ Pull FitChi NTPF KinkSig Hits ";
6300  } // PrintTPHeader
6301 
6302  ////////////////////////////////////////////////
6303  void
6305  const TCSlice& slc,
6306  unsigned short ipt,
6307  short dir,
6308  unsigned short pass,
6309  const TrajPoint& tp)
6310  {
6311  mf::LogVerbatim myprt("TC");
6312  myprt << someText << " TRP" << std::fixed;
6313  myprt << pass;
6314  if (dir > 0) { myprt << "+"; }
6315  else {
6316  myprt << "-";
6317  }
6318  myprt << std::setw(6) << tp.CTP;
6319  myprt << std::setw(5) << ipt;
6320  myprt << std::setw(5) << tp.Step;
6321  myprt << std::setw(6) << std::setprecision(2) << tp.Delta;
6322  myprt << std::setw(6) << std::setprecision(2) << tp.DeltaRMS;
6323  myprt << std::setw(6) << std::setprecision(2) << tp.Ang;
6324  myprt << std::setw(2) << tp.AngleCode;
6325  myprt << std::setw(6) << std::setprecision(2) << tp.AngErr;
6326  myprt << std::setw(6) << std::setprecision(2) << tp.Dir[0];
6327  myprt << std::setw(6) << std::setprecision(2) << tp.Dir[1];
6328  myprt << std::setw(7) << (int)tp.Chg;
6329  myprt << std::setw(8) << (int)tp.AveChg;
6330  myprt << std::setw(6) << std::setprecision(1) << tp.ChgPull;
6331  myprt << std::setw(7) << tp.FitChi;
6332  myprt << std::setw(6) << tp.NTPsFit;
6333  myprt << std::setw(7) << std::setprecision(3) << tp.KinkSig;
6334  // print the hits associated with this traj point
6335  if (tp.Hits.size() > 16) {
6336  // don't print too many hits (e.g. from a shower Tj)
6337  myprt << " " << tp.Hits.size() << " shower hits";
6338  }
6339  else {
6340  for (unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
6341  unsigned int iht = tp.Hits[ii];
6342  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
6343  myprt << " " << hit.WireID().Wire << ":" << (int)hit.PeakTime();
6344  if (tp.UseHit[ii]) {
6345  // Distinguish used hits from nearby hits
6346  myprt << "_";
6347  }
6348  else {
6349  myprt << "x";
6350  }
6351  myprt << "T" << slc.slHits[iht].InTraj;
6352  } // iht
6353  if (tp.InPFP > 0) myprt << " inP" << tp.InPFP;
6354  }
6355  // print Environment
6356  if (tp.Environment.any()) myprt << " Env: " << TPEnvString(tp);
6357  } // PrintTP
6358 
6359  /////////////////////////////////////////
6360  std::string
6362  {
6363  // Print environment bits in human-readable format
6364  std::string str = "";
6365  for (unsigned short ib = 0; ib < 8; ++ib) {
6366  // There aren't any bit names for Environment_t
6367  if (!tp.Environment[ib]) continue;
6368  if (ib == kEnvNotGoodWire) str += " NoGdwire";
6369  if (ib == kEnvNearMuon) str += " NearMuon";
6370  if (ib == kEnvNearShower) str += " NearShower";
6371  if (ib == kEnvOverlap) str += " Overlap";
6372  if (ib == kEnvUnusedHits) str += " UnusedHits";
6373  if (ib == kEnvNearSrcHit) str += " NearSrcHit";
6374  if (ib == kEnvFlag) str += " Flag";
6375  } // ib
6376  return str;
6377  } // TPEnvironment
6378 
6379  /////////////////////////////////////////
6380  void
6381  PrintPFP(std::string someText, TCSlice& slc, const PFPStruct& pfp, bool printHeader)
6382  {
6383  mf::LogVerbatim myprt("TC");
6384  if (printHeader) {
6385  myprt << someText;
6386  myprt << " PFP sVx ________sPos_______ EF _______sDir______ ____sdEdx_____ eVx "
6387  "________ePos_______ EF _______eDir______ ____edEdx____ Len nTp3 MCSMom ShLike? "
6388  "PDG Par Prim\n";
6389  }
6390  myprt << someText;
6391  std::string pid = "P" + std::to_string(pfp.ID);
6392  myprt << std::setw(5) << pid;
6393  // start and end stuff
6394  for (unsigned short end = 0; end < 2; ++end) {
6395  myprt << std::setw(4) << pfp.Vx3ID[end];
6396  myprt << std::fixed << std::right << std::setprecision(1);
6397  auto pos = PosAtEnd(pfp, end);
6398  myprt << std::setw(7) << pos[0];
6399  myprt << std::setw(7) << pos[1];
6400  myprt << std::setw(7) << pos[2];
6401  // print characters that encode the EndFlag
6402  std::string ef;
6403  if (pfp.EndFlag[end][kOutFV]) { ef = "O"; }
6404  else {
6405  ef = "I";
6406  }
6407  if (pfp.EndFlag[end][kBragg]) ef += "B";
6408  myprt << std::setw(6) << ef;
6409  myprt << std::fixed << std::right << std::setprecision(2);
6410  auto dir = DirAtEnd(pfp, end);
6411  myprt << std::setw(6) << dir[0];
6412  myprt << std::setw(6) << dir[1];
6413  myprt << std::setw(6) << dir[2];
6414  for (auto& dedx : pfp.dEdx[end]) {
6415  if (dedx < 50) { myprt << std::setw(5) << std::setprecision(1) << dedx; }
6416  else {
6417  myprt << std::setw(5) << std::setprecision(0) << dedx;
6418  }
6419  } // dedx
6420  if (pfp.dEdx[end].size() < 3) {
6421  for (size_t i = 0; i < 3 - pfp.dEdx[end].size(); ++i) {
6422  myprt << std::setw(6) << ' ';
6423  }
6424  }
6425  } // startend
6426  // global stuff
6427  float length = Length(pfp);
6428  if (length < 100) { myprt << std::setw(5) << std::setprecision(1) << length; }
6429  else {
6430  myprt << std::setw(5) << std::setprecision(0) << length;
6431  }
6432  myprt << std::setw(5) << std::setprecision(2) << pfp.TP3Ds.size();
6433  myprt << std::setw(7) << MCSMom(slc, pfp.TjIDs);
6434  myprt << std::setw(5) << IsShowerLike(slc, pfp.TjIDs);
6435  myprt << std::setw(5) << pfp.PDGCode;
6436  myprt << " NA";
6437  myprt << std::setw(4) << pfp.ParentUID;
6438  myprt << std::setw(5) << PrimaryUID(slc, pfp);
6439  if (!pfp.TjIDs.empty()) {
6440  for (auto& tjID : pfp.TjIDs)
6441  myprt << " T" << tjID;
6442  }
6443  if (!pfp.DtrUIDs.empty()) {
6444  myprt << " dtrs";
6445  for (auto& dtrUID : pfp.DtrUIDs)
6446  myprt << " P" << dtrUID;
6447  }
6448  } // PrintPFP
6449 
6450  /////////////////////////////////////////
6451  void
6453  {
6454  if (slc.pfps.empty()) return;
6455 
6456  mf::LogVerbatim myprt("TC");
6457  myprt << someText;
6458  myprt
6459  << " PFP sVx ________sPos_______ ______sDir______ ______sdEdx_____ eVx "
6460  "________ePos_______ ______eDir______ ______edEdx_____ BstPln PDG TruPDG Par Prim E*P\n";
6461  bool printHeader = true;
6462  for (auto& pfp : slc.pfps) {
6463  PrintPFP(someText, slc, pfp, printHeader);
6464  printHeader = false;
6465  } // im
6466 
6467  } // PrintPFPs
6468 
6469  /////////////////////////////////////////
6470  std::string
6471  PrintEndFlag(const PFPStruct& pfp, unsigned short end)
6472  {
6473  if (end > 1) return "Invalid end";
6474  std::string tmp;
6475  bool first = true;
6476  for (unsigned short ib = 0; ib < EndFlagNames.size(); ++ib) {
6477  if (pfp.EndFlag[end][ib]) {
6478  if (first) {
6479  tmp = std::to_string(end) + ":" + EndFlagNames[ib];
6480  first = false;
6481  }
6482  else {
6483  tmp += "," + EndFlagNames[ib];
6484  }
6485  }
6486  } // ib
6487  if (first) tmp = " none";
6488  return tmp;
6489  } // PrintEndFlag
6490 
6491  /////////////////////////////////////////
6492  std::string
6493  PrintEndFlag(const Trajectory& tj, unsigned short end)
6494  {
6495  if (end > 1) return "Invalid end";
6496  std::string tmp;
6497  bool first = true;
6498  for (unsigned short ib = 0; ib < EndFlagNames.size(); ++ib) {
6499  if (tj.EndFlag[end][ib]) {
6500  if (first) {
6501  tmp = std::to_string(end) + ":" + EndFlagNames[ib];
6502  first = false;
6503  }
6504  else {
6505  tmp += "," + EndFlagNames[ib];
6506  }
6507  }
6508  } // ib
6509  return tmp;
6510  } // PrintEndFlag
6511 
6512  /////////////////////////////////////////
6513  std::string
6514  PrintHitShort(const TCHit& tch)
6515  {
6516  if (tch.allHitsIndex > (*evt.allHits).size() - 1) return "NA";
6517  auto& hit = (*evt.allHits)[tch.allHitsIndex];
6518  return std::to_string(hit.WireID().Plane) + ":" + std::to_string(hit.WireID().Wire) + ":" +
6519  std::to_string((int)hit.PeakTime());
6520  } // PrintHit
6521 
6522  /////////////////////////////////////////
6523  std::string
6524  PrintHit(const TCHit& tch)
6525  {
6526  if (tch.allHitsIndex > (*evt.allHits).size() - 1) return "NA";
6527  auto& hit = (*evt.allHits)[tch.allHitsIndex];
6528  return std::to_string(hit.WireID().Plane) + ":" + std::to_string(hit.WireID().Wire) + ":" +
6529  std::to_string((int)hit.PeakTime()) + "_" + std::to_string(tch.InTraj);
6530  } // PrintHit
6531 
6532  /////////////////////////////////////////
6533  std::string
6534  PrintPos(const TCSlice& slc, const TrajPoint& tp)
6535  {
6536  return std::to_string(DecodeCTP(tp.CTP).Plane) + ":" + PrintPos(slc, tp.Pos);
6537  } // PrintPos
6538 
6539  /////////////////////////////////////////
6540  std::string
6541  PrintPos(const TCSlice& slc, const Point2_t& pos)
6542  {
6543  unsigned int wire = 0;
6544  if (pos[0] > -0.4) wire = std::nearbyint(pos[0]);
6545  int time = std::nearbyint(pos[1] / tcc.unitsPerTick);
6546  return std::to_string(wire) + ":" + std::to_string(time);
6547  } // PrintPos
6548 
6549 } // namespace tca
Expect tracks entering from the front face. Don&#39;t create neutrino PFParticles.
Definition: DataStructs.h:524
std::bitset< 16 > UseHit
Definition: DataStructs.h:171
void PrintAll(detinfo::DetectorPropertiesData const &detProp, std::string someText)
Definition: Utils.cxx:5529
geo::Length_t WireCoordinate(double YPos, double ZPos, geo::PlaneID const &planeid) const
Returns the index of the nearest wire to the specified position.
bool valDecreasing(SortEntry c1, SortEntry c2)
Definition: TCVertex.cxx:33
end
while True: pbar.update(maxval-len(onlies[E][S])) #print iS, "/", len(onlies[E][S]) found = False for...
Vector2_t Dir
Definition: DataStructs.h:154
float HitsPosTime(const TCSlice &slc, const std::vector< unsigned int > &hitsInMultiplet, float &sum, HitStatus_t hitRequest)
Definition: Utils.cxx:4287
void CheckTrajBeginChg(TCSlice &slc, unsigned short itj)
Definition: Utils.cxx:1345
float AveChg
Calculated using ALL hits.
Definition: DataStructs.h:195
void ReleaseHits(TCSlice &slc, Trajectory &tj)
Definition: Utils.cxx:1070
Point2_t Pos
Definition: DataStructs.h:153
int PDGCodeVote(const TCSlice &slc, const std::vector< int > &tjIDs)
Definition: Utils.cxx:415
Point2_t PosErr
Definition: DataStructs.h:72
span(IterB &&b, IterE &&e, Adaptor &&adaptor) -> span< decltype(adaptor(std::forward< IterB >(b))), decltype(adaptor(std::forward< IterE >(e))) >
MaybeLogger_< ELseverityLevel::ELsev_info, true > LogVerbatim
std::vector< Trajectory > tjs
vector of all trajectories in each plane
Definition: DataStructs.h:662
Point2_t dEdx
dE/dx for 3D matched trajectories
Definition: DataStructs.h:199
float Length(const PFPStruct &pfp)
Definition: PFPUtils.cxx:3171
bool dbgStitch
debug PFParticle stitching
Definition: DataStructs.h:595
void SetEndPoints(Trajectory &tj)
Definition: Utils.cxx:3421
float PointPull(TCSlice &slc, Point2_t pos, float chg, const Trajectory &tj)
Definition: Utils.cxx:554
void FitPar(const TCSlice &slc, const Trajectory &tj, unsigned short originPt, unsigned short npts, short fitDir, ParFit &pFit, unsigned short usePar)
Definition: Utils.cxx:1227
bool MakeVertexObsolete(std::string fcnLabel, TCSlice &slc, VtxStore &vx2, bool forceKill)
Definition: TCVertex.cxx:2740
std::vector< float > kinkCuts
kink finder algorithm
Definition: DataStructs.h:551
geo::TPCID TPCID
Definition: DataStructs.h:613
float ParSlpErr
Definition: DataStructs.h:182
bool InTrajOK(TCSlice &slc, std::string someText)
Definition: Utils.cxx:1284
bool FillWireHitRange(detinfo::DetectorClocksData const &clockData, detinfo::DetectorPropertiesData const &detProp, TCSlice &slc)
Definition: Utils.cxx:4545
geo::Length_t DetHalfWidth(geo::TPCID const &tpcid) const
Returns the half width of the active volume of the specified TPC.
unsigned short NumPtsWithCharge(const TCSlice &slc, const Trajectory &tj, bool includeDeadWires, unsigned short firstPt, unsigned short lastPt)
Definition: Utils.cxx:2133
Point2_t Pos
Definition: DataStructs.h:177
bool InsideFV(const TCSlice &slc, const PFPStruct &pfp, unsigned short end)
Definition: PFPUtils.cxx:2912
unsigned short CloseEnd(const TCSlice &slc, const Trajectory &tj, const Point2_t &pos)
Definition: Utils.cxx:2559
struct of temporary 2D vertices (end points)
Definition: DataStructs.h:70
std::vector< int > GetAssns(TCSlice &slc, std::string type1Name, int id, std::string type2Name)
Definition: Utils.cxx:4857
std::vector< unsigned int > PutTrajHitsInVector(const Trajectory &tj, HitStatus_t hitRequest)
Definition: Utils.cxx:2762
const std::vector< std::string > AlgBitNames
Definition: DataStructs.cxx:15
float ChgFracBetween(const TCSlice &slc, TrajPoint tp, float toPos0)
Definition: Utils.cxx:1849
std::vector< ShowerStruct > cots
Definition: DataStructs.h:671
bool AttachAnyVertexToTraj(TCSlice &slc, int tjID, bool prt)
Definition: TCVertex.cxx:1655
CTP_t CTP
Cryostat, TPC, Plane code.
Definition: DataStructs.h:151
double rms(sqlite3 *db, std::string const &table_name, std::string const &column_name)
Definition: statistics.cc:39
std::vector< float > maxPos0
Definition: DataStructs.h:564
short recoTPC
only reconstruct in the seleted TPC
Definition: DataStructs.h:581
unsigned short NTPsFit
Definition: DataStructs.h:167
std::vector< int > NearTjIDs
Definition: DataStructs.h:318
int UID
unique global ID
Definition: DataStructs.h:359
std::array< double, 3 > Point3_t
Definition: DataStructs.h:39
std::vector< ShowerStruct3D > showers
Definition: DataStructs.h:674
bool SignalAtTp(TrajPoint &tp)
Definition: Utils.cxx:2012
unsigned int index
Definition: StepUtils.cxx:26
float PosSep(const Point2_t &pos1, const Point2_t &pos2)
Definition: Utils.cxx:2671
std::vector< Point2_t > Envelope
Definition: DataStructs.h:324
std::string string
Definition: nybbler.cc:12
void SetPDGCode(TCSlice &slc, Trajectory &tj)
Definition: Utils.cxx:4366
void Print2V(std::string someText, mf::LogVerbatim &myprt, VtxStore &vx2, bool &printHeader)
Definition: Utils.cxx:5776
tagged as a vertex between Tjs that are matched to MC truth neutrino interaction particles ...
Definition: DataStructs.h:94
TCConfig tcc
Definition: DataStructs.cxx:8
unsigned short Step
Definition: DataStructs.h:168
void Print3V(detinfo::DetectorPropertiesData const &detProp, std::string someText, mf::LogVerbatim &myprt, Vtx3Store &vx3, bool &printHeader)
Definition: Utils.cxx:5699
void PrintPFP(std::string someText, TCSlice &slc, const PFPStruct &pfp, bool printHeader)
Definition: Utils.cxx:6381
vertex position fixed manually - no fitting done
Definition: DataStructs.h:91
Declaration of signal hit object.
void PrintTrajectory(std::string someText, const TCSlice &slc, const Trajectory &tj, unsigned short tPoint)
Definition: Utils.cxx:6203
void PrintTP(std::string someText, const TCSlice &slc, unsigned short ipt, short dir, unsigned short pass, const TrajPoint &tp)
Definition: Utils.cxx:6304
std::vector< int > Vx2ID
Definition: DataStructs.h:112
std::vector< std::vector< std::pair< unsigned int, unsigned int > > > wireHitRange
Definition: DataStructs.h:667
The data type to uniquely identify a Plane.
Definition: geo_types.h:472
Geometry information for a single TPC.
Definition: TPCGeo.h:38
bool WireHitRangeOK(TCSlice &slc, const CTP_t &inCTP)
Definition: Utils.cxx:4661
void UnsetUsedHits(TCSlice &slc, TrajPoint &tp)
Definition: Utils.cxx:1083
int ParentID
ID of the parent, or the ID of the Tj this one was merged with if it is killed.
Definition: DataStructs.h:193
std::vector< unsigned int > Hits
Definition: DataStructs.h:170
std::vector< int > TjIDs
Definition: DataStructs.h:317
void PrintT(std::string someText, mf::LogVerbatim &myprt, Trajectory &tj, bool &printHeader)
Definition: Utils.cxx:5868
bool StoreTraj(TCSlice &slc, Trajectory &tj)
Definition: Utils.cxx:1097
float TotChg
Total including an estimate for dead wires.
Definition: DataStructs.h:196
double Temperature() const
In kelvin.
short MCSMom
Normalized RMS using ALL hits. Assume it is 50% to start.
Definition: DataStructs.h:198
float TpSumHitChg(const TCSlice &slc, TrajPoint const &tp)
Definition: Utils.cxx:2112
unsigned short Pass
Definition: DataStructs.h:74
bool MakeBareTrajPoint(const TCSlice &slc, const TrajPoint &tpIn1, const TrajPoint &tpIn2, TrajPoint &tpOut)
Definition: Utils.cxx:4171
void FindAlongTrans(Point2_t pos1, Vector2_t dir1, Point2_t pos2, Point2_t &alongTrans)
Definition: Utils.cxx:3368
bool dbgDeltaRayTag
Definition: DataStructs.h:591
void ChkChgAsymmetry(TCSlice &slc, Trajectory &tj, bool prt)
Definition: Utils.cxx:1749
CryostatID_t Cryostat
Index of cryostat.
Definition: geo_types.h:212
float ExpectedHitsRMS(TCSlice &slc, const TrajPoint &tp)
Definition: Utils.cxx:1955
bool TrajClosestApproach(Trajectory const &tj, float x, float y, unsigned short &closePt, float &DOCA)
Definition: Utils.cxx:2698
void PosInPlane(detinfo::DetectorPropertiesData const &detProp, const TCSlice &slc, const Vtx3Store &vx3, unsigned short plane, Point2_t &pos)
Definition: TCVertex.cxx:2893
float TrajPointSeparation(const TrajPoint &tp1, const TrajPoint &tp2)
Definition: Utils.cxx:2688
float length
Definition: TCShower.cxx:31
std::vector< std::pair< unsigned int, unsigned int > > tpcSrcHitRange
Definition: DataStructs.h:620
void SetAngleCode(TrajPoint &tp)
Definition: Utils.cxx:781
void PrintP(std::string someText, mf::LogVerbatim &myprt, PFPStruct &pfp, bool &printHeader)
Definition: Utils.cxx:5613
PFPStruct CreatePFP(const TCSlice &slc)
Definition: PFPUtils.cxx:2699
Point3_t PosAtEnd(const PFPStruct &pfp, unsigned short end)
Definition: PFPUtils.cxx:3160
a general purpose flag bit used in 3D matching
Definition: DataStructs.h:518
bool SetMag(Vector2_t &v1, double mag)
Definition: Utils.cxx:3355
bool dbgSlc
debug only in the user-defined slice? default is all slices
Definition: DataStructs.h:582
string dir
float KinkSignificance(TCSlice &slc, Trajectory &tj, unsigned short kinkPt, unsigned short nPtsFit, bool useChg, bool prt)
Definition: Utils.cxx:3120
std::vector< unsigned int > lastWire
the last wire with a hit
Definition: DataStructs.h:647
HitStatus_t
Definition: Utils.h:37
float GoodnessOfFit() const
Degrees of freedom in the determination of the hit signal shape (-1 by default)
Definition: Hit.h:228
bool LongPulseHit(const recob::Hit &hit)
Definition: Utils.cxx:4460
Particle class.
std::array< int, 2 > Vx3ID
Definition: DataStructs.h:283
float AvePar
Definition: DataStructs.h:179
bool expectSlicedHits
info passed from the module - used to (not) define wireHitRange
Definition: DataStructs.h:640
float PointTrajDOCA(const TCSlice &slc, float wire, float time, TrajPoint const &tp)
Definition: Utils.cxx:2591
short int Multiplicity() const
How many hits could this one be shared with.
Definition: Hit.h:226
float TPHitsRMSTime(const TCSlice &slc, const TrajPoint &tp, HitStatus_t hitRequest)
Definition: Utils.cxx:4210
bool IsShowerLike(TCSlice &slc, const std::vector< int > TjIDs)
Definition: TCShower.cxx:1923
unsigned short NumUsedHitsInTj(const TCSlice &slc, const Trajectory &tj)
Definition: Utils.cxx:4322
std::string TPEnvString(const TrajPoint &tp)
Definition: Utils.cxx:6361
pure virtual base interface for detector clocks
std::vector< int > CotIDs
Definition: DataStructs.h:355
geo::Length_t WirePitch(geo::PlaneID const &planeid) const
Returns the distance between two consecutive wires.
bool StoreVertex(TCSlice &slc, VtxStore &vx)
Definition: TCVertex.cxx:1932
void PrintPFPs(std::string someText, TCSlice &slc)
Definition: Utils.cxx:6452
Q_EXPORT QTSManip setprecision(int p)
Definition: qtextstream.h:343
float MaxTjLen(const TCSlice &slc, std::vector< int > &tjIDs)
Definition: Utils.cxx:2638
void Print3S(detinfo::DetectorPropertiesData const &detProp, std::string someText, mf::LogVerbatim &myprt, ShowerStruct3D &ss3)
Definition: Utils.cxx:5827
unsigned int Nwires(unsigned int p, unsigned int tpc=0, unsigned int cstat=0) const
Returns the total number of wires in the specified plane.
bool MergeTjIntoPFP(TCSlice &slc, int mtjid, PFPStruct &pfp, bool prt)
Definition: Utils.cxx:521
double DeltaAngle2(double Ang1, double Ang2)
Definition: Utils.cxx:3401
std::vector< T > SetIntersection(const std::vector< T > &set1, const std::vector< T > &set2)
Definition: Utils.h:402
std::vector< float > angleRanges
list of max angles for each angle range
Definition: DataStructs.h:561
const std::vector< std::string > EndFlagNames
Definition: DataStructs.cxx:87
std::vector< float > showerTag
shower-like trajectory tagging + shower reconstruction
Definition: DataStructs.h:550
unsigned short Pass
the pass on which it was created
Definition: DataStructs.h:207
bool dbg3V
debug 3D vertex finding
Definition: DataStructs.h:589
double Efield(unsigned int planegap=0) const
kV/cm
bool SignalBetween(const TCSlice &slc, TrajPoint tp, float toPos0, const float &MinWireSignalFraction)
Definition: Utils.cxx:1841
std::string PrintHitShort(const TCHit &tch)
Definition: Utils.cxx:6514
decltype(auto) constexpr size(T &&obj)
ADL-aware version of std::size.
Definition: StdUtils.h:92
float OverlapFraction(const TCSlice &slc, const Trajectory &tj1, const Trajectory &tj2)
Definition: Utils.cxx:721
float HitsRMSTime(const TCSlice &slc, const std::vector< unsigned int > &hitsInMultiplet, HitStatus_t hitRequest)
Definition: Utils.cxx:4245
Access the description of detector geometry.
std::vector< int > TjUIDs
Definition: DataStructs.h:277
double y
T abs(T value)
std::vector< unsigned int > PutHitsInVector(const TCSlice &slc, PFPStruct const &pfp, HitStatus_t hitRequest)
Definition: Utils.cxx:2739
const std::vector< std::string > StrategyBitNames
float HitSep2(const TCSlice &slc, unsigned int iht, unsigned int jht)
Definition: Utils.cxx:2546
const HLTPathStatus pass
int close(int)
Closes the file descriptor fd.
int NeutrinoPrimaryTjID(const TCSlice &slc, const Trajectory &tj)
Definition: Utils.cxx:452
float HitsRMSTick(const TCSlice &slc, const std::vector< unsigned int > &hitsInMultiplet, HitStatus_t hitRequest)
Definition: Utils.cxx:4254
struct of temporary 3D vertices
Definition: DataStructs.h:102
unsigned int Nplanes(unsigned int tpc=0, unsigned int cstat=0) const
Returns the total number of wire planes in the specified TPC.
void PrintAllTraj(detinfo::DetectorPropertiesData const &detProp, std::string someText, TCSlice &slc, unsigned short itj, unsigned short ipt, bool prtVtx)
Definition: Utils.cxx:5960
short nPtsAve
dump trajectory points
Definition: DataStructs.h:598
int PrimaryID(const TCSlice &slc, const Trajectory &tj)
Definition: Utils.cxx:475
geo::Length_t DetHalfHeight(geo::TPCID const &tpcid) const
Returns the half height of the active volume of the specified TPC.
bool dbgStp
debug stepping using debug.Cryostat, debug.TPC, etc
Definition: DataStructs.h:583
std::array< float, 2 > Point2_t
Definition: DataStructs.h:41
std::vector< float > maxPos1
Definition: DataStructs.h:565
float unitsPerTick
scale factor from Tick to WSE equivalent units
Definition: DataStructs.h:563
IDparameter< geo::WireID > WireID
Member type of validated geo::WireID parameter.
std::string PrintEndFlag(const Trajectory &tj, unsigned short end)
Definition: Utils.cxx:6493
bool TrajIsClean(TCSlice &slc, Trajectory &tj, bool prt)
Definition: Utils.cxx:3450
bool BraggSplit(TCSlice &slc, unsigned short itj)
Definition: Utils.cxx:1452
void DefineHitPos(TCSlice &slc, TrajPoint &tp)
Definition: StepUtils.cxx:1676
unsigned short NearbyCleanPt(const TCSlice &slc, const Trajectory &tj, unsigned short end)
Definition: Utils.cxx:2964
geo::TPCID TPCID
Definition: DataStructs.h:111
void swap(Handle< T > &a, Handle< T > &b)
TP is near a hit in the srcHit collection but no allHit hit exists (DUNE disambiguation error) ...
Definition: DataStructs.h:517
CTP_t CTP
Cryostat, TPC, Plane code.
Definition: DataStructs.h:190
void DumpTj()
Definition: Utils.cxx:5408
int UID
unique global ID
Definition: DataStructs.h:114
bool MergeShowerTjsAndStore(TCSlice &slc, unsigned short istj, unsigned short jstj, bool prt)
Definition: TCShower.cxx:3031
bool dbg2V
debug 2D vertex finding
Definition: DataStructs.h:585
bool TrajHitsOK(TCSlice &slc, const unsigned int iht, const unsigned int jht)
Definition: Utils.cxx:1929
std::vector< float > aveHitRMS
average RMS of an isolated hit
Definition: DataStructs.h:630
std::vector< TrajPoint > Pts
Trajectory points.
Definition: DataStructs.h:189
float ElectronLikelihood(const TCSlice &slc, const Trajectory &tj)
Definition: Utils.cxx:3224
float TwoTPAngle(const TrajPoint &tp1, const TrajPoint &tp2)
Definition: Utils.cxx:2729
std::vector< std::vector< bool > > goodWire
Definition: DataStructs.h:622
float ChgFracNearPos(const TCSlice &slc, const Point2_t &pos, const std::vector< int > &tjIDs)
Definition: Utils.cxx:3244
void MakeHaloTj(TCSlice &slc, Trajectory &muTj, bool prt)
Definition: Utils.cxx:55
void FitTraj(TCSlice &slc, Trajectory &tj, unsigned short originPt, unsigned short npts, short fitDir, TrajPoint &tpFit)
Definition: Utils.cxx:830
TrajPoint MakeBareTP(detinfo::DetectorPropertiesData const &detProp, const TCSlice &slc, const Point3_t &pos, const Vector3_t &dir, CTP_t inCTP)
Definition: Utils.cxx:4054
std::vector< VtxStore > vtxs
2D vertices
Definition: DataStructs.h:668
std::array< unsigned short, 2 > EndPt
First and last point in the trajectory that has charge.
Definition: DataStructs.h:201
unsigned short PDGCode
shower-like or track-like {default is track-like}
Definition: DataStructs.h:206
double ConvertXToTicks(double X, int p, int t, int c) const
bool PointInsideEnvelope(const Point2_t &Point, const std::vector< Point2_t > &Envelope)
Definition: Utils.cxx:3331
void ChkEndKink(TCSlice &slc, Trajectory &tj, bool prt)
Definition: Utils.cxx:1715
std::vector< float > match3DCuts
3D matching cuts
Definition: DataStructs.h:552
std::vector< SectionFit > SectionFits
Definition: DataStructs.h:279
void TagJunkTj(TCSlice &slc, Trajectory &tj, bool prt)
Definition: Utils.cxx:2790
geo::Length_t DetLength(geo::TPCID const &tpcid) const
Returns the length of the active volume of the specified TPC.
virtual bool IsGood(raw::ChannelID_t channel) const
Returns whether the specified channel is physical and good.
void MakeTrajectoryObsolete(TCSlice &slc, unsigned int itj)
Definition: Utils.cxx:2192
short StartEnd
The starting end (-1 = don&#39;t know)
Definition: DataStructs.h:209
float PointTrajDOCA2(const TCSlice &slc, float wire, float time, TrajPoint const &tp)
Definition: Utils.cxx:2598
unsigned short GetPFPIndex(const TCSlice &slc, int tjID)
Definition: Utils.cxx:1058
Definition: SNSlice.h:7
std::string PrintHit(const TCHit &tch)
Definition: Utils.cxx:6524
Point2_t Pos
Definition: DataStructs.h:71
bool Fit2D(short mode, Point2_t inPt, float &inPtErr, Vector2_t &outVec, Vector2_t &outVecErr, float &chiDOF)
Definition: Utils.cxx:5137
string tmp
Definition: languages.py:63
bool StartTraj(TCSlice &slc, Trajectory &tj, float fromWire, float fromTick, float toWire, float toTick, CTP_t &tCTP, unsigned short pass)
Definition: Utils.cxx:5042
void TrajPointTrajDOCA(const TCSlice &slc, TrajPoint const &tp, Trajectory const &tj, unsigned short &closePt, float &minSep)
Definition: Utils.cxx:2443
const geo::GeometryCore * geom
Definition: DataStructs.h:568
Class providing information about the quality of channels.
bool SplitTraj(TCSlice &slc, unsigned short itj, unsigned short pos, unsigned short ivx, bool prt)
Definition: Utils.cxx:2327
double DriftVelocity(double efield=0., double temperature=0.) const
cm/us
int UID
a unique ID for all slices
Definition: DataStructs.h:204
The data type to uniquely identify a TPC.
Definition: geo_types.h:386
PlaneID_t Plane
Index of the plane within its TPC.
Definition: geo_types.h:493
float MaxChargeAsymmetry(TCSlice &slc, std::vector< int > &tjIDs)
Definition: Utils.cxx:382
unsigned short NearestPtWithChg(const TCSlice &slc, const Trajectory &tj, unsigned short thePt)
Definition: Utils.cxx:3530
View_t View(geo::PlaneID const &pid) const
Returns the view (wire orientation) on the channels of specified TPC plane.
std::array< double, 2 > Vector2_t
Definition: DataStructs.h:42
Definition of data types for geometry description.
void ReverseTraj(TCSlice &slc, Trajectory &tj)
Definition: Utils.cxx:3302
unsigned short NumHitsInTP(const TrajPoint &tp, HitStatus_t hitRequest)
Definition: Utils.cxx:4336
void TrimEndPts(std::string fcnLabel, TCSlice &slc, Trajectory &tj, const std::vector< float > &fQualityCuts, bool prt)
Definition: Utils.cxx:1608
void DefineTjParents(TCSlice &slc, bool prt)
Definition: Utils.cxx:176
float HitsPosTick(const TCSlice &slc, const std::vector< unsigned int > &hitsInMultiplet, float &sum, HitStatus_t hitRequest)
Definition: Utils.cxx:4297
std::vector< unsigned int > firstWire
the first wire with a hit
Definition: DataStructs.h:646
bool CompatibleMerge(const TCSlice &slc, const Trajectory &tj1, const Trajectory &tj2, bool prt)
Definition: Utils.cxx:660
float ChiDOF
Definition: DataStructs.h:183
double MCSThetaRMS(const TCSlice &slc, const Trajectory &tj, unsigned short firstPt, unsigned short lastPt)
Definition: Utils.cxx:3564
auto norm(Vector const &v)
Return norm of the specified vector.
int ID
ID that is local to one slice.
Definition: DataStructs.h:203
std::vector< TCSlice > slices
Definition: DataStructs.cxx:12
std::array< unsigned short, 2 > VtxID
ID of 2D vertex.
Definition: DataStructs.h:200
Detector simulation of raw signals on wires.
Q_EXPORT QTSManip setw(int w)
Definition: qtextstream.h:331
void UpdateTjChgProperties(std::string inFcnLabel, TCSlice &slc, Trajectory &tj, bool prt)
Definition: Utils.cxx:3681
float PosSep2(const Point2_t &pos1, const Point2_t &pos2)
Definition: Utils.cxx:2678
bool FindCloseHits(TCSlice &slc, TrajPoint &tp, float const &maxDelta, HitStatus_t hitRequest)
Definition: Utils.cxx:2915
std::bitset< 16 > modes
number of points to find AveChg
Definition: DataStructs.h:599
std::vector< TCHit > slHits
Definition: DataStructs.h:661
int twopi
Definition: units.py:12
bool MergeAndStore(TCSlice &slc, unsigned int itj1, unsigned int itj2, bool doPrt)
Definition: Utils.cxx:4672
void RestoreObsoleteTrajectory(TCSlice &slc, unsigned int itj)
Definition: Utils.cxx:2205
bool aveHitRMSValid
set true when the average hit RMS is well-known
Definition: DataStructs.h:639
std::bitset< 16 > Stat
Vertex status bits using kVtxBit_t.
Definition: DataStructs.h:86
int ID
set to 0 if killed
Definition: DataStructs.h:81
float ParSlp
Definition: DataStructs.h:181
unsigned short FarEnd(TCSlice &slc, const Trajectory &tj, const Point2_t &pos)
Definition: Utils.cxx:4185
unsigned short AngleCode
Definition: DataStructs.h:169
float val
Definition: StepUtils.cxx:27
raw::ChannelID_t PlaneWireToChannel(WireID const &wireid) const
Returns the ID of the TPC channel connected to the specified wire.
const std::vector< std::string > VtxBitNames
Definition: DataStructs.cxx:97
double DotProd(const Vector3_t &v1, const Vector3_t &v2)
Definition: PFPUtils.h:148
unsigned int CTP_t
Definition: DataStructs.h:45
std::vector< Vtx3Store > vtx3s
3D vertices
Definition: DataStructs.h:669
geo::TPCID TPCID
Definition: DataStructs.h:654
bool DecodeDebugString(std::string strng)
Definition: Utils.cxx:5224
std::vector< recob::Hit > const * srcHits
Definition: DataStructs.h:615
std::bitset< 128 > useAlg
Max hit separation for making junk trajectories. < 0 to turn off.
Definition: DataStructs.h:578
bool AnalyzeHits()
Definition: Utils.cxx:4402
Contains all timing reference information for the detector.
float ParErr
Definition: DataStructs.h:180
short StepDir
-1 = going US (-> small wire#), 1 = going DS (-> large wire#)
Definition: DataStructs.h:208
for(std::string line;std::getline(inFile, line);)
Definition: regex_t.cc:35
float MaxHitDelta(TCSlice &slc, Trajectory &tj)
Definition: Utils.cxx:3284
float TrajLength(const Trajectory &tj)
Definition: Utils.cxx:2654
std::bitset< 128 > AlgMod
Bit set if algorithm AlgBit_t modifed the trajectory.
Definition: DataStructs.h:191
static unsigned int reverse(QString &chars, unsigned char *level, unsigned int a, unsigned int b)
Definition: qstring.cpp:11649
std::vector< short > muonTag
Definition: DataStructs.h:547
bool TrajTrajDOCA(const TCSlice &slc, const Trajectory &tj1, const Trajectory &tj2, unsigned short &ipt1, unsigned short &ipt2, float &minSep, bool considerDeadWires)
Definition: Utils.cxx:2480
unsigned short NTraj
Definition: DataStructs.h:73
geo::PlaneID DecodeCTP(CTP_t CTP)
unsigned short AngleRange(float angle)
Definition: Utils.cxx:801
Vector2_t PointDirection(const Point2_t p1, const Point2_t p2)
Definition: Utils.cxx:4195
std::vector< int > GetVtxTjIDs(const TCSlice &slc, const VtxStore &vx2)
Definition: TCVertex.cxx:2851
std::vector< int > TjIDs
Definition: DataStructs.h:276
MaybeLogger_< ELseverityLevel::ELsev_warning, false > LogWarning
void TrimHiChgEndPts(TCSlice &slc, Trajectory &tj, bool prt)
Definition: Utils.cxx:1565
std::bitset< 128 > dbgAlg
Allow user to turn on debug printing in algorithms (that print...)
Definition: DataStructs.h:579
unsigned short PDGCodeIndex(int PDGCode)
Definition: Utils.cxx:2179
std::bitset< 8 > Environment
Definition: DataStructs.h:172
std::vector< recob::Hit > const * allHits
Definition: DataStructs.h:614
CTP_t EncodeCTP(unsigned int cryo, unsigned int tpc, unsigned int plane)
Definition: DataStructs.h:50
Interface for experiment-specific channel quality info provider.
std::pair< unsigned short, unsigned short > GetSliceIndex(std::string typeName, int uID)
Definition: Utils.cxx:5096
std::vector< unsigned int > nWires
Definition: DataStructs.h:645
use the stiff electron strategy
Definition: DataStructs.h:493
std::array< std::bitset< 8 >, 2 > EndFlag
Definition: DataStructs.h:210
float PointTrajSep2(float wire, float time, TrajPoint const &tp)
Definition: Utils.cxx:2571
std::array< double, 3 > Vector3_t
Definition: DataStructs.h:40
std::vector< float > chkStopCuts
Bragg peak finder configuration.
Definition: DataStructs.h:549
#define A
Definition: memgrp.cpp:38
bool SignalAtTpInSlc(const TCSlice &slc, const TrajPoint &tp)
Definition: Utils.cxx:1970
std::vector< TP3D > TP3Ds
Definition: DataStructs.h:278
void PrintDebugMode()
Definition: Utils.cxx:5458
int ID
ID of the recob::Slice (not the sub-slice)
Definition: DataStructs.h:656
std::array< std::vector< float >, 2 > dEdx
Definition: DataStructs.h:281
int UID
unique global ID
Definition: DataStructs.h:82
void SetVx2Score(TCSlice &slc)
Definition: TCVertex.cxx:2278
std::vector< float > vtx2DCuts
Max position pull, max Position error rms.
Definition: DataStructs.h:542
list x
Definition: train.py:276
std::bitset< 8 > Strategy
Definition: DataStructs.h:211
unsigned short nPlanes
Definition: DataStructs.h:655
bool NearbySrcHit(geo::PlaneID plnID, unsigned int wire, float loTick, float hiTick)
Definition: Utils.cxx:2079
TPCGeo const & TPC(unsigned int const tpc=0, unsigned int const cstat=0) const
Returns the specified TPC.
void TjDeltaRMS(const TCSlice &slc, const Trajectory &tj, unsigned short firstPt, unsigned short lastPt, double &rms, unsigned short &cnt)
Definition: Utils.cxx:3592
void split(std::string const &s, char c, OutIter dest)
Definition: split.h:35
std::vector< PFPStruct > pfps
Definition: DataStructs.h:670
std::vector< int > FindCloseTjs(const TCSlice &slc, const TrajPoint &fromTp, const TrajPoint &toTp, const float &maxDelta)
Definition: Utils.cxx:2985
2D representation of charge deposited in the TDC/wire plane
Definition: Hit.h:48
void MoveTPToWire(TrajPoint &tp, float wire)
Definition: Utils.cxx:2839
void MergeGhostTjs(TCSlice &slc, CTP_t inCTP)
Definition: Utils.cxx:2228
TCEvent evt
Definition: DataStructs.cxx:7
unsigned int ChannelID_t
Type representing the ID of a readout channel.
Definition: RawTypes.h:28
unsigned int allHitsIndex
Definition: DataStructs.h:605
const char * cs
unsigned short MVI
Definition: DataStructs.h:292
short MCSMom(const TCSlice &slc, const Trajectory &tj, unsigned short firstPt, unsigned short lastPt)
Definition: Utils.cxx:3500
TPCID_t TPC
Index of the TPC within its cryostat.
Definition: geo_types.h:406
Interface for experiment-specific service for channel quality info.
std::string PrintPos(const TCSlice &slc, const Point2_t &pos)
Definition: Utils.cxx:6541
void TrajIntersection(TrajPoint const &tp1, TrajPoint const &tp2, float &x, float &y)
Definition: Utils.cxx:2618
bool StorePFP(TCSlice &slc, PFPStruct &pfp)
Definition: PFPUtils.cxx:2879
size_t ParentUID
Definition: DataStructs.h:287
double sampling_rate(DetectorClocksData const &data)
Returns the period of the TPC readout electronics clock.
int PrimaryUID(const TCSlice &slc, const PFPStruct &pfp)
Definition: Utils.cxx:495
unsigned short nPtsFit
Definition: DataStructs.h:184
std::vector< std::vector< std::pair< unsigned int, unsigned int > > > wireHitRange
Definition: DataStructs.h:618
Vector3_t DirAtEnd(const PFPStruct &pfp, unsigned short end)
Definition: PFPUtils.cxx:3151
if(!yymsg) yymsg
short recoSlice
only reconstruct the slice with ID (0 = all)
Definition: DataStructs.h:580
double DeltaAngle(double Ang1, double Ang2)
Definition: Utils.cxx:3414
bool useChannelStatus
Definition: DataStructs.h:601
float TjChgFrac
Fraction of charge near the vertex that is from hits on the vertex Tjs.
Definition: DataStructs.h:85
bool dbgSummary
print a summary report
Definition: DataStructs.h:596
bool NeedsUpdate
Set true when the Tj needs to be updated.
Definition: DataStructs.h:212
master switch for turning on debug mode
Definition: DataStructs.h:525
Point2_t HitPos
Definition: DataStructs.h:152
std::string to_string(ModuleType const mt)
Definition: ModuleType.h:34
void LocalToWorld(const double *tpc, double *world) const
Transform point from local TPC frame to world frame.
Definition: TPCGeo.h:563
float TPHitsRMSTick(const TCSlice &slc, const TrajPoint &tp, HitStatus_t hitRequest)
Definition: Utils.cxx:4217
static QCString * s
Definition: config.cpp:1042
def parent(G, child, parent_type)
Definition: graph.py:67
static QCString str
void PrintTPHeader(std::string someText)
Definition: Utils.cxx:6295
std::vector< int > DtrUIDs
Definition: DataStructs.h:286
constexpr Point origin()
Returns a origin position with a point of the specified type.
Definition: geo_vectors.h:227
use the stiff muon strategy
Definition: DataStructs.h:494
void UpdateVxEnvironment(TCSlice &slc, VtxStore &vx2, bool prt)
Definition: Utils.cxx:3893
Encapsulate the construction of a single detector plane.
std::array< std::bitset< 8 >, 2 > EndFlag
Definition: DataStructs.h:294
void SetTPEnvironment(TCSlice &slc, CTP_t inCTP)
Definition: Utils.cxx:3634
float DeadWireCount(const TCSlice &slc, const float &inWirePos1, const float &inWirePos2, CTP_t tCTP)
Definition: Utils.cxx:2156
bool HasDuplicateHits(const TCSlice &slc, Trajectory const &tj, bool prt)
Definition: Utils.cxx:2820
TFile * ef
Definition: doAna.cpp:25