LArSoft  v06_85_00
Liquid Argon Software toolkit - http://larsoft.org/
Utils.cxx
Go to the documentation of this file.
2 
3 #include "larcorealg/CoreUtils/NumericUtils.h" // util::absDiff()
4 
5 
6 struct SortEntry{
7  unsigned int index;
8  float val;
9 };
10 
11 bool valDecreasing (SortEntry c1, SortEntry c2) { return (c1.val > c2.val);}
12 bool valIncreasing (SortEntry c1, SortEntry c2) { return (c1.val < c2.val);}
13 
14 namespace tca {
15 
17  void DefineTjParents(TjStuff& tjs, const geo::TPCID& tpcid, bool prt)
18  {
19 /*
20  This function sets the ParentID of Tjs in this tpcid to create a hierarchy. The highest Score
21  3D vertex in a chain of Tjs and vertices is declared the primary vertex; vx3.Primary = true. Tjs directly attached
22  to that vertex are declared Primary trajectories with ParentID = 0. All other Tjs in the chain have ParentID
23  set to the next upstream Tj to which it is attached by a vertex. In the graphical description below, V1 and V4 are
24  2D vertices that are matched to a high-score 3D vertex. The V1 Score is greater than the V2 Score and V3 Score.
25  V1 and V4 are declared to be primary vertices. T1, T2, T6 and T7 are declared to be primary Tjs.
26 
27  V1 - T1 - V2 - T3 V4 - T6 / T8
28  \ \ /
29  T2 - V3 - T4 T7
30  \
31  T5
32 
33  This is represented as follows. The NeutrinoPrimaryTjID is defined by a function.
34  Tj ParentID NeutrinoPrimaryTjID
35  -----------------------------------
36  T1 0 T1
37  T2 0 T2
38  T3 T1 T2
39  T4 T2 T2
40  T5 T2 T2
41  T6 0 -1
42  T7 0 -1
43  T8 -1 -1
44 */
45 
46  // don't do anything if this is test beam data
47  if(tjs.TestBeam) return;
48 
49  // clear old information
50  for(auto& tj : tjs.allTraj) {
51  if(tj.AlgMod[kKilled]) continue;
52  if(DecodeCTP(tj.CTP).Cryostat != tpcid.Cryostat) continue;
53  if(DecodeCTP(tj.CTP).TPC != tpcid.TPC) continue;
54  // ignore delta rays
55  if(tj.AlgMod[kDeltaRay]) continue;
56  // ignore pion-like
57 // if(tj.PDGCode == 211) continue;
58  tj.ParentID = 0;
59  } // tj
60 
61  // sort vertice by decreasing score
62  std::vector<int> temp;
63  for(auto& vx3 : tjs.vtx3) {
64  if(vx3.ID == 0) continue;
65  if(vx3.TPCID != tpcid) continue;
66  // clear the Primary flag while we are here
67  vx3.Primary = false;
68  temp.push_back(vx3.ID);
69  } // vx3
70  if(temp.empty()) return;
71 
72  // Make a master list of all Tjs that are attached to these vertices
73  std::vector<int> masterlist;
74  for(auto vx3id : temp) {
75  auto& vx3 = tjs.vtx3[vx3id - 1];
76  float score;
77  auto tjlist = GetVtxTjIDs(tjs, vx3, score);
78  for(auto tjid : tjlist) {
79  // Temp? Check for an existing parentID
80  auto& tj = tjs.allTraj[tjid - 1];
81  if(tj.ParentID != 0) {
82 // std::cout<<"**** Tj "<<tj.ID<<" Existing parent "<<tj.ParentID<<" PDGCode "<<tj.PDGCode<<". with a vertex... \n";
83  tj.ParentID = 0;
84  }
85  if(std::find(masterlist.begin(), masterlist.end(), tjid) == masterlist.end()) masterlist.push_back(tjid);
86  } // tjid
87  } // vxid
88  if(prt) {
89  mf::LogVerbatim myprt("TC");
90  myprt<<"DTP: masterlist Tjs";
91  for(auto tjid : masterlist) myprt<<" "<<tjid;
92  }
93 
94  // Do the sort
95  std::vector<SortEntry> sortVec(temp.size());
96  for(unsigned short indx = 0; indx < temp.size(); ++indx) {
97  auto& vx3 = tjs.vtx3[temp[indx] - 1];
98  sortVec[indx].index = indx;
99  sortVec[indx].val = vx3.Score;
100  } // indx
101  if(sortVec.size() > 1) std::sort(sortVec.begin(), sortVec.end(), valDecreasing);
102  // put them into order
103  auto vlist = temp;
104  for(unsigned short indx = 0; indx < temp.size(); ++indx) vlist[indx] = temp[sortVec[indx].index];
105 
106  // make a neutrino PFParticle to associate with the highest score vertex if it is high enough
107  if(tjs.Match3DCuts[0] > 0) {
108  auto& vx3 = tjs.vtx3[vlist[0] - 1];
109  if(vx3.Score > tjs.Vertex2DCuts[7]) {
110  auto neutrinoPFP = CreatePFP(tjs, tpcid);
111  // call it the neutrino vertex
112  vx3.Neutrino = true;
113  // put the vertex at the end of the neutrino
114  neutrinoPFP.XYZ[1][0] = vx3.X;
115  neutrinoPFP.XYZ[1][1] = vx3.Y;
116  neutrinoPFP.XYZ[1][2] = vx3.Z;
117  neutrinoPFP.XYZ[0] = neutrinoPFP.XYZ[1];
118  neutrinoPFP.Dir[1][2] = 1;
119  neutrinoPFP.Dir[0][2] = 1;
120  // This may be set to 12 later on if a primary shower is reconstructed
121  neutrinoPFP.PDGCode = 14;
122  neutrinoPFP.Vx3ID[1] = vx3.ID;
123  neutrinoPFP.Vx3ID[0] = vx3.ID;
124  neutrinoPFP.NeedsUpdate = false;
125  // the rest of this will be defined later
126  if(!StorePFP(tjs, neutrinoPFP)) return;
127  }
128  } // User wants to make PFParticles
129  // a temp vector to ensure that we only consider a vertex once
130  std::vector<bool> lookedAt3(tjs.vtx3.size() + 1, false);
131  std::vector<bool> lookedAt2(tjs.vtx.size() + 1, false);
132  // vector of parent-daughter pairs
133  std::vector<std::pair<int, int>> pardtr;
134  // Start with the highest score vertex
135  for(unsigned short indx = 0; indx < vlist.size(); ++indx) {
136  auto& vx3 = tjs.vtx3[vlist[indx] - 1];
137  if(lookedAt3[vx3.ID]) continue;
138  vx3.Primary = true;
139  lookedAt3[vx3.ID] = true;
140  // make a list of Tjs attached to this vertex
141  float score;
142  auto primTjList = GetVtxTjIDs(tjs, vx3, score);
143  if(primTjList.empty()) continue;
144  pardtr.clear();
145  for(auto primTjID : primTjList) {
146  auto& primTj = tjs.allTraj[primTjID - 1];
147  // This isn't a primary tj if the parent ID isn't -1
148  if(primTj.ParentID != -1) continue;
149  if(prt) mf::LogVerbatim("TC")<<"Vx3 "<<vx3.ID<<" Primary tj "<<primTj.ID;
150  // declare this a primary tj
151  primTj.ParentID = 0;
152  // look for daughter tjs = those that are attached to a 2D vertex
153  // at the other end
154  for(unsigned short end = 0; end < 2; ++end) {
155  if(primTj.VtxID[end] == 0) continue;
156  auto& vx2 = tjs.vtx[primTj.VtxID[end] - 1];
157  if(vx2.Vx3ID == vx3.ID) continue;
158  // found a 2D vertex. Check for daughters
159  auto dtrList = GetVtxTjIDs(tjs, vx2);
160  for(auto dtrID : dtrList) {
161  // ignore the primary tj
162  if(dtrID == primTjID) continue;
163  auto& dtj = tjs.allTraj[dtrID - 1];
164  if(dtj.ParentID != -1) {
165 // std::cout<<"DTP Error: dtr "<<dtrID<<" already has a parent "<<dtj.ParentID<<". Can't make it daughter of "<<primTjID<<"\n";
166  continue;
167  }
168  pardtr.push_back(std::make_pair(primTjID, dtrID));
169  if(prt) mf::LogVerbatim("TC")<<" primTj "<<primTjID<<" dtrID "<<dtrID;
170  } // tjid
171  } // end
172  // Ensure that end 0 of the trajectory is attached to the primary vertex
173  for(unsigned short end = 0; end < 2; ++end) {
174  if(primTj.VtxID[end] == 0) continue;
175  auto& vx2 = tjs.vtx[primTj.VtxID[end] - 1];
176  if(vx2.Vx3ID == vx3.ID && end != 0) ReverseTraj(tjs, primTj);
177  } // end
178  } // tjid
179  if(pardtr.empty()) continue;
180  if(prt) {
181  mf::LogVerbatim myprt("TC");
182  myprt<<" par_dtr";
183  for(auto pdtr : pardtr) myprt<<" "<<pdtr.first<<"_"<<pdtr.second;
184  }
185  // iterate through the parent - daughter stack, removing the last pair when a
186  // ParentID is updated and adding pairs for new daughters
187  for(unsigned short nit = 0; nit < 100; ++nit) {
188  auto lastPair = pardtr[pardtr.size() - 1];
189  auto& dtj = tjs.allTraj[lastPair.second - 1];
190  dtj.ParentID = lastPair.first;
191  // reverse the daughter trajectory if necessary so that end 0 is closest to the parent
192  float doca = 100;
193  unsigned short dpt = 0, ppt = 0;
194  auto& ptj = tjs.allTraj[lastPair.first - 1];
195  // find the point on the daughter tj that is closest to the parent
196  TrajTrajDOCA(tjs, dtj, ptj, dpt, ppt, doca);
197  // reverse the daughter if the closest point is near end 1 of the daughter
198  unsigned short midPt = (dtj.EndPt[0] + dtj.EndPt[1]) / 2;
199  if(dpt > midPt && !dtj.AlgMod[kSetDir]) ReverseTraj(tjs, dtj);
200  if(prt) mf::LogVerbatim("TC")<<"Set parent "<<ptj.ID<<" dtr "<<dtj.ID;
201  // remove that entry
202  pardtr.pop_back();
203  // Add entries for new daughters
204  for(unsigned short end = 0; end < 2; ++end) {
205  if(dtj.VtxID[end] == 0) continue;
206  auto& vx2 = tjs.vtx[dtj.VtxID[end] - 1];
207  if(lookedAt2[vx2.ID]) continue;
208  lookedAt2[vx2.ID] = true;
209  auto tjlist = GetVtxTjIDs(tjs, vx2);
210  for(auto tjid : tjlist) {
211  if(tjid == dtj.ID || tjid == ptj.ID) continue;
212  pardtr.push_back(std::make_pair(dtj.ID, tjid));
213  if(prt) {
214  mf::LogVerbatim myprt("TC");
215  myprt<<" add par_dtr";
216  for(auto pdtr : pardtr) myprt<<" "<<pdtr.first<<"_"<<pdtr.second;
217  }
218  }
219  } // end
220  if(pardtr.empty()) break;
221  } // nit
222  } // indx
223  // check the master list
224  for(auto tjid : masterlist) {
225  auto& tj = tjs.allTraj[tjid - 1];
226  if(tj.ParentID < 0) {
227 // std::cout<<"Tj "<<tj.ID<<" is in the master list but doesn't have a Parent\n";
228  tj.ParentID = tj.ID;
229  }
230  } // tjid
231 
232  } // DefineTjParents
233 
235  float MaxChargeAsymmetry(TjStuff& tjs, std::vector<int>& tjIDs)
236  {
237  // calculates the maximum charge asymmetry in all planes using the supplied list of Tjs
238  if(tjIDs.size() < 2) return 1;
239  std::vector<float> plnchg(tjs.NumPlanes);
240  for(auto tjid : tjIDs) {
241  if(tjid <= 0 || tjid > (int)tjs.allTraj.size()) return 1;
242  auto& tj = tjs.allTraj[tjid - 1];
243  if(tj.TotChg == 0) UpdateTjChgProperties("MCA", tjs, tj, false);
244  unsigned short plane = DecodeCTP(tj.CTP).Plane;
245  plnchg[plane] += tj.TotChg;
246  } // tjid
247  float aveChg = 0;
248  float cnt = 0;
249  for(unsigned short plane = 0; plane < tjs.NumPlanes; ++plane) {
250  if(plnchg[plane] == 0) continue;
251  aveChg += plnchg[plane];
252  ++cnt;
253  } // plane
254  if(cnt < 2) return 1;
255  aveChg /= cnt;
256  float maxAsym = 0;
257  for(unsigned short plane = 0; plane < tjs.NumPlanes; ++plane) {
258  // ignore zeros
259  if(plnchg[plane] == 0) continue;
260  float asym = std::abs(plnchg[plane] - aveChg) / (plnchg[plane] + aveChg);
261  if(asym > maxAsym) maxAsym = asym;
262  } // plane
263  return maxAsym;
264  } // MaxChargeAsymmetry
265 
267  int PDGCodeVote(TjStuff& tjs, std::vector<int>& tjIDs, bool prt)
268  {
269  // Returns the most likely PDGCode for the set of Tjs provided
270  // The PDG codes are:
271  // 0 = your basic track-like trajectory
272  // 11 = Tagged delta-ray
273  // 13 = Tagged muon
274  // 211 = pion-like. There exists a Bragg peak at an end with a vertex
275  // 2212 = proton-like. There exists a Bragg peak at an end without a vertex
276  // BUG the double brace syntax is required to work around clang bug 21629
277  // (https://bugs.llvm.org/show_bug.cgi?id=21629)
278  std::array<int, 5> codeList = {{0, 11, 13, 211, 2212}};
279  unsigned short codeIndex = 0;
280  if(tjIDs.empty()) return codeList[codeIndex];
281 
282  std::array<unsigned short, 5> cnts;
283  cnts.fill(0);
284  // Count Bragg peaks. This assumes that the Tjs are in order...
285  std::array<unsigned short, 2> stopCnt {{0, 0}};
286  float maxLen = 0;
287  for(auto tjid : tjIDs) {
288  if(tjid <= 0 || tjid > (int)tjs.allTraj.size()) continue;
289  auto& tj = tjs.allTraj[tjid - 1];
290  for(unsigned short ii = 0; ii < 5; ++ii) if(tj.PDGCode == codeList[ii]) ++cnts[ii];
291  // count InShower Tjs with PDGCode not set (yet)
292  if(tj.PDGCode != 11 && tj.AlgMod[kShowerLike]) ++cnts[1];
293  for(unsigned short end = 0; end < 2; ++end) if(tj.StopFlag[end][kBragg]) ++stopCnt[end];
294  float len = TrajLength(tj);
295  if(len > maxLen) maxLen = len;
296  } // tjid
297  unsigned maxCnt = 0;
298  // ignore the first PDG code in the list (the default)
299  for(unsigned short ii = 1; ii < 5; ++ii) {
300  if(cnts[ii] > maxCnt) {
301  maxCnt = cnts[ii];
302  codeIndex = ii;
303  }
304  } // ii
305  // check for an inconsistent code
306  bool confused = false;
307  for(unsigned short ii = 1; ii < 5; ++ii) {
308  if(ii == codeIndex) continue;
309  if(cnts[ii] == 0) continue;
310  confused = true;
311  } // ii
312  if(confused) {
313  // Check for a muon called it a proton
314  if(cnts[4] > 0 && stopCnt[2] > 0 && NumDeltaRays(tjs, tjIDs) == 0) {
315  codeIndex = 4;
316  confused = false;
317  }
318  } // confused
319  if(confused) {
320  codeIndex = 0;
321  if(prt) {
322  mf::LogVerbatim myprt("TC");
323  myprt<<"PDGCodeVote: mixed vote on the PDGCode: Tj_PDGCode";
324  for(auto tjid : tjIDs) {
325  if(tjid <= 0 || tjid > (int)tjs.allTraj.size()) continue;
326  auto& tj = tjs.allTraj[tjid - 1];
327  myprt<<" "<<tj.ID<<"_"<<tj.PDGCode<<"_"<<tj.StopFlag[1][kBragg];
328  } // tjid
329  }
330  } // confused
331  return codeList[codeIndex];
332  } // PDGCodeVote
333 
335  unsigned short NumDeltaRays(const TjStuff& tjs, const Trajectory& tj)
336  {
337  // returns the number of delta rays that have this tj as a parent
338  unsigned short cnt = 0;
339  for(auto& dtj : tjs.allTraj) {
340  if(dtj.AlgMod[kKilled]) continue;
341  if(!dtj.AlgMod[kDeltaRay]) continue;
342  if(dtj.ParentID == tj.ID) ++cnt;
343  } // tj
344  return cnt;
345  } // NumDeltaRays
346 
348  unsigned short NumDeltaRays(const TjStuff& tjs, std::vector<int>& tjIDs)
349  {
350  // Count the number of delta-rays that have a Tj in the list of TjIDs as a parent.
351  if(tjIDs.empty()) return 0;
352  if(tjIDs[0] <= 0 || tjIDs[0] > (int)tjs.allTraj.size()) return 0;
353  unsigned short cnt = 0;
354  for(auto& tj : tjs.allTraj) {
355  if(tj.AlgMod[kKilled]) continue;
356  if(!tj.AlgMod[kDeltaRay]) continue;
357  if(std::find(tjIDs.begin(), tjIDs.end(), tj.ParentID) != tjIDs.end()) ++cnt;
358  } // tj
359  return cnt;
360  } // NumDeltaRays
361 
363  int NeutrinoPrimaryTjID(const TjStuff& tjs, const Trajectory& tj)
364  {
365  // Returns the ID of the grandparent of this tj that is a primary tj that is attached
366  // to the neutrino vertex. 0 is returned if this condition is not met.
367  if(tj.AlgMod[kKilled]) return -1;
368  if(tj.ParentID <= 0) return -1;
369  int primID = PrimaryID(tjs, tj);
370  if(primID <= 0 || primID > (int)tjs.allTraj.size()) return -1;
371 
372  // We have the ID of the primary tj. Now see if it is attached to the neutrino vertex
373  auto& ptj = tjs.allTraj[primID - 1];
374  for(unsigned short end = 0; end < 2; ++end) {
375  if(ptj.VtxID[end] == 0) continue;
376  auto& vx2 = tjs.vtx[ptj.VtxID[end] - 1];
377  if(vx2.Vx3ID == 0) continue;
378  auto& vx3 = tjs.vtx3[vx2.Vx3ID - 1];
379  if(vx3.Neutrino) return primID;
380  } // end
381  return -1;
382  } // NeutrinoPrimaryTjID
383 
385  int PrimaryID(const TjStuff& tjs, const Trajectory& tj)
386  {
387  // Returns the ID of the grandparent trajectory of this trajectory that is a primary
388  // trajectory (i.e. whose ParentID = 0).
389  if(tj.AlgMod[kKilled]) return -1;
390  if(tj.ParentID < 0 || tj.ParentID > (int)tjs.allTraj.size()) return -1;
391  if(tj.ParentID == 0) return tj.ID;
392  int parid = tj.ParentID;
393  for(unsigned short nit = 0; nit < 10; ++nit) {
394  if(parid < 1 || parid > (int)tjs.allTraj.size()) break;
395  auto& tj = tjs.allTraj[parid - 1];
396  if(tj.ParentID < 0 || tj.ParentID > (int)tjs.allTraj.size()) return -1;
397  if(tj.ParentID == 0) return tj.ID;
398  parid = tj.ParentID;
399  } // nit
400  return -1;
401  } // PrimaryID
402 
404  int PrimaryID(const TjStuff& tjs, const PFPStruct& pfp)
405  {
406  // returns the ID of the most upstream PFParticle (that is not a neutrino)
407 
408  if(int(pfp.ParentID) == pfp.ID || pfp.ParentID <= 0) return pfp.ID;
409  int parid = pfp.ParentID;
410  int dtrid = pfp.ID;
411  unsigned short nit = 0;
412  while(true) {
413  auto& parent = tjs.pfps[parid - 1];
414  // found a neutrino
415  if(parent.PDGCode == 14 || parent.PDGCode == 12) return dtrid;
416  // found a primary PFParticle?
417  if(parent.ParentID == 0) return parent.ID;
418  if(int(parent.ParentID) == parent.ID) return parent.ID;
419  dtrid = parent.ID;
420  parid = parent.ParentID;
421  if(parid < 0) return 0;
422  ++nit;
423  if(nit == 10) return 0;
424  }
425  } // PrimaryID
426 
428  bool MergeTjIntoPFP(TjStuff& tjs, int mtjid, PFPStruct& pfp, bool prt)
429  {
430  // Tries to merge Tj with ID tjid into PFParticle pfp
431  if(mtjid > (int)tjs.allTraj.size()) return false;
432  auto& mtj = tjs.allTraj[mtjid - 1];
433  // find the Tj in pfp.TjIDs which it should be merged with
434  int otjid = 0;
435  for(auto tjid : pfp.TjIDs) {
436  auto& otj = tjs.allTraj[tjid - 1];
437  if(otj.CTP == mtj.CTP) {
438  otjid = tjid;
439  break;
440  }
441  } // tjid
442  if(otjid == 0) return false;
443  if(MergeAndStore(tjs, otjid - 1, mtjid - 1, prt)) {
444  int newtjid = tjs.allTraj.size();
445  if(prt) mf::LogVerbatim("TC")<<"MergeTjIntoPFP: merged T"<<otjid<<" with T"<<mtjid<<" -> T"<<newtjid;
446  std::replace(pfp.TjIDs.begin(), pfp.TjIDs.begin(), otjid, newtjid);
447  return true;
448  } else {
449  if(prt) mf::LogVerbatim("TC")<<"MergeTjIntoPFP: merge T"<<otjid<<" with T"<<mtjid<<" failed ";
450  return false;
451  }
452  } // MergeTjIntoPFP
453 
455  bool CompatibleMerge(TjStuff& tjs, std::vector<int>& tjIDs, bool prt)
456  {
457  // Returns true if the last Tj in tjIDs has a topology consistent with it being
458  // merged with other Tjs in the same plane in the list. This is done by requiring that
459  // the closest TP between the last Tj and any other Tj is EndPt[0] or EndPt[1]. This is
460  // shown graphically here where the numbers represent the ID of a Tj that has a TP on a wire.
461  // Assume that TjIDs = {1, 2, 3, 4, 7} where T1 and T3 are in plane 0, T2 is in plane 1 and
462  // T4 is in plane 2. T7, in plane 0, was added to TjIDs with the intent of merging it with
463  // T1 and T3 into a single trajectory. This is a compatible merge if Tj7 has the following
464  // topology:
465  // 111111 333333 7777777
466  // This is an incompatible topology
467  // 111111 333333
468  // 7777777777
469  if(tjIDs.size() < 2) return false;
470  unsigned short lasttj = tjIDs[tjIDs.size() - 1] - 1;
471  auto& mtj = tjs.allTraj[lasttj];
472  bool mtjIsShort = (mtj.Pts.size() < 5);
473  // minimum separation from each end of mtj
474  std::array<float, 2> minsep2 {{1000, 1000}};
475  // ID of the Tj with the minimum separation
476  std::array<int, 2> minsepTj {{0, 0}};
477  // and the index of the point on that Tj
478  std::array<unsigned short, 2> minsepPt;
479  // determine the end of the closest Tj point. Start by assuming
480  // the closest Tj point is not near an end (end = 0);
481  std::array<unsigned short, 2> minsepEnd;
482  for(auto tjid : tjIDs) {
483  auto& tj = tjs.allTraj[tjid - 1];
484  if(tj.CTP != mtj.CTP) continue;
485  if(tj.ID == mtj.ID) continue;
486  for(unsigned short mend = 0; mend < 2; ++mend) {
487  Point2_t mendPos = mtj.Pts[mtj.EndPt[mend]].Pos;
488  float sep2 = minsep2[mend];
489  unsigned short closePt = 0;
490  if(!TrajClosestApproach(tj, mendPos[0], mendPos[1], closePt, sep2)) continue;
491  minsep2[mend] = sep2;
492  minsepTj[mend] = tjid;
493  minsepPt[mend] = closePt;
494  // set the end to a bogus value (not near an end)
495  minsepEnd[mend] = 2;
496  short dend0 = abs((short)closePt - tj.EndPt[0]);
497  short dend1 = abs((short)closePt - tj.EndPt[1]);
498  if(dend0 < dend1 && dend0 < 3) minsepEnd[mend] = 0;
499  if(dend1 < dend0 && dend1 < 3) minsepEnd[mend] = 1;
500  } // mend
501  } // tjid
502 // bool isCompatible = (minsepEnd[0] != 2 && minsepTj[0] == minsepTj[1] && minsepEnd[0] == minsepEnd[1]);
503  // don't require that the minsepTjs be the same. This would reject this topology
504  // 111111 333333 7777777
505  // if mtj.ID = 3
506  bool isCompatible = (minsepEnd[0] != 2 && minsepEnd[1] != 2);
507  // check for large separation between the closest points for short Tjs
508  if(isCompatible && mtjIsShort) {
509  float minminsep = minsep2[0];
510  if(minsep2[1] < minminsep) minminsep = minsep2[1];
511  // require that the separation be less than sqrt(5)
512  isCompatible = minminsep < 5;
513 // if(minminsep > 2) std::cout<<"CM: mtj T"<<mtj.ID<<" is short and the separation is large "<<minminsep<<". Check for signal between\n ";
514  }
515  if(prt) {
516  mf::LogVerbatim myprt("TC");
517  myprt<<"CompatibleMerge: T"<<mtj.ID<<" end";
518  for(unsigned short end = 0; end < 2; ++end) myprt<<" T"<<minsepTj[end]<<"_I"<<minsepPt[end]<<"_E"<<minsepEnd[end]<<" minsep "<<sqrt(minsep2[end]);
519  myprt<<" Compatible? "<<isCompatible;
520  } // prt
521  return isCompatible;
522 
523  } // CompatibleMerge
524 
526  bool CompatibleMerge(TjStuff& tjs, const Trajectory& tj1, const Trajectory& tj2, bool prt)
527  {
528  // returns true if the two Tjs are compatible with and end0-end1 merge. This function has many aspects of the
529  // compatibility checks done in EndMerge but with looser cuts.
530  if(tj1.AlgMod[kKilled] || tj2.AlgMod[kKilled]) return false;
531  if(tj1.CTP != tj2.CTP) return false;
532  unsigned short end1 = -1, end2 = 0;
533  float minLen = PosSep(tj1.Pts[tj1.EndPt[0]].Pos, tj1.Pts[tj1.EndPt[1]].Pos);
534  float len2 = PosSep(tj2.Pts[tj2.EndPt[0]].Pos, tj2.Pts[tj2.EndPt[1]].Pos);
535  if(len2 < minLen) minLen = len2;
536  minLen *= 1.2;
537  if(minLen > 10) minLen = 10;
538  for(unsigned short e1 = 0; e1 < 2; ++e1) {
539  auto& tp1 = tj1.Pts[tj1.EndPt[e1]];
540  for(unsigned short e2 = 0; e2 < 2; ++e2) {
541  auto& tp2 = tj2.Pts[tj2.EndPt[e2]];
542  float sep = PosSep(tp1.Pos, tp2.Pos);
543  if(sep < minLen) {
544  minLen = sep;
545  end1 = e1; end2 = e2;
546  }
547  } // e2
548  } // e1
549  if(end1 < 0) return false;
550  // require end to end
551  if(end2 != 1 - end1) return false;
552 
553  float overlapFraction = OverlapFraction(tjs, tj1, tj2);
554  if(overlapFraction > 0.25) {
555  if(prt) mf::LogVerbatim("TC")<<"CM: "<<tj1.ID<<" "<<tj2.ID<<" overlapFraction "<<overlapFraction<<" > 0.25 ";
556  return false;
557  }
558 
559  auto& tp1 = tj1.Pts[tj1.EndPt[end1]];
560  auto& tp2 = tj2.Pts[tj2.EndPt[end2]];
561 /* This causes problems with hit collections that have cosmics removed
562  if(!SignalBetween(tjs, tp1, tp2, 0.8, false)) {
563  if(prt) mf::LogVerbatim("TC")<<"CM: "<<tj1.ID<<" "<<tj2.ID<<" no signal between these points "<<PrintPos(tjs, tp1.Pos)<<" "<<PrintPos(tjs, tp2.Pos);
564  return false;
565  }
566 */
567  float doca1 = PointTrajDOCA(tjs, tp1.Pos[0], tp1.Pos[1], tp2);
568  float doca2 = PointTrajDOCA(tjs, tp2.Pos[0], tp2.Pos[1], tp1);
569  if(doca1 > 2 && doca2 > 2) {
570  if(prt) mf::LogVerbatim("TC")<<"CM: "<<tj1.ID<<" "<<tj2.ID<<" Both docas > 2 "<<doca1<<" "<<doca2;
571  return false;
572  }
573 
574  float dang = DeltaAngle(tp1.Ang, tp2.Ang);
575  if(dang > 2 * tjs.KinkCuts[0]) {
576  if(prt) mf::LogVerbatim("TC")<<"CM: "<<tj1.ID<<" "<<tj2.ID<<" dang "<<dang<<" > "<<2 * tjs.KinkCuts[0];
577  return false;
578  }
579 
580  return true;
581  } // CompatibleMerge
582 
584  float OverlapFraction(TjStuff& tjs, const Trajectory& tj1, const Trajectory& tj2)
585  {
586  // returns the fraction of wires spanned by two trajectories
587  float minWire = 1E6;
588  float maxWire = -1E6;
589 
590  float cnt1 = 0;
591  for(auto& tp : tj1.Pts) {
592  if(tp.Chg == 0) continue;
593  if(tp.Pos[0] < 0) continue;
594  if(tp.Pos[0] < minWire) minWire = tp.Pos[0];
595  if(tp.Pos[0] > maxWire) maxWire = tp.Pos[0];
596  ++cnt1;
597  }
598  if(cnt1 == 0) return 0;
599  float cnt2 = 0;
600  for(auto& tp : tj2.Pts) {
601  if(tp.Chg == 0) continue;
602  if(tp.Pos[0] < 0) continue;
603  if(tp.Pos[0] < minWire) minWire = tp.Pos[0];
604  if(tp.Pos[0] > maxWire) maxWire = tp.Pos[0];
605  ++cnt2;
606  }
607  if(cnt2 == 0) return 0;
608  int span = maxWire - minWire;
609  if(span <= 0) return 0;
610  std::vector<unsigned short> wcnt(span);
611  for(auto& tp : tj1.Pts) {
612  if(tp.Chg == 0) continue;
613  if(tp.Pos[0] < -0.4) continue;
614  int indx = std::nearbyint(tp.Pos[0] - minWire);
615  if(indx < 0 || indx > span - 1) continue;
616  ++wcnt[indx];
617  }
618  for(auto& tp : tj2.Pts) {
619  if(tp.Chg == 0) continue;
620  if(tp.Pos[0] < -0.4) continue;
621  int indx = std::nearbyint(tp.Pos[0] - minWire);
622  if(indx < 0 || indx > span - 1) continue;
623  ++wcnt[indx];
624  }
625  float cntOverlap = 0;
626  for(auto cnt : wcnt) if(cnt > 1) ++cntOverlap;
627  if(cnt1 < cnt2) {
628  return cntOverlap / cnt1;
629  } else {
630  return cntOverlap / cnt2;
631  }
632 
633  } // OverlapFraction
634 
636  unsigned short AngleRange(TjStuff& tjs, TrajPoint const& tp)
637  {
638  return AngleRange(tjs, tp.Ang);
639  }
640 
643  {
644  unsigned short ar = AngleRange(tjs, tp.Ang);
645  if(ar == tjs.AngleRanges.size() - 1) {
646  // Very large angle
647  tp.AngleCode = 2;
648  } else if(tjs.AngleRanges.size() > 2 && ar == tjs.AngleRanges.size() - 2) {
649  // Large angle
650  tp.AngleCode = 1;
651  } else {
652  // Small angle
653  tp.AngleCode = 0;
654  }
655 
656  } // SetAngleCode
657 
659  unsigned short AngleRange(TjStuff& tjs, float angle)
660  {
661  // returns the index of the angle range
662  if(angle > M_PI) angle = M_PI;
663  if(angle < -M_PI) angle = M_PI;
664  if(angle < 0) angle = -angle;
665  if(angle > M_PI/2) angle = M_PI - angle;
666  for(unsigned short ir = 0; ir < tjs.AngleRanges.size(); ++ir) {
667  if(angle < tjs.AngleRanges[ir]) return ir;
668  }
669  return tjs.AngleRanges.size() - 1;
670  } // AngleRange
671 
673  void FitTraj(TjStuff& tjs, Trajectory& tj)
674  {
675  // Jacket around FitTraj to fit the leading edge of the supplied trajectory
676  unsigned short originPt = tj.EndPt[1];
677  unsigned short npts = tj.Pts[originPt].NTPsFit;
678  TrajPoint tpFit;
679  unsigned short fitDir = -1;
680  FitTraj(tjs, tj, originPt, npts, fitDir, tpFit);
681  tj.Pts[originPt] = tpFit;
682 
683  } // FitTraj
684 
686  void FitTraj(TjStuff& tjs, Trajectory& tj, unsigned short originPt, unsigned short npts, short fitDir, TrajPoint& tpFit)
687  {
688  // Fit the supplied trajectory using HitPos positions with the origin at originPt.
689  // The npts is interpreted as the number of points on each side of the origin
690  // The allowed modes are as follows, where i denotes a TP that is included, . denotes
691  // a TP with no hits, and x denotes a TP that is not included
692  //TP 012345678 fitDir originPt npts
693  // Oiiixxxxx 1 0 4 << npts in the fit
694  // xi.iiOxxx -1 5 4
695  // xiiiOiiix 0 4 4 << 2 * npts + 1 points in the fit
696  // xxxiO.ixx 0 4 1
697  // 0iiixxxxx 0 0 4
698  // This routine puts the results into tp if the fit is successfull. The
699  // fit "direction" is in increasing order along the trajectory from 0 to tj.Pts.size() - 1.
700 
701  // static const float twoPi = 2 * M_PI;
702 
703  if(originPt > tj.Pts.size() - 1) {
704  mf::LogWarning("TC")<<"FitTraj: Requesting fit of invalid TP "<<originPt;
705  return;
706  }
707 
708  // copy the origin TP into the fit TP
709  tpFit = tj.Pts[originPt];
710  // Assume that the fit will fail
711  tpFit.FitChi = 999;
712  if(fitDir < -1 || fitDir > 1) return;
713 
714  std::vector<double> x, y;
715  Point2_t origin = tj.Pts[originPt].HitPos;
716  // Use TP position if there aren't any hits on it
717  if(tj.Pts[originPt].Chg == 0) origin = tj.Pts[originPt].Pos;
718 
719  // simple two point case
720  if(NumPtsWithCharge(tjs, tj, false) == 2) {
721  for(unsigned short ipt = tj.EndPt[0]; ipt < tj.EndPt[1]; ++ipt) {
722  if(tj.Pts[ipt].Chg == 0) continue;
723  double xx = tj.Pts[ipt].HitPos[0] - origin[0];
724  double yy = tj.Pts[ipt].HitPos[1] - origin[1];
725  x.push_back(xx);
726  y.push_back(yy);
727  } // ii
728  if(x.size() != 2) return;
729  if(x[0] == x[1]) {
730  // Either + or - pi/2
731  tpFit.Ang = M_PI/2;
732  if(y[1] < y[0]) tpFit.Ang = -tpFit.Ang;
733  } else {
734  double dx = x[1] - x[0];
735  double dy = y[1] - y[0];
736  tpFit.Ang = atan2(dy, dx);
737  }
738  tpFit.Dir[0] = cos(tpFit.Ang);
739  tpFit.Dir[1] = sin(tpFit.Ang);
740  tpFit.Pos[0] += origin[0];
741  tpFit.Pos[1] += origin[1];
742  tpFit.AngErr = 0.01;
743  tpFit.FitChi = 0.01;
744  SetAngleCode(tjs, tpFit);
745  return;
746  } // two points
747 
748  std::vector<double> w, q;
749  std::array<double, 2> dir;
750  double xx, yy, xr, yr;
751  double chgWt;
752 
753  // Rotate the traj hit position into the coordinate system defined by the
754  // originPt traj point, where x = along the trajectory, y = transverse
755  double rotAngle = tj.Pts[originPt].Ang;
756  double cs = cos(-rotAngle);
757  double sn = sin(-rotAngle);
758 
759  // enter the originPT hit info if it exists
760  if(tj.Pts[originPt].Chg > 0) {
761  xx = tj.Pts[originPt].HitPos[0] - origin[0];
762  yy = tj.Pts[originPt].HitPos[1] - origin[1];
763  xr = cs * xx - sn * yy;
764  yr = sn * xx + cs * yy;
765  x.push_back(xr);
766  y.push_back(yr);
767  chgWt = tj.Pts[originPt].ChgPull;
768  if(chgWt < 1) chgWt = 1;
769  chgWt *= chgWt;
770  w.push_back(chgWt * tj.Pts[originPt].HitPosErr2);
771  }
772 
773  // correct npts to account for the origin point
774  if(fitDir != 0) --npts;
775 
776  // step in the + direction first
777  if(fitDir != -1) {
778  unsigned short cnt = 0;
779  for(unsigned short ipt = originPt + 1; ipt < tj.Pts.size(); ++ipt) {
780  if(tj.Pts[ipt].Chg == 0) continue;
781  xx = tj.Pts[ipt].HitPos[0] - origin[0];
782  yy = tj.Pts[ipt].HitPos[1] - origin[1];
783  xr = cs * xx - sn * yy;
784  yr = sn * xx + cs * yy;
785  x.push_back(xr);
786  y.push_back(yr);
787  chgWt = tj.Pts[ipt].ChgPull;
788  if(chgWt < 1) chgWt = 1;
789  chgWt *= chgWt;
790  w.push_back(chgWt * tj.Pts[ipt].HitPosErr2);
791  ++cnt;
792  if(cnt == npts) break;
793  } // ipt
794  } // fitDir != -1
795 
796  // step in the - direction next
797  if(fitDir != 1 && originPt > 0) {
798  unsigned short cnt = 0;
799  for(unsigned short ii = 1; ii < tj.Pts.size(); ++ii) {
800  unsigned short ipt = originPt - ii;
801  if(ipt > tj.Pts.size() - 1) continue;
802  if(tj.Pts[ipt].Chg == 0) continue;
803  xx = tj.Pts[ipt].HitPos[0] - origin[0];
804  yy = tj.Pts[ipt].HitPos[1] - origin[1];
805  xr = cs * xx - sn * yy;
806  yr = sn * xx + cs * yy;
807  x.push_back(xr);
808  y.push_back(yr);
809  chgWt = tj.Pts[ipt].ChgPull;
810  if(chgWt < 1) chgWt = 1;
811  chgWt *= chgWt;
812  w.push_back(chgWt * tj.Pts[ipt].HitPosErr2);
813  ++cnt;
814  if(cnt == npts) break;
815  if(ipt == 0) break;
816  } // ipt
817  } // fitDir != -1
818 
819  // Not enough points to define a line?
820  if(x.size() < 2) return;
821 
822  double sum = 0.;
823  double sumx = 0.;
824  double sumy = 0.;
825  double sumxy = 0.;
826  double sumx2 = 0.;
827  double sumy2 = 0.;
828 
829  // weight by the charge ratio and accumulate sums
830  double wght;
831  for(unsigned short ipt = 0; ipt < x.size(); ++ipt) {
832  if(w[ipt] < 0.00001) w[ipt] = 0.00001;
833  wght = 1 / w[ipt];
834  sum += wght;
835  sumx += wght * x[ipt];
836  sumy += wght * y[ipt];
837  sumx2 += wght * x[ipt] * x[ipt];
838  sumy2 += wght * y[ipt] * y[ipt];
839  sumxy += wght * x[ipt] * y[ipt];
840  }
841  // calculate coefficients and std dev
842  double delta = sum * sumx2 - sumx * sumx;
843  if(delta == 0) return;
844  // A is the intercept
845  double A = (sumx2 * sumy - sumx * sumxy) / delta;
846  // B is the slope
847  double B = (sumxy * sum - sumx * sumy) / delta;
848 
849  // The chisq will be set below if there are enough points. Don't allow it to be 0
850  // so we can take Chisq ratios later
851  tpFit.FitChi = 0.01;
852  double newang = atan(B);
853  dir[0] = cos(newang);
854  dir[1] = sin(newang);
855  // rotate back into the (w,t) coordinate system
856  cs = cos(rotAngle);
857  sn = sin(rotAngle);
858  tpFit.Dir[0] = cs * dir[0] - sn * dir[1];
859  tpFit.Dir[1] = sn * dir[0] + cs * dir[1];
860  // ensure that the direction is consistent with the originPt direction
861  bool flipDir = false;
862  if(AngleRange(tjs, tj.Pts[originPt]) > 0) {
863  flipDir = std::signbit(tpFit.Dir[1]) != std::signbit(tj.Pts[originPt].Dir[1]);
864  } else {
865  flipDir = std::signbit(tpFit.Dir[0]) != std::signbit(tj.Pts[originPt].Dir[0]);
866  }
867  if(flipDir) {
868  tpFit.Dir[0] = -tpFit.Dir[0];
869  tpFit.Dir[1] = -tpFit.Dir[1];
870  }
871  tpFit.Ang = atan2(tpFit.Dir[1], tpFit.Dir[0]);
872  SetAngleCode(tjs, tpFit);
873  // if(prt) mf::LogVerbatim("TC")<<"FitTraj "<<originPt<<" originPt Dir "<<tj.Pts[originPt].Dir[0]<<" "<<tj.Pts[originPt].Dir[1]<<" rotAngle "<<rotAngle<<" tpFit.Dir "<<tpFit.Dir[0]<<" "<<tpFit.Dir[1]<<" Ang "<<tpFit.Ang<<" flipDir "<<flipDir<<" fit vector size "<<x.size();
874 
875  // rotate (0, intcpt) into (W,T) coordinates
876  tpFit.Pos[0] = -sn * A + origin[0];
877  tpFit.Pos[1] = cs * A + origin[1];
878  // force the origin to be at origin[0]
879  if(tpFit.AngleCode < 2) MoveTPToWire(tpFit, origin[0]);
880 
881  if(x.size() < 3) return;
882 
883  // Calculate chisq/DOF
884  double ndof = x.size() - 2;
885  double varnce = (sumy2 + A*A*sum + B*B*sumx2 - 2 * (A*sumy + B*sumxy - A*B*sumx)) / ndof;
886  if(varnce > 0.) {
887  // Intercept error is not used
888  // InterceptError = sqrt(varnce * sumx2 / delta);
889  double slopeError = sqrt(varnce * sum / delta);
890  tpFit.AngErr = std::abs(atan(slopeError));
891  } else {
892  tpFit.AngErr = 0.01;
893  }
894  sum = 0;
895  // calculate chisq
896  double arg;
897  for(unsigned short ii = 0; ii < y.size(); ++ii) {
898  arg = y[ii] - A - B * x[ii];
899  sum += arg * arg / w[ii];
900  }
901  tpFit.FitChi = sum / ndof;
902 
903  } // FitTraj
904 
906  float TjDirFOM(const TjStuff& tjs, const Trajectory& tj, bool prt)
907  {
908  // Calculate a FOM for the tj to be going from EndPt[0] -> EndPt[1] (FOM = 1)
909  // or EndPt[1] -> EndPt[0] (FOM = -1) by finding the overall charge slope, weighted
910  // by the presence of nearby InShower Tjs
911  if(tj.AlgMod[kKilled]) return 0;
912  if(tj.EndPt[1] - tj.EndPt[0] < 8) return 0;
913 
914  std::vector<double> x, y;
915  Point2_t origin = tj.Pts[tj.EndPt[0]].HitPos;
916  std::vector<double> w, q;
917 
918  unsigned short firstPt = tj.EndPt[0] + 2;
919  unsigned short lastPt = tj.EndPt[1] - 2;
920  if(lastPt < firstPt + 3) return 0;
921  // don't include end points
922  for(unsigned short ipt = firstPt; ipt <= lastPt; ++ipt) {
923  auto& tp = tj.Pts[ipt];
924  if(tp.Chg <= 0) continue;
925  // only consider points that don't overlap with other tjs
926  if(tp.Environment[kEnvOverlap]) continue;
927  double sep = PosSep(tp.Pos, origin);
928  x.push_back(sep);
929  y.push_back((double)tp.Chg);
930  double wght = 0.2 * tp.Chg;
931  w.push_back(wght * wght);
932  } // tp
933  if(w.size() < 3) return 0;
934 
935  double sum = 0.;
936  double sumx = 0.;
937  double sumy = 0.;
938  double sumxy = 0.;
939  double sumx2 = 0.;
940  double sumy2 = 0.;
941 
942  // weight by the charge ratio and accumulate sums
943  double wght;
944  for(unsigned short ipt = 0; ipt < x.size(); ++ipt) {
945  wght = 1 / w[ipt];
946  sum += wght;
947  sumx += wght * x[ipt];
948  sumy += wght * y[ipt];
949  sumx2 += wght * x[ipt] * x[ipt];
950  sumy2 += wght * y[ipt] * y[ipt];
951  sumxy += wght * x[ipt] * y[ipt];
952  }
953  // calculate coefficients and std dev
954  double delta = sum * sumx2 - sumx * sumx;
955  if(delta == 0) return 0;
956  // A is the intercept
957  double A = (sumx2 * sumy - sumx * sumxy) / delta;
958  // B is the slope
959  double B = (sumxy * sum - sumx * sumy) / delta;
960 
961  // Calculate the FOM
962  double ndof = x.size() - 2;
963  double varnce = (sumy2 + A*A*sum + B*B*sumx2 - 2 * (A*sumy + B*sumxy - A*B*sumx)) / ndof;
964  if(varnce <= 0) return 0;
965  double BErr = sqrt(varnce * sum / delta);
966  // scale the error so that the significance is +/-1 when the slope is 3 * error
967  // Note that the error is correct only if the average Chi/DOF = 1 which is probably not the case
968  float slopeSig = B / (3 * BErr);
969  if(slopeSig > 1) slopeSig = 1;
970  if(slopeSig < -1) slopeSig = -1;
971  // rescale it to be in the range of 0 - 1
972  slopeSig = (1 + slopeSig) / 2;
973 
974  if(prt) {
975  mf::LogVerbatim myprt("TC");
976  myprt<<"TjDir: T"<<tj.ID<<" slope "<<std::fixed<<std::setprecision(1)<<B<<" error "<<BErr<<" DirFOM "<<slopeSig;
977  myprt<<" using points from "<<PrintPos(tjs, tj.Pts[firstPt])<<" "<<PrintPos(tjs, tj.Pts[lastPt]);
978  } // prt
979  return slopeSig;
980 
981  } // TjDirFOM
982 
984  void WatchHit(std::string someText, TjStuff& tjs, const unsigned int& wHit, short& wInTraj, const unsigned short& tjID)
985  {
986  // a temp routine to watch when inTraj changes for the supplied hit index, watchHit
987  if(wHit > tjs.fHits.size() - 1) return;
988 
989  if(tjs.fHits[wHit].InTraj != wInTraj) {
990  std::cout<<someText<<" Hit "<<PrintHitShort(tjs.fHits[wHit])<<" was InTraj "<<wInTraj<<" now InTraj "<<tjs.fHits[wHit].InTraj<<" T"<<tjID<<"\n";
991  wInTraj = tjs.fHits[wHit].InTraj;
992  }
993  } // WatchHit
994 
996  void Reverse3DMatchTjs(TjStuff& tjs, PFPStruct& pfp, bool prt)
997  {
998  // Return true if the 3D matched hits in the trajectories in tjs.pfps are in the wrong order in terms of the
999  // physics standpoint, e.g. dQ/dx, muon delta-ray tag, cosmic rays entering the detector, etc.
1000 
1001  // Don't reverse showers
1002  if(pfp.PDGCode == 1111) return;
1003 
1004  bool reverseMe = false;
1005 
1006  // look for stopping Tjs for contained PFParticles
1007  if(!reverseMe) {
1008  unsigned short braggCnt0 = 0;
1009  unsigned short braggCnt1 = 0;
1010  for(auto& tjID : pfp.TjIDs) {
1011  auto& tj = tjs.allTraj[tjID - 1];
1012  if(tj.StopFlag[0][kBragg]) ++braggCnt0;
1013  if(tj.StopFlag[1][kBragg]) ++braggCnt1;
1014  }
1015  if(braggCnt0 > 0 || braggCnt1 > 0) {
1016  pfp.PDGCode = 2212;
1017  // Vote for a Bragg peak at the beginning. It should be at the end
1018  if(braggCnt0 > braggCnt1) reverseMe = true;
1019  } // found a Bragg Peak
1020  } // look for stopping Tjs
1021 
1022  if(!reverseMe) return;
1023 
1024  // All of the trajectories should be reversed
1025  for(auto& tjID : pfp.TjIDs) {
1026  unsigned short itj = tjID - 1;
1027  Trajectory& tj = tjs.allTraj[itj];
1028  tj.AlgMod[kMat3D] = false;
1029  ReverseTraj(tjs, tj);
1030  tj.AlgMod[kMat3D] = true;
1031  } // tjID
1032  // swap the matchVec end info also
1033  std::swap(pfp.XYZ[0], pfp.XYZ[1]);
1034  std::swap(pfp.Dir[0], pfp.Dir[1]);
1035  std::swap(pfp.DirErr[0], pfp.DirErr[1]);
1036  std::swap(pfp.dEdx[0], pfp.dEdx[1]);
1037  std::swap(pfp.dEdxErr[0], pfp.dEdxErr[1]);
1038  std::swap(pfp.Vx3ID[0], pfp.Vx3ID[1]);
1039 
1040  return;
1041 
1042  } // Reverse3DMatchTjs
1043 
1045  unsigned short GetPFPIndex(const TjStuff& tjs, int tjID)
1046  {
1047  // returns the index into the tjs.matchVec vector of the first 3D match that
1048  // includes tjID
1049  if(tjs.pfps.empty()) return USHRT_MAX;
1050  for(unsigned int ipfp = 0; ipfp < tjs.pfps.size(); ++ipfp) {
1051  const auto& pfp = tjs.pfps[ipfp];
1052  if(std::find(pfp.TjIDs.begin(), pfp.TjIDs.end(), tjID) != pfp.TjIDs.end()) return ipfp;
1053  } // indx
1054  return USHRT_MAX;
1055  } // GetPFPIndex
1056 
1058  unsigned short MatchVecIndex(const TjStuff& tjs, int tjID)
1059  {
1060  // returns the index into the tjs.matchVec vector of the first 3D match that
1061  // includes tjID
1062  for(unsigned int ims = 0; ims < tjs.matchVec.size(); ++ims) {
1063  const auto& ms = tjs.matchVec[ims];
1064  if(std::find(ms.TjIDs.begin(), ms.TjIDs.end(), tjID) != ms.TjIDs.end()) return ims;
1065  } // indx
1066  return USHRT_MAX;
1067  } // MatchVecIndex
1068 
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(tjs.fHits[iht].InTraj == tj.ID) tjs.fHits[iht].InTraj = 0;
1076  }
1077  } // tp
1078 
1079  } // ReleaseWorkHits
1080 
1083  {
1084  // Sets InTraj = 0 and UseHit false for all used hits in tp
1085  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1086  if(tp.UseHit[ii]) {
1087  tjs.fHits[tp.Hits[ii]].InTraj = 0;
1088  tp.UseHit[ii] = false;
1089  } // UseHit
1090  } // ii
1091  tp.Chg = 0;
1092  } // UnsetUsedHits
1093 
1095  bool StoreTraj(TjStuff& tjs, Trajectory& tj)
1096  {
1097 
1098  if(!(tj.StepDir == 1 || tj.StepDir == -1)) {
1099  mf::LogError("TC")<<"StoreTraj: Invalid StepDir "<<tj.StepDir;
1100  return false;
1101  }
1102 
1103  if(tjs.allTraj.size() >= USHRT_MAX) {
1104  mf::LogError("TC")<<"StoreTraj: Too many trajectories "<<tjs.allTraj.size();
1105  return false;
1106  }
1107 
1108  // This shouldn't be necessary but do it anyway
1109  SetEndPoints(tjs, tj);
1110 
1111  if(tj.EndPt[1] <= tj.EndPt[0]) return false;
1112  if(tj.EndPt[1] > tj.Pts.size()) return false;
1113  unsigned short npts = tj.EndPt[1] - tj.EndPt[0] + 1;
1114  if(npts < 2) return false;
1115 
1116  auto& endTp0 = tj.Pts[tj.EndPt[0]];
1117  auto& endTp1 = tj.Pts[tj.EndPt[1]];
1118 
1119  // Calculate the charge near the end and beginning if necessary. This must be a short
1120  // trajectory. Find the average using 4 points
1121  if(endTp0.AveChg <= 0) {
1122  unsigned short cnt = 0;
1123  float sum = 0;
1124  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
1125  if(tj.Pts[ipt].Chg == 0) continue;
1126  sum += tj.Pts[ipt].Chg;
1127  ++cnt;
1128  if(cnt == 4) break;
1129  }
1130  tj.Pts[tj.EndPt[0]].AveChg = sum / (float)cnt;
1131  }
1132  if(endTp1.AveChg <= 0 && npts < 5) endTp1.AveChg = endTp0.AveChg;
1133  if(endTp1.AveChg <= 0) {
1134  float sum = 0;
1135  unsigned short cnt = 0;
1136  for(unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
1137  short ipt = tj.EndPt[1] - ii;
1138  if(ipt < 0) break;
1139  if(tj.Pts[ipt].Chg == 0) continue;
1140  sum += tj.Pts[ipt].Chg;
1141  ++cnt;
1142  if(cnt == 4) break;
1143  if(ipt == 0) break;
1144  } // ii
1145  tj.Pts[tj.EndPt[1]].AveChg = sum / (float)cnt;
1146  } // begin charge == end charge
1147 
1148 
1149  tj.DirFOM = TjDirFOM(tjs, tj, false);
1150  UpdateTjChgProperties("ST", tjs, tj, false);
1151 
1152  int trID = tjs.allTraj.size() + 1;
1153 
1154  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
1155  for(unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
1156  if(tj.Pts[ipt].UseHit[ii]) {
1157  unsigned int iht = tj.Pts[ipt].Hits[ii];
1158  if(tjs.fHits[iht].InTraj > 0) {
1159  mf::LogWarning("TC")<<"StoreTraj: Failed trying to store hit "<<PrintHit(tjs.fHits[iht])<<" in T"<<trID<<" but it is used in T"<<tjs.fHits[iht].InTraj<<" with WorkID "<<tjs.allTraj[tjs.fHits[iht].InTraj-1].WorkID<<" Print and quit";
1160 // PrintTrajectory("ST", tjs, tj, USHRT_MAX);
1161  ReleaseHits(tjs, tj);
1162  return false;
1163  } // error
1164  tjs.fHits[iht].InTraj = trID;
1165  }
1166  } // ii
1167  } // ipt
1168 
1169  // ensure that inTraj is clean for the ID
1170  for(unsigned int iht = 0; iht < tjs.fHits.size(); ++iht) {
1171  if(tjs.fHits[iht].InTraj == tj.ID) {
1172  mf::LogWarning("TC")<<"StoreTraj: Hit "<<PrintHit(tjs.fHits[iht])<<" thinks it belongs to T"<<tj.ID<<" but it isn't in the Tj\n";
1173 // PrintTrajectory("ST", tjs, tj, USHRT_MAX);
1174  return false;
1175  }
1176  } // iht
1177 
1178  tj.WorkID = tj.ID;
1179  tj.ID = trID;
1180  // Don't clobber the ParentID if it was defined by the calling function
1181  if(tj.ParentID == 0) tj.ParentID = trID;
1182  tjs.allTraj.push_back(tj);
1183 // if(prt) mf::LogVerbatim("TC")<<"StoreTraj trID "<<trID<<" CTP "<<tj.CTP<<" EndPts "<<tj.EndPt[0]<<" "<<tj.EndPt[1];
1184  if(debug.Hit != UINT_MAX) {
1185  // print out some debug info
1186  for(unsigned short ipt = 0; ipt < tj.Pts.size(); ++ipt) {
1187  for(unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
1188  unsigned int iht = tj.Pts[ipt].Hits[ii];
1189  if(iht == debug.Hit) std::cout<<"Debug hit appears in trajectory w WorkID "<<tj.WorkID<<" UseHit "<<tj.Pts[ipt].UseHit[ii]<<"\n";
1190  } // ii
1191  } // ipt
1192  } // debug.Hit ...
1193 
1194  return true;
1195 
1196  } // StoreTraj
1197 
1199  bool InTrajOK(TjStuff& tjs, std::string someText)
1200  {
1201  // Check tjs.allTraj -> InTraj associations
1202 
1203  unsigned short tID;
1204  unsigned int iht;
1205  unsigned short itj = 0;
1206  std::vector<unsigned int> tHits;
1207  std::vector<unsigned int> atHits;
1208  for(auto& tj : tjs.allTraj) {
1209  // ignore abandoned trajectories
1210  if(tj.AlgMod[kKilled]) continue;
1211  tID = tj.ID;
1212  if(tj.AlgMod[kKilled]) {
1213  std::cout<<someText<<" ChkInTraj hit size mis-match in tj ID "<<tj.ID<<" AlgBitNames";
1214  for(unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) if(tj.AlgMod[ib]) std::cout<<" "<<AlgBitNames[ib];
1215  std::cout<<"\n";
1216  continue;
1217  }
1218  tHits = PutTrajHitsInVector(tj, kUsedHits);
1219  if(tHits.size() < 2) {
1220  std::cout<<someText<<" ChkInTraj: Insufficient hits in traj "<<tj.ID<<"\n";
1221  PrintTrajectory("CIT", tjs, tj, USHRT_MAX);
1222  continue;
1223  }
1224  std::sort(tHits.begin(), tHits.end());
1225  atHits.clear();
1226  for(iht = 0; iht < tjs.fHits.size(); ++iht) {
1227  if(tjs.fHits[iht].InTraj == tID) atHits.push_back(iht);
1228  } // iht
1229  if(atHits.size() < 2) {
1230  std::cout<<someText<<" ChkInTraj: Insufficient hits in atHits in traj "<<tj.ID<<" Killing it\n";
1231  tj.AlgMod[kKilled] = true;
1232  continue;
1233  }
1234  if(!std::equal(tHits.begin(), tHits.end(), atHits.begin())) {
1235  mf::LogVerbatim myprt("TC");
1236  myprt<<someText<<" ChkInTraj failed: inTraj - UseHit mis-match for T"<<tID<<" tj.WorkID "<<tj.WorkID<<" atHits size "<<atHits.size()<<" tHits size "<<tHits.size()<<" in CTP "<<tj.CTP<<"\n";
1237  myprt<<"AlgMods: ";
1238  for(unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) if(tj.AlgMod[ib]) myprt<<" "<<AlgBitNames[ib];
1239  myprt<<"\n";
1240  myprt<<"index inTraj UseHit \n";
1241  for(iht = 0; iht < atHits.size(); ++iht) {
1242  myprt<<"iht "<<iht<<" "<<PrintHit(tjs.fHits[atHits[iht]]);
1243  if(iht < tHits.size()) myprt<<" "<<PrintHit(tjs.fHits[tHits[iht]]);
1244  if(atHits[iht] != tHits[iht]) myprt<<" <<< "<<atHits[iht]<<" != "<<tHits[iht];
1245  myprt<<"\n";
1246  } // iht
1247  if(tHits.size() > atHits.size()) {
1248  for(iht = atHits.size(); iht < atHits.size(); ++iht) {
1249  myprt<<"atHits "<<iht<<" "<<PrintHit(tjs.fHits[atHits[iht]])<<"\n";
1250  } // iht
1251  PrintTrajectory("CIT", tjs, tj, USHRT_MAX);
1252  } // tHit.size > atHits.size()
1253  return false;
1254  }
1255  // check the VtxID
1256  for(unsigned short end = 0; end < 2; ++end) {
1257  if(tj.VtxID[end] > tjs.vtx.size()) {
1258  mf::LogVerbatim("TC")<<someText<<" ChkInTraj: Bad VtxID "<<tj.ID;
1259  std::cout<<someText<<" ChkInTraj: Bad VtxID "<<tj.ID<<" vtx size "<<tjs.vtx.size()<<"\n";
1260  tj.AlgMod[kKilled] = true;
1261  PrintTrajectory("CIT", tjs, tj, USHRT_MAX);
1262  return false;
1263  }
1264  } // end
1265  ++itj;
1266  } // tj
1267  return true;
1268 
1269  } // InTrajOK
1270 
1272  void CheckTrajBeginChg(TjStuff& tjs, unsigned short itj, bool prt)
1273  {
1274  // This function is called after the beginning of the tj has been inspected to see if
1275  // reverse propagation was warranted. Trajectory points at the beginning were removed by
1276  // this process.
1277  // A search has been made for a Bragg peak with nothing
1278  // found. Here we look for a charge pattern like the following, where C means large charge
1279  // and c means lower charge:
1280  // CCCCCCccccccc
1281  // The charge in the two regions should be fairly uniform.
1282 
1283  // This function may split the trajectory so it needs to have been stored
1284  if(itj > tjs.allTraj.size() - 1) return;
1285  auto& tj = tjs.allTraj[itj];
1286 
1287  if(!tjs.UseAlg[kBeginChg]) return;
1288  if(tj.StopFlag[0][kBragg]) return;
1289  if(tj.AlgMod[kFTBRvProp]) return;
1290  if(tj.AlgMod[kKilled]) return;
1291  if(tj.Pts.size() < 20) return;
1292 
1293  // look for a large drop between the average charge near the beginning
1294  float chg2 = tj.Pts[tj.EndPt[0] + 2].AveChg;
1295  // and the average charge 15 points away
1296  float chg15 = tj.Pts[tj.EndPt[0] + 15].AveChg;
1297  if(chg2 < 3 * chg15) return;
1298 
1299  // find the point where the charge falls below the mid-point
1300  float midChg = 0.5 * (chg2 + chg15);
1301 
1302  unsigned short breakPt = USHRT_MAX;
1303  for(unsigned short ipt = tj.EndPt[0] + 3; ipt < 15; ++ipt) {
1304  float chgm2 = tj.Pts[ipt - 2].Chg;
1305  if(chgm2 == 0) continue;
1306  float chgm1 = tj.Pts[ipt - 1].Chg;
1307  if(chgm1 == 0) continue;
1308  float chgp1 = tj.Pts[ipt + 1].Chg;
1309  if(chgp1 == 0) continue;
1310  float chgp2 = tj.Pts[ipt + 2].Chg;
1311  if(chgp2 == 0) continue;
1312  if(chgm2 > midChg && chgm1 > midChg && chgp1 < midChg && chgp2 < midChg) {
1313  breakPt = ipt;
1314  break;
1315  }
1316  } // breakPt
1317  if(breakPt == USHRT_MAX) return;
1318  // Create a vertex at the break point
1319  VtxStore aVtx;
1320  aVtx.Pos = tj.Pts[breakPt].Pos;
1321  aVtx.NTraj = 2;
1322  aVtx.Pass = tj.Pass;
1323  aVtx.Topo = 8;
1324  aVtx.ChiDOF = 0;
1325  aVtx.CTP = tj.CTP;
1326  aVtx.ID = tjs.vtx.size() + 1;
1327  aVtx.Stat[kFixed] = true;
1328  unsigned short ivx = tjs.vtx.size();
1329  if(!StoreVertex(tjs, aVtx)) return;
1330  if(!SplitTraj(tjs, itj, breakPt, ivx, prt)) {
1331  if(prt) mf::LogVerbatim("TC")<<"CTBC: Failed to split trajectory";
1332  MakeVertexObsolete(tjs, tjs.vtx[ivx], false);
1333  return;
1334  }
1335  SetVx2Score(tjs, prt);
1336 
1337  if(prt) mf::LogVerbatim("TC")<<"CTBC: Split T"<<tj.ID<<" at "<<PrintPos(tjs, tj.Pts[breakPt].Pos)<<"\n";
1338 
1339  } // CheckTrajBeginChg
1340 
1342  void TrimEndPts(std::string fcnLabel, TjStuff& tjs, Trajectory& tj, const std::vector<float>& fQualityCuts, bool prt)
1343  {
1344  // Trim the hits off the end until there are at least MinPts consecutive hits at the end
1345  // and the fraction of hits on the trajectory exceeds fQualityCuts[0]
1346  // Minimum length requirement accounting for dead wires where - denotes a wire with a point
1347  // and D is a dead wire. Here is an example with minPts = 3
1348  // ---DDDDD--- is OK
1349  // ----DD-DD-- is OK
1350  // ----DDD-D-- is OK
1351  // ----DDDDD-- is not OK
1352 
1353  if(!tjs.UseAlg[kTEP]) return;
1354 
1355  unsigned short npwc = NumPtsWithCharge(tjs, tj, false);
1356  short minPts = fQualityCuts[1];
1357  if(minPts < 1) return;
1358  if(npwc < minPts) return;
1359 
1360  // handle short tjs
1361  if(npwc == minPts + 1) {
1362  unsigned short endPt1 = tj.EndPt[1];
1363  auto& tp = tj.Pts[endPt1];
1364  auto& ptp = tj.Pts[endPt1 - 1];
1365  // remove the last point if the previous point has no charge or if
1366  // it isn't on the next wire
1367  float dwire = std::abs(ptp.Pos[0] - tp.Pos[0]);
1368  if(ptp.Chg == 0 || dwire > 1.1) {
1369  UnsetUsedHits(tjs, tp);
1370  SetEndPoints(tjs, tj);
1371  tj.AlgMod[kTEP] = true;
1372  }
1373  return;
1374  } // short tj
1375 
1376  // find the separation between adjacent points, starting at the end
1377  short lastPt = 0;
1378  for(lastPt = tj.EndPt[1]; lastPt >= minPts; --lastPt) {
1379  // check for an error
1380  if(lastPt == 1) break;
1381  if(tj.Pts[lastPt].Chg == 0) continue;
1382  // number of adjacent points on adjacent wires
1383  unsigned short nadj = 0;
1384  unsigned short npwc = 0;
1385  for(short ipt = lastPt - minPts; ipt < lastPt; ++ipt) {
1386  if(ipt < 2) break;
1387  // the current point
1388  auto& tp = tj.Pts[ipt];
1389  // the previous point
1390  auto& ptp = tj.Pts[ipt - 1];
1391  if(tp.Chg > 0 && ptp.Chg > 0) {
1392  ++npwc;
1393  if(std::abs(tp.Pos[0] - ptp.Pos[0]) < 1.5) ++nadj;
1394  }
1395  } // ipt
1396  float ntpwc = NumPtsWithCharge(tjs, tj, true, tj.EndPt[0], lastPt);
1397  float nwires = std::abs(tj.Pts[tj.EndPt[0]].Pos[0] - tj.Pts[lastPt].Pos[0]) + 1;
1398  float hitFrac = ntpwc / nwires;
1399  if(prt) mf::LogVerbatim("TC")<<fcnLabel<<"-TEP: T"<<tj.ID<<" lastPt "<<lastPt<<" npwc "<<npwc<<" nadj "<<nadj<<" hitFrac "<<hitFrac;
1400  if(hitFrac > fQualityCuts[0] && npwc == minPts && nadj == minPts) break;
1401  } // lastPt
1402 
1403  // trim the last point if it just after a dead wire.
1404  if(tj.Pts[lastPt].Pos[0] > -0.4) {
1405  unsigned int prevWire = std::nearbyint(tj.Pts[lastPt].Pos[0]);
1406  if(tj.StepDir > 0) {
1407  --prevWire;
1408  } else {
1409  ++prevWire;
1410  }
1411  if(prt) {
1412  mf::LogVerbatim("TC")<<fcnLabel<<"-TEP: is prevWire "<<prevWire<<" dead? ";
1413  }
1414  unsigned short plane = DecodeCTP(tj.CTP).Plane;
1415  if(prevWire < tjs.NumWires[plane] && tjs.WireHitRange[plane][prevWire].first == -1) --lastPt;
1416  } // valid Pos[0]
1417 
1418  // Nothing needs to be done
1419  if(lastPt == tj.EndPt[1]) {
1420  if(prt) mf::LogVerbatim("TC")<<fcnLabel<<"-TEPo: Tj is OK";
1421  return;
1422  }
1423 
1424  // clear the points after lastPt
1425  for(unsigned short ipt = lastPt + 1; ipt <= tj.EndPt[1]; ++ipt) UnsetUsedHits(tjs, tj.Pts[ipt]);
1426  SetEndPoints(tjs, tj);
1427  tj.AlgMod[kTEP] = true;
1428  if(prt) {
1429  fcnLabel += "-TEPo";
1430  PrintTrajectory(fcnLabel, tjs, tj, USHRT_MAX);
1431  }
1432 
1433  } // TrimEndPts
1434 
1436  void ChkChgAsymmetry(TjStuff& tjs, Trajectory& tj, bool prt)
1437  {
1438  // looks for a high-charge point in the trajectory which may be due to the
1439  // trajectory crossing an interaction vertex. The properties of points on the opposite
1440  // sides of the high-charge point are analyzed. If significant differences are found, all points
1441  // near the high-charge point are removed as well as those from that point to the end
1442  if(!tjs.UseAlg[kChkChgAsym]) return;
1443  unsigned short npts = tj.EndPt[1] - tj.EndPt[0];
1444  if(prt) mf::LogVerbatim("TC")<<" Inside ChkChgAsymmetry T"<<tj.ID;
1445  // ignore long tjs
1446  if(npts > 50) return;
1447  // ignore short tjs
1448  if(npts < 8) return;
1449  // require the charge pull > 5
1450  float bigPull = 5;
1451  unsigned short atPt = 0;
1452  // Don't consider the first/last few points in case there is a Bragg peak
1453  for(unsigned short ipt = tj.EndPt[0] + 2; ipt <= tj.EndPt[1] - 2; ++ipt) {
1454  auto& tp = tj.Pts[ipt];
1455  if(tp.ChgPull > bigPull) {
1456  bigPull = tp.ChgPull;
1457  atPt = ipt;
1458  }
1459  } // ipt
1460  if(atPt == 0) return;
1461  // require that this point be near the DS end
1462  if((atPt - tj.EndPt[0]) < 0.5 * npts) return;
1463  if(prt) mf::LogVerbatim("TC")<<"CCA: T"<<tj.ID<<" Large Chg point at "<<atPt<<". Check charge asymmetry around it.";
1464  unsigned short nchk = 0;
1465  unsigned short npos = 0;
1466  unsigned short nneg = 0;
1467  for(short ii = 1; ii < 5; ++ii) {
1468  short iplu = atPt + ii;
1469  if(iplu > tj.EndPt[1]) break;
1470  short ineg = atPt - ii;
1471  if(ineg < tj.EndPt[0]) break;
1472  if(tj.Pts[iplu].Chg == 0) continue;
1473  if(tj.Pts[ineg].Chg == 0) continue;
1474  float asym = (tj.Pts[iplu].Chg - tj.Pts[ineg].Chg) / (tj.Pts[iplu].Chg + tj.Pts[ineg].Chg);
1475  ++nchk;
1476  if(asym > 0.5) ++npos;
1477  if(asym < -0.5) ++nneg;
1478  if(prt) mf::LogVerbatim("TC")<<" ineg "<<ineg<<" iplu "<<iplu<<" asym "<<asym<<" nchk "<<nchk;
1479  } // ii
1480  if(nchk < 3) return;
1481  // require most of the points be very positive or very negative
1482  nchk -= 2;
1483  bool doTrim = (nneg > nchk) || (npos > nchk);
1484  if(!doTrim) return;
1485  // remove all the points at the end starting at the one just before the peak if the pull is not so good
1486  auto& prevTP = tj.Pts[atPt - 1];
1487  if(std::abs(prevTP.ChgPull) > 2) --atPt;
1488  for(unsigned short ipt = atPt; ipt <= tj.EndPt[1]; ++ipt) UnsetUsedHits(tjs, tj.Pts[ipt]);
1489  SetEndPoints(tjs, tj);
1490  tj.AlgMod[kChkChgAsym] = true;
1491  if(prt) PrintTrajectory("CCA", tjs, tj, USHRT_MAX);
1492  } // ChkChgAsymmetry
1493 
1495  bool SignalBetween(TjStuff& tjs, const TrajPoint& tp1, const TrajPoint& tp2, const float& MinWireSignalFraction, bool prt)
1496  {
1497  // Returns true if there is a signal on > MinWireSignalFraction of the wires between tp1 and tp2.
1498  if(MinWireSignalFraction == 0) return true;
1499 
1500  if(tp1.Pos[0] < -0.4 || tp2.Pos[0] < -0.4) return false;
1501  int fromWire = std::nearbyint(tp1.Pos[0]);
1502  int toWire = std::nearbyint(tp2.Pos[0]);
1503 
1504  if(fromWire == toWire) {
1505  TrajPoint tp = tp1;
1506  // check for a signal midway between
1507  tp.Pos[1] = 0.5 * (tp1.Pos[1] + tp2.Pos[1]);
1508  if(prt) mf::LogVerbatim("TC")<<" SignalBetween fromWire = toWire = "<<fromWire<<" SignalAtTp? "<<SignalAtTp(tjs, tp);
1509  return SignalAtTp(tjs, tp);
1510  }
1511  // define a trajectory point located at tp1 that has a direction towards tp2
1512  TrajPoint tp;
1513  if(!MakeBareTrajPoint(tjs, tp1, tp2, tp)) return true;
1514  return SignalBetween(tjs, tp, toWire, MinWireSignalFraction, prt);
1515  } // SignalBetween
1516 
1517 
1518 
1520  bool SignalBetween(TjStuff& tjs, TrajPoint tp, float toPos0, const float& MinWireSignalFraction, bool prt)
1521  {
1522  // Returns true if there is a signal on > MinWireSignalFraction of the wires between tp and toPos0.
1523  return ChgFracBetween(tjs, tp, toPos0, prt) >= MinWireSignalFraction;
1524  } // SignalBetween
1525 
1527  float ChgFracBetween(TjStuff& tjs, TrajPoint tp, float toPos0, bool prt)
1528  {
1529  // Returns the fraction of wires between tp.Pos[0] and toPos0 that have a hit
1530  // on the line defined by tp.Pos and tp.Dir
1531 
1532  if(tp.Pos[0] < -0.4 || toPos0 < -0.4) return 0;
1533  int fromWire = std::nearbyint(tp.Pos[0]);
1534  int toWire = std::nearbyint(toPos0);
1535 
1536  if(fromWire == toWire) {
1537  if(prt) mf::LogVerbatim("TC")<<" SignalBetween fromWire = toWire = "<<fromWire<<" SignalAtTp? "<<SignalAtTp(tjs, tp);
1538  return SignalAtTp(tjs, tp);
1539  }
1540 
1541  int nWires = abs(toWire - fromWire) + 1;
1542 
1543  if(std::abs(tp.Dir[0]) < 0.001) tp.Dir[0] = 0.001;
1544  float stepSize = std::abs(1/tp.Dir[0]);
1545  // ensure that we step in the right direction
1546  if(toWire > fromWire && tp.Dir[0] < 0) stepSize = -stepSize;
1547  if(toWire < fromWire && tp.Dir[0] > 0) stepSize = -stepSize;
1548  float nsig = 0;
1549  float num = 0;
1550  for(unsigned short cnt = 0; cnt < nWires; ++cnt) {
1551  ++num;
1552  if(SignalAtTp(tjs, tp)) ++nsig;
1553  tp.Pos[0] += tp.Dir[0] * stepSize;
1554  tp.Pos[1] += tp.Dir[1] * stepSize;
1555  } // cnt
1556  float sigFrac = nsig / num;
1557  if(prt) mf::LogVerbatim("TC")<<" ChgFracBetween fromWire "<<fromWire<<" toWire "<<toWire<<" nWires "<<num<<" nsig "<<nsig<<" "<<sigFrac;
1558  return sigFrac;
1559 
1560  } // ChgFracBetween
1561 
1563  bool TrajHitsOK(TjStuff& tjs, const std::vector<unsigned int>& iHitsInMultiplet, const std::vector<unsigned int>& jHitsInMultiplet)
1564  {
1565  // Hits (assume to be on adjacent wires have an acceptable signal overlap
1566 
1567  if(iHitsInMultiplet.empty() || jHitsInMultiplet.empty()) return false;
1568 
1569  float sum;
1570  float cvI = HitsPosTick(tjs, iHitsInMultiplet, sum, kAllHits);
1571  float minI = 1E6;
1572  float maxI = 0;
1573  for(auto& iht : iHitsInMultiplet) {
1574  float cv = tjs.fHits[iht].PeakTime;
1575  float rms = tjs.fHits[iht].RMS;
1576  float arg = cv - 3 * rms;
1577  if(arg < minI) minI = arg;
1578  arg = cv + 3 * rms;
1579  if(arg > maxI) maxI = arg;
1580  }
1581 
1582  float cvJ = HitsPosTick(tjs, jHitsInMultiplet, sum, kAllHits);
1583  float minJ = 1E6;
1584  float maxJ = 0;
1585  for(auto& jht : jHitsInMultiplet) {
1586  float cv = tjs.fHits[jht].PeakTime;
1587  float rms = tjs.fHits[jht].RMS;
1588  float arg = cv - 3 * rms;
1589  if(arg < minJ) minJ = arg;
1590  arg = cv + 3 * rms;
1591  if(arg > maxJ) maxJ = arg;
1592  }
1593 
1594  if(cvI < cvJ) {
1595  if(maxI > minJ) return true;
1596  } else {
1597  if(minI < maxJ) return true;
1598  }
1599  return false;
1600  } // TrajHitsOK
1601 
1603  bool TrajHitsOK(TjStuff& tjs, const unsigned int iht, const unsigned int jht)
1604  {
1605  // ensure that two adjacent hits have an acceptable overlap
1606  if(iht > tjs.fHits.size() - 1) return false;
1607  if(jht > tjs.fHits.size() - 1) return false;
1608  // require that they be on adjacent wires
1609  TCHit& ihit = tjs.fHits[iht];
1610  TCHit& jhit = tjs.fHits[jht];
1611  int iwire = ihit.ArtPtr->WireID().Wire;
1612  int jwire = jhit.ArtPtr->WireID().Wire;
1613  if(std::abs(iwire - jwire) > 1) return false;
1614  if(ihit.PeakTime > jhit.PeakTime) {
1615  float minISignal = ihit.PeakTime - 3 * ihit.RMS;
1616  float maxJSignal = jhit.PeakTime + 3 * ihit.RMS;
1617  if(maxJSignal > minISignal) return true;
1618  } else {
1619  float maxISignal = ihit.PeakTime + 3 * ihit.RMS;
1620  float minJSignal = jhit.PeakTime - 3 * ihit.RMS;
1621  if(minJSignal > maxISignal) return true;
1622  }
1623  return false;
1624  } // TrajHitsOK
1625 
1627  float ExpectedHitsRMS(TjStuff& tjs, const TrajPoint& tp)
1628  {
1629  // returns the expected RMS of hits for the trajectory point in ticks
1630  if(std::abs(tp.Dir[0]) > 0.001) {
1631  geo::PlaneID planeID = DecodeCTP(tp.CTP);
1632  return 1.5 * tjs.AveHitRMS[planeID.Plane] + 2 * std::abs(tp.Dir[1]/tp.Dir[0])/tjs.UnitsPerTick;
1633  } else {
1634  return 500;
1635  }
1636  } // ExpectedHitsRMS
1637 
1639  bool SignalAtTp(TjStuff& tjs, const TrajPoint& tp)
1640  {
1641  // returns true if there is a hit near tp.Pos
1642 
1643  if(tp.Pos[0] < -0.4) return false;
1644  unsigned int wire = std::nearbyint(tp.Pos[0]);
1645  geo::PlaneID planeID = DecodeCTP(tp.CTP);
1646  unsigned int ipl = planeID.Plane;
1647  if(wire >= tjs.NumWires[ipl]) return false;
1648  if(tp.Pos[1] > tjs.MaxPos1[ipl]) return false;
1649  // Assume dead wires have a signal
1650  if(tjs.WireHitRange[ipl][wire].first == -1) return true;
1651  float projTick = (float)(tp.Pos[1] / tjs.UnitsPerTick);
1652  float tickRange = 0;
1653  if(std::abs(tp.Dir[1]) != 0) {
1654  tickRange = std::abs(0.5 / tp.Dir[1]) / tjs.UnitsPerTick;
1655  // don't let it get too large
1656  if(tickRange > 40) tickRange = 40;
1657  }
1658  float loTpTick = projTick - tickRange;
1659  float hiTpTick = projTick + tickRange;
1660  unsigned int firstHit = (unsigned int)tjs.WireHitRange[ipl][wire].first;
1661  unsigned int lastHit = (unsigned int)tjs.WireHitRange[ipl][wire].second;
1662 
1663  for(unsigned int iht = firstHit; iht < lastHit; ++iht) {
1664  TCHit& hit = tjs.fHits[iht];
1665  if(projTick < hit.PeakTime) {
1666  float loHitTick = hit.PeakTime - 3 * hit.RMS;
1667  if(hiTpTick > loHitTick) return true;
1668  } else {
1669  float hiHitTick = hit.PeakTime + 3 * hit.RMS;
1670  if(loTpTick < hiHitTick) return true;
1671  }
1672  } // iht
1673  return false;
1674 
1675  } // SignalAtTp
1676 
1678  float TpSumHitChg(TjStuff& tjs, TrajPoint const& tp){
1679  float totchg = 0;
1680  for (size_t i = 0; i<tp.Hits.size(); ++i){
1681  if (!tp.UseHit[i]) continue;
1682  totchg += tjs.fHits[tp.Hits[i]].Integral;
1683  }
1684  return totchg;
1685  } // TpSumHitChg
1686 /*
1688  bool CheckHitClusterAssociations(TjStuff& tjs)
1689  {
1690  // check hit - cluster associations
1691 
1692  if(tjs.fHits.size() != tjs.inClus.size()) {
1693  mf::LogWarning("TC")<<"CHCA: Sizes wrong "<<tjs.fHits.size()<<" "<<tjs.inClus.size();
1694  return false;
1695  }
1696 
1697  // check cluster -> hit association
1698  for(unsigned short icl = 0; icl < tjs.tcl.size(); ++icl) {
1699  if(tjs.tcl[icl].ID <= 0) continue;
1700  int clID = tjs.tcl[icl].ID;
1701  for(unsigned short ii = 0; ii < tjs.tcl[icl].tclhits.size(); ++ii) {
1702  unsigned int iht = tjs.tcl[icl].tclhits[ii];
1703  if(iht > tjs.fHits.size() - 1) {
1704  mf::LogWarning("CC")<<"CHCA: Bad tclhits index "<<iht<<" tjs.fHits size "<<tjs.fHits.size();
1705  return false;
1706  } // iht > tjs.fHits.size() - 1
1707  if(tjs.inClus[iht] != clID) {
1708  mf::LogError("TC")<<"CHCA: Bad cluster -> hit association. clID "<<clID<<" hit "<<PrintHit(tjs.fHits[iht])<<" tjs.inClus "<<tjs.inClus[iht]<<" CTP "<<tjs.tcl[icl].CTP;
1709  return false;
1710  }
1711  } // ii
1712  } // icl
1713 
1714  // check hit -> cluster association
1715  for(unsigned int iht = 0; iht < tjs.fHits.size(); ++iht) {
1716  if(tjs.inClus[iht] <= 0) continue;
1717  unsigned int icl = tjs.inClus[iht] - 1;
1718  // see if the cluster is obsolete
1719  if(tjs.tcl[icl].ID == 0) {
1720  mf::LogError("TC")<<"CHCA: Hit "<<PrintHit(tjs.fHits[iht])<<" associated with an obsolete cluster tjs.tcl[icl].ID "<<tjs.tcl[icl].ID;
1721  return false;
1722  }
1723  if (std::find(tjs.tcl[icl].tclhits.begin(), tjs.tcl[icl].tclhits.end(), iht) == tjs.tcl[icl].tclhits.end()) {
1724  mf::LogError("TC")<<"CHCA: Hit "<<":"<<PrintHit(tjs.fHits[iht])<<" -> tjs.inClus "<<tjs.inClus[iht]<<" but isn't in tjs.tcl[icl].ID "<<tjs.tcl[icl].ID<<" list of hits. icl "<<icl<<" iht "<<iht;
1725  for(unsigned short itj = 0; itj < tjs.allTraj.size(); ++itj) {
1726  if(tjs.allTraj[itj].ClusterIndex == icl) mf::LogError("TC")<<"CHCA: Cluster index "<<icl<<" found in traj ID "<<tjs.allTraj[itj].ID;
1727  } // itj
1728  PrintAllTraj("CHCA", tjs, debug, USHRT_MAX, USHRT_MAX);
1729  return false;
1730  }
1731  } // iht
1732 
1733  return true;
1734 
1735  } // CheckHitClusterAssociations()
1736 */
1738  unsigned short NumPtsWithCharge(const TjStuff& tjs, const Trajectory& tj, bool includeDeadWires)
1739  {
1740  unsigned short firstPt = tj.EndPt[0];
1741  unsigned short lastPt = tj.EndPt[1];
1742  return NumPtsWithCharge(tjs, tj, includeDeadWires, firstPt, lastPt);
1743  }
1744 
1746  unsigned short NumPtsWithCharge(const TjStuff& tjs, const Trajectory& tj, bool includeDeadWires, unsigned short firstPt, unsigned short lastPt)
1747  {
1748  unsigned short ntp = 0;
1749  for(unsigned short ipt = firstPt; ipt <= lastPt; ++ipt) if(tj.Pts[ipt].Chg > 0) ++ntp;
1750  // Add the count of deadwires
1751  if(includeDeadWires) ntp += DeadWireCount(tjs, tj.Pts[firstPt], tj.Pts[lastPt]);
1752  return ntp;
1753  } // NumPtsWithCharge
1754 
1756  float DeadWireCount(const TjStuff& tjs, const TrajPoint& tp1, const TrajPoint& tp2)
1757  {
1758  return DeadWireCount(tjs, tp1.Pos[0], tp2.Pos[0], tp1.CTP);
1759  } // DeadWireCount
1760 
1762  float DeadWireCount(const TjStuff& tjs, const float& inWirePos1, const float& inWirePos2, CTP_t tCTP)
1763  {
1764  if(inWirePos1 < -0.4 || inWirePos2 < -0.4) return 0;
1765  unsigned int inWire1 = std::nearbyint(inWirePos1);
1766  unsigned int inWire2 = std::nearbyint(inWirePos2);
1767  geo::PlaneID planeID = DecodeCTP(tCTP);
1768  unsigned short plane = planeID.Plane;
1769  if(inWire1 > tjs.NumWires[plane] || inWire2 > tjs.NumWires[plane]) return 0;
1770  if(inWire1 > inWire2) {
1771  // put in increasing order
1772  unsigned int tmp = inWire1;
1773  inWire1 = inWire2;
1774  inWire2 = tmp;
1775  } // inWire1 > inWire2
1776  ++inWire2;
1777  unsigned int wire, ndead = 0;
1778  for(wire = inWire1; wire < inWire2; ++wire) if(tjs.WireHitRange[plane][wire].first == -1) ++ndead;
1779  return ndead;
1780  } // DeadWireCount
1781 
1783  unsigned short PDGCodeIndex(TjStuff& tjs, int PDGCode)
1784  {
1785  unsigned short pdg = abs(PDGCode);
1786  if(pdg == 11) return 0; // electron
1787  if(pdg == 13) return 1; // muon
1788  if(pdg == 211) return 2; // pion
1789  if(pdg == 321) return 3; // kaon
1790  if(pdg == 2212) return 4; // proton
1791 
1792  return USHRT_MAX;
1793 
1794  } // PDGCodeIndex
1795 
1797  void MakeTrajectoryObsolete(TjStuff& tjs, unsigned int itj)
1798  {
1799  // Note that this does not change the state of UseHit to allow
1800  // resurrecting the trajectory later (RestoreObsoleteTrajectory)
1801  if(itj > tjs.allTraj.size() - 1) return;
1802  int killTjID = tjs.allTraj[itj].ID;
1803  for(auto& hit : tjs.fHits) if(hit.InTraj == killTjID) hit.InTraj = 0;
1804  tjs.allTraj[itj].AlgMod[kKilled] = true;
1805  } // MakeTrajectoryObsolete
1806 
1808  void RestoreObsoleteTrajectory(TjStuff& tjs, unsigned int itj)
1809  {
1810  if(itj > tjs.allTraj.size() - 1) return;
1811  if(!tjs.allTraj[itj].AlgMod[kKilled]) {
1812  mf::LogWarning("TC")<<"RestoreObsoleteTrajectory: Trying to restore not-obsolete trajectory "<<tjs.allTraj[itj].ID;
1813  return;
1814  }
1815  unsigned int iht;
1816  for(auto& tp : tjs.allTraj[itj].Pts) {
1817  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1818  if(tp.UseHit[ii]) {
1819  iht = tp.Hits[ii];
1820  if(tjs.fHits[iht].InTraj == 0) {
1821  tjs.fHits[iht].InTraj = tjs.allTraj[itj].ID;
1822  }
1823  }
1824  } // ii
1825  } // tp
1826  tjs.allTraj[itj].AlgMod[kKilled] = false;
1827  } // RestoreObsoleteTrajectory
1828 
1830  void MergeGhostTjs(TjStuff& tjs, CTP_t inCTP)
1831  {
1832  // Merges short Tjs that share many hits with a longer Tj
1833  if(!tjs.UseAlg[kMrgGhost]) return;
1834 
1835  for(auto& shortTj : tjs.allTraj) {
1836  if(shortTj.AlgMod[kKilled]) continue;
1837  if(shortTj.CTP != inCTP) continue;
1838  unsigned short spts = shortTj.EndPt[1] - shortTj.EndPt[0];
1839  if(spts > 20) continue;
1840  // ignore delta rays
1841  if(shortTj.PDGCode == 11) continue;
1842  // ignore InShower Tjs
1843  if(shortTj.SSID > 0) continue;
1844  auto tjhits = PutTrajHitsInVector(shortTj, kAllHits);
1845  if(tjhits.empty()) continue;
1846  std::vector<int> tids;
1847  std::vector<unsigned short> tcnt;
1848  for(auto iht : tjhits) {
1849  auto& hit = tjs.fHits[iht];
1850  if(hit.InTraj <= 0) continue;
1851  if((unsigned int)hit.InTraj > tjs.allTraj.size()) continue;
1852  if(hit.InTraj == shortTj.ID) continue;
1853  unsigned short indx = 0;
1854  for(indx = 0; indx < tids.size(); ++indx) if(hit.InTraj == tids[indx]) break;
1855  if(indx == tids.size()) {
1856  tids.push_back(hit.InTraj);
1857  tcnt.push_back(1);
1858  } else {
1859  ++tcnt[indx];
1860  }
1861  } // iht
1862  if(tids.empty()) continue;
1863  // find the max count for Tjs that are longer than this one
1864  unsigned short maxcnt = 0;
1865  for(unsigned short indx = 0; indx < tids.size(); ++indx) {
1866  if(tcnt[indx] > maxcnt) {
1867  auto& ltj = tjs.allTraj[tids[indx] - 1];
1868  unsigned short lpts = ltj.EndPt[1] - ltj.EndPt[0];
1869  if(lpts < spts) continue;
1870  maxcnt = tcnt[indx];
1871  }
1872  } // indx
1873  float hitFrac = (float)maxcnt / (float)tjhits.size();
1874  if(hitFrac < 0.1) continue;
1875  } // shortTj
1876  } // MergeGhostTjs
1877 
1879  bool SplitTraj(TjStuff& tjs, unsigned short itj, float XPos, bool makeVx2, bool prt)
1880  {
1881  // Splits the trajectory at an X position and optionally creates a 2D vertex at the split point
1882  if(itj > tjs.allTraj.size()-1) return false;
1883 
1884  auto& tj = tjs.allTraj[itj];
1885  geo::PlaneID planeID = DecodeCTP(tj.CTP);
1886  float atPos1 = tjs.detprop->ConvertXToTicks(XPos, planeID) * tjs.UnitsPerTick;
1887  unsigned short atPt = USHRT_MAX;
1888  for(unsigned short ipt = tj.EndPt[0] + 1; ipt <= tj.EndPt[1]; ++ipt) {
1889  if(tj.Pts[ipt].Pos[1] > tj.Pts[ipt - 1].Pos[1]) {
1890  // positive slope
1891  if(tj.Pts[ipt - 1].Pos[1] < atPos1 && tj.Pts[ipt].Pos[1] >= atPos1) {
1892  atPt = ipt;
1893  break;
1894  }
1895  } else {
1896  // negative slope
1897  if(tj.Pts[ipt - 1].Pos[1] >= atPos1 && tj.Pts[ipt].Pos[1] < atPos1) {
1898  atPt = ipt;
1899  break;
1900  }
1901  } // negative slope
1902  } // ipt
1903  if(atPt == USHRT_MAX) return false;
1904  unsigned short vx2Index = USHRT_MAX;
1905  if(makeVx2) {
1906  VtxStore newVx2;
1907  newVx2.CTP = tj.CTP;
1908  newVx2.Pos[0] = 0.5 * (tj.Pts[atPt - 1].Pos[0] + tj.Pts[atPt].Pos[0]);
1909  newVx2.Pos[1] = 0.5 * (tj.Pts[atPt - 1].Pos[1] + tj.Pts[atPt].Pos[1]);
1910  newVx2.Topo = 10;
1911  newVx2.NTraj = 2;
1912  if(StoreVertex(tjs, newVx2)) vx2Index = tjs.vtx.size() - 1;
1913  } // makeVx2
1914  return SplitTraj(tjs, itj, atPt, vx2Index, prt);
1915  } // SplitTraj
1916 
1918  bool SplitTraj(TjStuff& tjs, unsigned short itj, unsigned short pos, unsigned short ivx, bool prt)
1919  {
1920  // Splits the trajectory itj in the tjs.allTraj vector into two trajectories at position pos. Splits
1921  // the trajectory and associates the ends to the supplied vertex.
1922  // Here is an example where itj has 9 points and we will split at pos = 4
1923  // itj (0 1 2 3 4 5 6 7 8) -> new traj (0 1 2 3) + new traj (4 5 6 7 8)
1924 
1925  if(itj > tjs.allTraj.size()-1) return false;
1926  if(pos < tjs.allTraj[itj].EndPt[0] + 1 || pos > tjs.allTraj[itj].EndPt[1] - 1) return false;
1927  if(ivx != USHRT_MAX && ivx > tjs.vtx.size() - 1) return false;
1928 
1929  Trajectory& tj = tjs.allTraj[itj];
1930 
1931  // Reset the PDG Code if we are splitting a tagged muon
1932  bool splittingMuon = (tj.PDGCode == 13);
1933  if(splittingMuon) tj.PDGCode = 0;
1934 
1935  if(prt) {
1936  mf::LogVerbatim myprt("TC");
1937  myprt<<"SplitTraj: Split T"<<tj.ID<<" at point "<<pos;
1938  if(ivx < tjs.vtx.size()) myprt<<" with Vtx 2V"<<tjs.vtx[ivx].ID;
1939  }
1940 
1941  // ensure that there will be at least 3 TPs on each trajectory
1942  unsigned short ipt, ii, ntp = 0;
1943  for(ipt = 0; ipt < pos; ++ipt) {
1944  if(tj.Pts[ipt].Chg > 0) ++ntp;
1945  if(ntp > 2) break;
1946  } // ipt
1947  if(ntp < 3) {
1948  if(prt) mf::LogVerbatim("TC")<<" Split point to small at begin "<<ntp<<" pos "<<pos<<" ID ";
1949  return false;
1950  }
1951  ntp = 0;
1952  for(ipt = pos + 1; ipt < tj.Pts.size(); ++ipt) {
1953  if(tj.Pts[ipt].Chg > 0) ++ntp;
1954  if(ntp > 2) break;
1955  } // ipt
1956  if(ntp < 3) {
1957  if(prt) mf::LogVerbatim("TC")<<" Split point too small at end "<<ntp<<" pos "<<pos<<" EndPt "<<tj.EndPt[1];
1958  return false;
1959  }
1960 
1961  // make a copy that will become the Tj after the split point
1962  Trajectory newTj = tj;
1963  newTj.ID = tjs.allTraj.size() + 1;
1964  // make another copy in case something goes wrong
1965  Trajectory oldTj = tj;
1966 
1967  // Leave the first section of tj in place. Re-assign the hits
1968  // to the new trajectory
1969  unsigned int iht;
1970  for(ipt = pos + 1; ipt < tj.Pts.size(); ++ipt) {
1971  tj.Pts[ipt].Chg = 0;
1972  for(ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
1973  if(!tj.Pts[ipt].UseHit[ii]) continue;
1974  iht = tj.Pts[ipt].Hits[ii];
1975  // This shouldn't happen but check anyway
1976  if(tjs.fHits[iht].InTraj != tj.ID) continue;
1977  tjs.fHits[iht].InTraj = newTj.ID;
1978  tj.Pts[ipt].UseHit[ii] = false;
1979  } // ii
1980  } // ipt
1981  SetEndPoints(tjs, tj);
1982  UpdateTjChgProperties("ST", tjs, tj, prt);
1983  if(splittingMuon) SetPDGCode(tjs, tj);
1984 
1985  // Append 3 points from the end of tj onto the
1986  // beginning of newTj so that hits can be swapped between
1987  // them later
1988  unsigned short eraseSize = pos - 2;
1989  if(eraseSize > newTj.Pts.size() - 1) {
1990  tj = oldTj;
1991  return false;
1992  }
1993 
1994  if(ivx < tjs.vtx.size()) tj.VtxID[1] = tjs.vtx[ivx].ID;
1995  tj.AlgMod[kSplit] = true;
1996  if(prt) {
1997  mf::LogVerbatim("TC")<<" Splitting T"<<tj.ID<<" new EndPts "<<tj.EndPt[0]<<" to "<<tj.EndPt[1];
1998  }
1999 
2000  // erase the TPs at the beginning of the new trajectory
2001  newTj.Pts.erase(newTj.Pts.begin(), newTj.Pts.begin() + eraseSize);
2002  // unset the first 3 TP hits
2003  for(ipt = 0; ipt < 3; ++ipt) {
2004  for(ii = 0; ii < newTj.Pts[ipt].Hits.size(); ++ii) newTj.Pts[ipt].UseHit[ii] = false;
2005  newTj.Pts[ipt].Chg = 0;
2006  } // ipt
2007  SetEndPoints(tjs, newTj);
2008  UpdateTjChgProperties("ST", tjs, newTj, prt);
2009  if(splittingMuon) SetPDGCode(tjs, newTj);
2010  if(ivx < tjs.vtx.size()) newTj.VtxID[0] = tjs.vtx[ivx].ID;
2011  newTj.AlgMod[kSplit] = true;
2012  newTj.ParentID = 0;
2013  // save the ID before push_back in case the tj reference gets lost
2014  int tjid = tj.ID;
2015  tjs.allTraj.push_back(newTj);
2016  UpdateMatchStructs(tjs, tjid, newTj.ID);
2017 
2018  if(prt) {
2019  mf::LogVerbatim("TC")<<" newTj T"<<newTj.ID<<" EndPts "<<newTj.EndPt[0]<<" to "<<newTj.EndPt[1];
2020  }
2021  return true;
2022 
2023  } // SplitTraj
2024 
2026  void TrajPointTrajDOCA(TjStuff& tjs, TrajPoint const& tp, Trajectory const& tj, unsigned short& closePt, float& minSep)
2027  {
2028  // Finds the point, ipt, on trajectory tj that is closest to trajpoint tp
2029  float best = minSep * minSep;
2030  closePt = USHRT_MAX;
2031  float dw, dt, dp2;
2032  unsigned short ipt;
2033  for(ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
2034  dw = tj.Pts[ipt].Pos[0] - tp.Pos[0];
2035  dt = tj.Pts[ipt].Pos[1] - tp.Pos[1];
2036  dp2 = dw * dw + dt * dt;
2037  if(dp2 < best) {
2038  best = dp2;
2039  closePt = ipt;
2040  }
2041  } // ipt
2042  minSep = sqrt(best);
2043  } // TrajPointTrajDOCA
2044 
2046  bool TrajTrajDOCA(const TjStuff& tjs, const Trajectory& tj1, const Trajectory& tj2, unsigned short& ipt1, unsigned short& ipt2, float& minSep)
2047  {
2048  return TrajTrajDOCA(tjs, tj1, tj2, ipt1, ipt2, minSep, false);
2049  } // TrajTrajDOCA
2050 
2052  bool TrajTrajDOCA(const TjStuff& tjs, const Trajectory& tj1, const Trajectory& tj2, unsigned short& ipt1, unsigned short& ipt2, float& minSep, bool considerDeadWires)
2053  {
2054  // Find the Distance Of Closest Approach between two trajectories less than minSep
2055  // start with some rough cuts to minimize the use of the more expensive checking. This
2056  // function returns true if the DOCA is less than minSep
2057  for(unsigned short iwt = 0; iwt < 2; ++iwt) {
2058  // Apply box cuts on the ends of the trajectories
2059  // The Lo/Hi wire(time) at each end of tj1
2060  float wt0 = tj1.Pts[tj1.EndPt[0]].Pos[iwt];
2061  float wt1 = tj1.Pts[tj1.EndPt[1]].Pos[iwt];
2062  float lowt1 = wt0;
2063  float hiwt1 = wt1;
2064  if(wt1 < lowt1) {
2065  lowt1 = wt1;
2066  hiwt1 = wt0;
2067  }
2068  // The Lo/Hi wire(time) at each end of tj2
2069  wt0 = tj2.Pts[tj2.EndPt[0]].Pos[iwt];
2070  wt1 = tj2.Pts[tj2.EndPt[1]].Pos[iwt];
2071  float lowt2 = wt0;
2072  float hiwt2 = wt1;
2073  if(wt1 < lowt2) {
2074  lowt2 = wt1;
2075  hiwt2 = wt0;
2076  }
2077  // Check for this configuration
2078  // loWire1.......hiWire1 minSep loWire2....hiWire2
2079  // loTime1.......hiTime1 minSep loTime2....hiTime2
2080  if(lowt2 > hiwt1 + minSep) return false;
2081  // and the other
2082  if(lowt1 > hiwt2 + minSep) return false;
2083  } // iwt
2084 
2085  float best = minSep * minSep;
2086  ipt1 = 0; ipt2 = 0;
2087  float dwc = 0;
2088  bool isClose = false;
2089  for(unsigned short i1 = tj1.EndPt[0]; i1 < tj1.EndPt[1] + 1; ++i1) {
2090  for(unsigned short i2 = tj2.EndPt[0]; i2 < tj2.EndPt[1] + 1; ++i2) {
2091  if(considerDeadWires) dwc = DeadWireCount(tjs, tj1.Pts[i1], tj2.Pts[i2]);
2092  float dw = tj1.Pts[i1].Pos[0] - tj2.Pts[i2].Pos[0] - dwc;
2093  if(std::abs(dw) > minSep) continue;
2094  float dt = tj1.Pts[i1].Pos[1] - tj2.Pts[i2].Pos[1];
2095  if(std::abs(dt) > minSep) continue;
2096  float dp2 = dw * dw + dt * dt;
2097  if(dp2 < best) {
2098  best = dp2;
2099  ipt1 = i1;
2100  ipt2 = i2;
2101  isClose = true;
2102  }
2103  } // i2
2104  } // i1
2105  minSep = sqrt(best);
2106  return isClose;
2107  } // TrajTrajDOCA
2108 
2110  float HitSep2(TjStuff& tjs, unsigned int iht, unsigned int jht)
2111  {
2112  // returns the separation^2 between two hits in WSE units
2113  if(iht > tjs.fHits.size()-1 || jht > tjs.fHits.size()-1) return 1E6;
2114  float dw = (float)tjs.fHits[iht].ArtPtr->WireID().Wire - (float)tjs.fHits[jht].ArtPtr->WireID().Wire;
2115  float dt = (tjs.fHits[iht].PeakTime - tjs.fHits[jht].PeakTime) * tjs.UnitsPerTick;
2116  return dw * dw + dt * dt;
2117  } // HitSep2
2118 
2120  unsigned short CloseEnd(TjStuff& tjs, const Trajectory& tj, const Point2_t& pos)
2121  {
2122  unsigned short endPt = tj.EndPt[0];
2123  auto& tp0 = tj.Pts[endPt];
2124  endPt = tj.EndPt[1];
2125  auto& tp1 = tj.Pts[endPt];
2126  if(PosSep2(tp0.Pos, pos) < PosSep2(tp1.Pos, pos)) return 0;
2127  return 1;
2128  } // CloseEnd
2129 
2131  float PointTrajSep2(float wire, float time, TrajPoint const& tp)
2132  {
2133  float dw = wire - tp.Pos[0];
2134  float dt = time - tp.Pos[1];
2135  return dw * dw + dt * dt;
2136  }
2137 
2139  float PointTrajDOCA(TjStuff const& tjs, unsigned int iht, TrajPoint const& tp)
2140  {
2141  float wire = tjs.fHits[iht].ArtPtr->WireID().Wire;
2142  float time = tjs.fHits[iht].PeakTime * tjs.UnitsPerTick;
2143  return sqrt(PointTrajDOCA2(tjs, wire, time, tp));
2144  } // PointTrajDOCA
2145 
2147  float PointTrajDOCA(TjStuff const& tjs, float wire, float time, TrajPoint const& tp)
2148  {
2149  return sqrt(PointTrajDOCA2(tjs, wire, time, tp));
2150  } // PointTrajDOCA
2151 
2153  float PointTrajDOCA2(TjStuff const& tjs, float wire, float time, TrajPoint const& tp)
2154  {
2155  // returns the distance of closest approach squared between a (wire, time(WSE)) point
2156  // and a trajectory point
2157 
2158  float t = (wire - tp.Pos[0]) * tp.Dir[0] + (time - tp.Pos[1]) * tp.Dir[1];
2159  float dw = tp.Pos[0] + t * tp.Dir[0] - wire;
2160  float dt = tp.Pos[1] + t * tp.Dir[1] - time;
2161  return (dw * dw + dt * dt);
2162 
2163  } // PointTrajDOCA2
2164 
2166  void TrajIntersection(TrajPoint const& tp1, TrajPoint const& tp2, Point2_t& pos)
2167  {
2168  TrajIntersection(tp1, tp2, pos[0], pos[1]);
2169  } // TrajIntersection
2171  void TrajIntersection(TrajPoint const& tp1, TrajPoint const& tp2, float& x, float& y)
2172  {
2173  // returns the intersection position, (x,y), of two trajectory points
2174 
2175  x = -9999; y = -9999;
2176 
2177  double arg1 = tp1.Pos[0] * tp1.Dir[1] - tp1.Pos[1] * tp1.Dir[0];
2178  double arg2 = tp2.Pos[0] * tp1.Dir[1] - tp2.Pos[1] * tp1.Dir[0];
2179  double arg3 = tp2.Dir[0] * tp1.Dir[1] - tp2.Dir[1] * tp1.Dir[0];
2180  if(arg3 == 0) return;
2181  double s = (arg1 - arg2) / arg3;
2182 
2183  x = (float)(tp2.Pos[0] + s * tp2.Dir[0]);
2184  y = (float)(tp2.Pos[1] + s * tp2.Dir[1]);
2185 
2186  } // TrajIntersection
2187 
2189  float MaxTjLen(TjStuff const& tjs, std::vector<int>& tjIDs)
2190  {
2191  // returns the length of the longest Tj in the supplied list
2192  if(tjIDs.empty()) return 0;
2193  float maxLen = 0;
2194  for(auto tjid : tjIDs) {
2195  if(tjid < 1 || tjid > (int)tjs.allTraj.size()) continue;
2196  auto& tj = tjs.allTraj[tjid - 1];
2197  float sep2 = PosSep2(tj.Pts[tj.EndPt[0]].Pos, tj.Pts[tj.EndPt[1]].Pos);
2198  if(sep2 > maxLen) maxLen = sep2;
2199  } // tj
2200  return sqrt(maxLen);
2201  } // MaxTjLen
2202 
2205  {
2206  float len = 0, dx, dy;
2207  unsigned short ipt;
2208  unsigned short prevPt = tj.EndPt[0];
2209  for(ipt = tj.EndPt[0] + 1; ipt < tj.EndPt[1] + 1; ++ipt) {
2210  if(tj.Pts[ipt].Chg == 0) continue;
2211  dx = tj.Pts[ipt].Pos[0] - tj.Pts[prevPt].Pos[0];
2212  dy = tj.Pts[ipt].Pos[1] - tj.Pts[prevPt].Pos[1];
2213  len += sqrt(dx * dx + dy * dy);
2214  prevPt = ipt;
2215  }
2216  return len;
2217  } // TrajLength
2218 
2220  float PosSep(const Point2_t& pos1, const Point2_t& pos2)
2221  {
2222  return sqrt(PosSep2(pos1, pos2));
2223  } // PosSep
2224 
2226  float PosSep2(const Point2_t& pos1, const Point2_t& pos2)
2227  {
2228  // returns the separation distance^2 between two positions
2229  float d0 = pos1[0] - pos2[0];
2230  float d1 = pos1[1] - pos2[1];
2231  return d0*d0+d1*d1;
2232  } // PosSep2
2233 
2236  {
2237  // Returns the separation distance between two trajectory points
2238  float dx = tp1.Pos[0] - tp2.Pos[0];
2239  float dy = tp1.Pos[1] - tp2.Pos[1];
2240  return sqrt(dx * dx + dy * dy);
2241  } // TrajPointSeparation
2242 
2244  bool TrajClosestApproach(Trajectory const& tj, float x, float y, unsigned short& closePt, float& DOCA)
2245  {
2246  // find the closest approach between a trajectory tj and a point (x,y). Returns
2247  // the index of the closest trajectory point and the distance. Returns false if none
2248  // of the points on the tj are within DOCA
2249 
2250  float close2 = DOCA * DOCA;
2251  closePt = 0;
2252  bool foundClose = false;
2253 
2254  for(unsigned short ipt = tj.EndPt[0]; ipt < tj.EndPt[1] + 1; ++ipt) {
2255  if(tj.Pts[ipt].Chg == 0) continue;
2256  float dx = tj.Pts[ipt].Pos[0] - x;
2257  if(std::abs(dx) > DOCA) continue;
2258  float dy = tj.Pts[ipt].Pos[1] - y;
2259  if(std::abs(dy) > DOCA) continue;
2260  float sep2 = dx * dx + dy * dy;
2261  if(sep2 < close2) {
2262  close2 = sep2;
2263  closePt = ipt;
2264  foundClose = true;
2265  }
2266  } // ipt
2267 
2268  DOCA = sqrt(close2);
2269  return foundClose;
2270 
2271  } // TrajClosestApproach
2272 
2274  float TwoTPAngle(TrajPoint& tp1, TrajPoint& tp2)
2275  {
2276  // Calculates the angle of a line between two TPs
2277  float dw = tp2.Pos[0] - tp1.Pos[0];
2278  float dt = tp2.Pos[1] - tp1.Pos[1];
2279  return atan2(dw, dt);
2280  } // TwoTPAngle
2281 
2283  std::vector<unsigned int> PutTrajHitsInVector(Trajectory const& tj, HitStatus_t hitRequest)
2284  {
2285  // Put hits in each trajectory point into a flat vector
2286  std::vector<unsigned int> hitVec;
2287 
2288  // special handling for shower trajectories. UseHit isn't valid
2289  if(tj.AlgMod[kShowerTj]) {
2290  for(auto& tp : tj.Pts) hitVec.insert(hitVec.end(), tp.Hits.begin(), tp.Hits.end());
2291  return hitVec;
2292  } // shower Tj
2293 
2294  // reserve under the assumption that there will be one hit per point
2295  hitVec.reserve(tj.Pts.size());
2296  for(unsigned short ipt = 0; ipt < tj.Pts.size(); ++ipt) {
2297  for(unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
2298  unsigned int iht = tj.Pts[ipt].Hits[ii];
2299  bool useit = (hitRequest == kAllHits);
2300  if(tj.Pts[ipt].UseHit[ii] && hitRequest == kUsedHits) useit = true;
2301  if(!tj.Pts[ipt].UseHit[ii] && hitRequest == kUnusedHits) useit = true;
2302  if(useit) hitVec.push_back(iht);
2303  } // iht
2304  } // ipt
2305  return hitVec;
2306  } // PutTrajHitsInVector
2307 
2309  void TagJunkTj(TjStuff const& tjs, Trajectory& tj, bool prt)
2310  {
2311  // Characterizes the trajectory as a junk tj even though it may not
2312  // have been reconstructed in FindJunkTraj. The distinguishing feature is
2313  // that it is short and has many used hits in each trajectory point.
2314 
2315  // Don't bother if it is too long
2316  if(tj.Pts.size() > 10) return;
2317  // count the number of points that have many used hits
2318  unsigned short nhm = 0;
2319  unsigned short npwc = 0;
2320  for(auto& tp : tj.Pts) {
2321  if(tp.Chg == 0) continue;
2322  ++npwc;
2323  unsigned short nused = 0;
2324  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
2325  if(tp.UseHit[ii]) ++nused;
2326  } // ii
2327  if(nused > 3) ++nhm;
2328  } // tp
2329  // Set the junkTj bit if most of the hits are used in most of the tps
2330  if(nhm > 0.5 * npwc) tj.AlgMod[kJunkTj] = true;
2331  if(prt) mf::LogVerbatim("TC")<<"TGT: T"<<tj.ID<<" npwc "<<npwc<<" nhm "<<nhm<<" junk? "<<tj.AlgMod[kJunkTj];
2332  } // TagJunkTj
2333 
2335  bool HasDuplicateHits(TjStuff const& tjs, Trajectory const& tj, bool prt)
2336  {
2337  // returns true if a hit is associated with more than one TP
2338  auto tjHits = PutTrajHitsInVector(tj, kAllHits);
2339  for(unsigned short ii = 0; ii < tjHits.size() - 1; ++ii) {
2340  for(unsigned short jj = ii + 1; jj < tjHits.size(); ++jj) {
2341  if(tjHits[ii] == tjHits[jj]) {
2342  if(prt) mf::LogVerbatim("TC")<<"HDH: Hit "<<PrintHit(tjs.fHits[ii])<<" is a duplicate "<<ii<<" "<<jj;
2343  return true;
2344  }
2345  } // jj
2346  } // ii
2347  return false;
2348  } // HasDuplicateHits
2349 
2351  void MoveTPToWire(TrajPoint& tp, float wire)
2352  {
2353  // Project TP to a "wire position" Pos[0] and update Pos[1]
2354  if(tp.Dir[0] == 0) return;
2355  float dw = wire - tp.Pos[0];
2356  if(std::abs(dw) < 0.01) return;
2357  tp.Pos[0] = wire;
2358  tp.Pos[1] += dw * tp.Dir[1] / tp.Dir[0];
2359  } // MoveTPToWire
2360 
2362  std::vector<unsigned int> FindCloseHits(TjStuff const& tjs, std::array<int, 2> const& wireWindow, Point2_t const& timeWindow, const unsigned short plane, HitStatus_t hitRequest, bool usePeakTime, bool& hitsNear)
2363  {
2364  // returns a vector of hits that are within the Window[Pos0][Pos1] in plane.
2365  // Note that hits on wire wireWindow[1] are returned as well. The definition of close
2366  // depends on setting of usePeakTime. If UsePeakTime is true, a hit is considered nearby if
2367  // the PeakTime is within the window. This is shown schematically here where
2368  // the time is on the horizontal axis and a "-" denotes a valid entry
2369  // timeWindow -----------------
2370  // hit PeakTime + close
2371  // hit PeakTime + not close
2372  // If usePeakTime is false, a hit is considered nearby if the hit StartTick and EndTick overlap with the timeWindow
2373  // Time window ---------
2374  // Hit StartTick-EndTick -------- close
2375  // Hit StartTick - EndTick -------- not close
2376 
2377  hitsNear = false;
2378  std::vector<unsigned int> closeHits;
2379  if(plane > tjs.FirstWire.size() - 1) return closeHits;
2380  // window in the wire coordinate
2381  int loWire = wireWindow[0];
2382  if(loWire < (int)tjs.FirstWire[plane]) loWire = tjs.FirstWire[plane];
2383  int hiWire = wireWindow[1];
2384  if(hiWire > (int)tjs.LastWire[plane]-1) hiWire = tjs.LastWire[plane]-1;
2385  // window in the time coordinate
2386  float minTick = timeWindow[0] / tjs.UnitsPerTick;
2387  float maxTick = timeWindow[1] / tjs.UnitsPerTick;
2388  for(int wire = loWire; wire <= hiWire; ++wire) {
2389  // Set hitsNear if the wire is dead
2390  if(tjs.WireHitRange[plane][wire].first == -2) hitsNear = true;
2391  if(tjs.WireHitRange[plane][wire].first < 0) continue;
2392  unsigned int firstHit = (unsigned int)tjs.WireHitRange[plane][wire].first;
2393  unsigned int lastHit = (unsigned int)tjs.WireHitRange[plane][wire].second;
2394  for(unsigned int iht = firstHit; iht < lastHit; ++iht) {
2395  if(tjs.fHits[iht].InTraj == INT_MAX) continue;
2396  if(usePeakTime) {
2397  if(tjs.fHits[iht].PeakTime < minTick) continue;
2398  if(tjs.fHits[iht].PeakTime > maxTick) break;
2399  } else {
2400  int hiLo = minTick;
2401  if(tjs.fHits[iht].StartTick > hiLo) hiLo = tjs.fHits[iht].StartTick;
2402  int loHi = maxTick;
2403  if(tjs.fHits[iht].EndTick < loHi) loHi = tjs.fHits[iht].EndTick;
2404  if(loHi < hiLo) continue;
2405  if(hiLo > loHi) break;
2406  }
2407  hitsNear = true;
2408  bool takeit = (hitRequest == kAllHits);
2409  if(hitRequest == kUsedHits && tjs.fHits[iht].InTraj > 0) takeit = true;
2410  if(hitRequest == kUnusedHits && tjs.fHits[iht].InTraj == 0) takeit = true;
2411  if(takeit) closeHits.push_back(iht);
2412  } // iht
2413  } // wire
2414  return closeHits;
2415  } // FindCloseHits
2416 
2418  bool FindCloseHits(TjStuff const& tjs, TrajPoint& tp, float const& maxDelta, HitStatus_t hitRequest)
2419  {
2420  // Fills tp.Hits sets tp.UseHit true for hits that are close to tp.Pos. Returns true if there are
2421  // close hits OR if the wire at this position is dead
2422 
2423  tp.Hits.clear();
2424  tp.UseHit.reset();
2425  if(!WireHitRangeOK(tjs, tp.CTP)) {
2426 // std::cout<<"FindCloseHits: WireHitRange not valid for CTP "<<tp.CTP<<". tjs.WireHitRange Cstat "<<tjs.TPCID.Cryostat<<" TPC "<<tjs.TPCID.TPC<<"\n";
2427  return false;
2428  }
2429 
2430  geo::PlaneID planeID = DecodeCTP(tp.CTP);
2431  unsigned short ipl = planeID.Plane;
2432  if(tp.Pos[0] < -0.4) return false;
2433  unsigned int wire = std::nearbyint(tp.Pos[0]);
2434  if(wire < tjs.FirstWire[ipl]) return false;
2435  if(wire > tjs.LastWire[ipl]-1) return false;
2436 
2437  // dead wire
2438  if(tjs.WireHitRange[ipl][wire].first == -1) return true;
2439  // live wire with no hits
2440  if(tjs.WireHitRange[ipl][wire].first == -2) return false;
2441 
2442  unsigned int firstHit = (unsigned int)tjs.WireHitRange[ipl][wire].first;
2443  unsigned int lastHit = (unsigned int)tjs.WireHitRange[ipl][wire].second;
2444 
2445  float fwire = wire;
2446  for(unsigned int iht = firstHit; iht < lastHit; ++iht) {
2447  if(tjs.fHits[iht].InTraj == INT_MAX) continue;
2448  bool useit = (hitRequest == kAllHits);
2449  if(hitRequest == kUsedHits && tjs.fHits[iht].InTraj > 0) useit = true;
2450  if(hitRequest == kUnusedHits && tjs.fHits[iht].InTraj == 0) useit = true;
2451  if(!useit) continue;
2452  float ftime = tjs.UnitsPerTick * tjs.fHits[iht].PeakTime;
2453  float delta = PointTrajDOCA(tjs, fwire, ftime, tp);
2454  if(delta < maxDelta) tp.Hits.push_back(iht);
2455  } // iht
2456  if(tp.Hits.size() > 16) {
2457  tp.Hits.resize(16);
2458  }
2459  // Set UseHit false. The calling routine should decide if these hits should be used
2460  tp.UseHit.reset();
2461  return true;
2462 
2463  } // FindCloseHits
2464 
2466  std::vector<int> FindCloseTjs(const TjStuff& tjs, const TrajPoint& fromTp, const TrajPoint& toTp, const float& maxDelta)
2467  {
2468  // Returns a list of Tj IDs that have hits within distance maxDelta on a line drawn between the two Tps as shown
2469  // graphically here, where a "*" is a Tp and "|" and "-" are the boundaries of the region that is checked
2470  //
2471  // ---------------
2472  // | |
2473  // * *
2474  // | |
2475  // ---------------
2476  // If the wire positions of fromTp and toTp are the same, a different region is checked as shown here
2477  //
2478  // -----------
2479  // | |
2480  // | * |
2481  // | |
2482  // -----------
2483 
2484  std::vector<int> tmp;
2485  if(fromTp.Pos[0] < -0.4 || toTp.Pos[0] < -0.4) return tmp;
2486 
2487  TrajPoint tp;
2488  // Make the tp so that stepping is positive
2489  unsigned int firstWire, lastWire;
2490  if(toTp.Pos[0] > fromTp.Pos[0]) {
2491  if(!MakeBareTrajPoint(tjs, fromTp, toTp, tp)) return tmp;
2492  firstWire = std::nearbyint(fromTp.Pos[0]);
2493  lastWire = std::nearbyint(toTp.Pos[0]);
2494  } else if(toTp.Pos[0] < fromTp.Pos[0]) {
2495  if(!MakeBareTrajPoint(tjs, toTp, fromTp, tp)) return tmp;
2496  firstWire = std::nearbyint(toTp.Pos[0]);
2497  lastWire = std::nearbyint(fromTp.Pos[0]);
2498  } else {
2499  tp.Pos = fromTp.Pos;
2500  float tmp = fromTp.Pos[0] - maxDelta;
2501  if(tmp < 0) tmp = 0;
2502  firstWire = std::nearbyint(tmp);
2503  tmp = fromTp.Pos[0] + maxDelta;
2504  lastWire = std::nearbyint(tmp);
2505  }
2506 
2507  geo::PlaneID planeID = DecodeCTP(tp.CTP);
2508  unsigned short ipl = planeID.Plane;
2509 
2510  if(firstWire < tjs.FirstWire[ipl]) firstWire = tjs.FirstWire[ipl];
2511  if(firstWire > tjs.LastWire[ipl]-1) return tmp;
2512  if(lastWire < tjs.FirstWire[ipl]) return tmp;
2513  if(lastWire > tjs.LastWire[ipl]-1) lastWire = tjs.LastWire[ipl]-1;
2514 
2515  for(unsigned int wire = firstWire; wire <= lastWire; ++wire) {
2516  if(tjs.WireHitRange[ipl][wire].first == -1) continue;
2517  if(tjs.WireHitRange[ipl][wire].first == -2) continue;
2518  MoveTPToWire(tp, (float)wire);
2519  // Find the tick range at this position
2520  float minTick = (tp.Pos[1] - maxDelta) / tjs.UnitsPerTick;
2521  float maxTick = (tp.Pos[1] + maxDelta) / tjs.UnitsPerTick;
2522  unsigned int firstHit = (unsigned int)tjs.WireHitRange[ipl][wire].first;
2523  unsigned int lastHit = (unsigned int)tjs.WireHitRange[ipl][wire].second;
2524  for(unsigned int iht = firstHit; iht < lastHit; ++iht) {
2525  if(tjs.fHits[iht].InTraj <= 0) continue;
2526  if((unsigned int)tjs.fHits[iht].InTraj > tjs.allTraj.size()) continue;
2527  if(tjs.fHits[iht].PeakTime < minTick) continue;
2528  // Hits are sorted by increasing time so we can break when maxTick is reached
2529  if(tjs.fHits[iht].PeakTime > maxTick) break;
2530  if(std::find(tmp.begin(), tmp.end(), tjs.fHits[iht].InTraj) != tmp.end()) continue;
2531  tmp.push_back(tjs.fHits[iht].InTraj);
2532  } // iht
2533  } // wire
2534 
2535  return tmp;
2536 
2537  } // FindCloseTjs
2538 /* this doesn't do anything but print out at this point. Needs further study
2540  void PrimaryElectronLikelihood(TjStuff& tjs, Trajectory& tj, float& likelihood, bool& flipDirection, bool prt)
2541  {
2542  // returns the likelihood that the tj is a primary electron.
2543  likelihood = 0;
2544  flipDirection = false;
2545  // Too short to consider?
2546  unsigned short npts = tj.EndPt[1] - tj.EndPt[0] + 1;
2547  if(npts < 10) return;
2548  if(npts > 150) return;
2549  unsigned short nPtsInSection = npts / 2;
2550  if(npts > 30) nPtsInSection = npts / 3;
2551  // Define a range of points at the beginning and the end
2552  unsigned short endPt0 = tj.EndPt[0] + nPtsInSection;
2553  float mom0 = MCSMom(tjs, tj, tj.EndPt[0], endPt0);
2554 // std::vector<unsigned int> FindCloseHits(TjStuff const& tjs, std::array<int, 2> const& wireWindow, Point2_t const& timeWindow, const unsigned short plane, HitStatus_t hitRequest, bool usePeakTime, bool& hitsNear)
2555  float tChg0 = 0;
2556  float oChg0 = 0;
2557  std::array<int, 2> wireWindow;
2558  Point2_t timeWindow;
2559  unsigned short plane = DecodeCTP(tj.CTP).Plane;
2560  bool hitsNear;
2561  for(unsigned short ipt = tj.EndPt[0]; ipt <= endPt0; ++ipt) {
2562  auto& tp = tj.Pts[ipt];
2563  wireWindow[0] = std::nearbyint(tp.Pos[0]);
2564  wireWindow[1] = wireWindow[0];
2565  timeWindow[0] = tp.Pos[1] - 5;
2566  timeWindow[1] = tp.Pos[1] + 5;
2567  auto closeHits = FindCloseHits(tjs, wireWindow, timeWindow, plane, kAllHits, true, hitsNear);
2568  for(auto iht : closeHits) {
2569  auto& hit = tjs.fHits[iht];
2570  if(hit.InTraj == tj.ID) {
2571  tChg0 += hit.Integral;
2572  } else {
2573  oChg0 += hit.Integral;
2574  }
2575  } // iht
2576  } // ipt
2577  if(tChg0 == 0) return;
2578  float chgFrac0 = tChg0 / (oChg0 + tChg0);
2579 
2580  unsigned short startPt1 = tj.EndPt[1] - nPtsInSection;
2581  float mom1 = MCSMom(tjs, tj, startPt1, tj.EndPt[1]);
2582  float tChg1 = 0;
2583  float oChg1 = 0;
2584  for(unsigned short ipt = startPt1; ipt <= tj.EndPt[1]; ++ipt) {
2585  auto& tp = tj.Pts[ipt];
2586  wireWindow[0] = std::nearbyint(tp.Pos[0]);
2587  wireWindow[1] = wireWindow[0];
2588  timeWindow[0] = tp.Pos[1] - 5;
2589  timeWindow[1] = tp.Pos[1] + 5;
2590  auto closeHits = FindCloseHits(tjs, wireWindow, timeWindow, plane, kAllHits, true, hitsNear);
2591  for(auto iht : closeHits) {
2592  auto& hit = tjs.fHits[iht];
2593  if(hit.InTraj == tj.ID) {
2594  tChg1 += hit.Integral;
2595  } else {
2596  oChg1 += hit.Integral;
2597  }
2598  } // iht
2599  } // ipt
2600  if(tChg1 == 0) return;
2601  float chgFrac1 = tChg1 / (oChg1 + tChg1);
2602 
2603  if(prt) {
2604  std::cout<<"PEL: T"<<tj.ID;
2605  std::cout<<" sec0 "<<" "<<PrintPos(tjs, tj.Pts[tj.EndPt[0]])<<"-"<<PrintPos(tjs, tj.Pts[startPt1]);
2606  std::cout<<" mom "<<(int)mom0<<" oChg0 "<<(int)oChg0<<" chgFrac0 "<<std::fixed<<std::setprecision(2)<<chgFrac0;
2607  std::cout<<" sec1 "<<PrintPos(tjs, tj.Pts[startPt1])<<"-"<<PrintPos(tjs, tj.Pts[tj.EndPt[1]]);
2608  std::cout<<" mom "<<(int)mom1<<" oChg1 "<<(int)oChg1<<" chgFrac1 "<<std::fixed<<std::setprecision(2)<<chgFrac1<<"\n";
2609  } // prt
2610 
2611  } // PrimaryElectronLikelihood
2612 */
2614  float ChgFracNearPos(TjStuff& tjs, const Point2_t& pos, const std::vector<int>& tjIDs)
2615  {
2616  // returns the fraction of the charge in the region around pos that is associated with
2617  // the list of Tj IDs
2618  if(tjIDs.empty()) return 0;
2619  std::array<int, 2> wireWindow;
2620  Point2_t timeWindow;
2621  // 1/2 size of the region
2622  constexpr float NNDelta = 5;
2623  wireWindow[0] = pos[0] - NNDelta;
2624  wireWindow[1] = pos[0] + NNDelta;
2625  timeWindow[0] = pos[1] - NNDelta;
2626  timeWindow[1] = pos[1] + NNDelta;
2627  // do some checking
2628  for(auto& tjID : tjIDs) if(tjID <= 0 || tjID > (int)tjs.allTraj.size()) return 0;
2629  // Determine which plane we are in
2630  geo::PlaneID planeID = DecodeCTP(tjs.allTraj[tjIDs[0]-1].CTP);
2631  // get a list of all hits in this region
2632  bool hitsNear;
2633  std::vector<unsigned int> closeHits = FindCloseHits(tjs, wireWindow, timeWindow, planeID.Plane, kAllHits, true, hitsNear);
2634  if(closeHits.empty()) return 0;
2635  float chg = 0;
2636  float tchg = 0;
2637  // Add the hit charge in the box
2638  // All hits in the box, and all hits associated with the Tjs
2639  for(auto& iht : closeHits) {
2640  chg += tjs.fHits[iht].Integral;
2641  if(tjs.fHits[iht].InTraj == 0) continue;
2642  if(std::find(tjIDs.begin(), tjIDs.end(), tjs.fHits[iht].InTraj) != tjIDs.end()) tchg += tjs.fHits[iht].Integral;
2643  } // iht
2644  if(chg == 0) return 0;
2645  return tchg / chg;
2646  } // ChgFracNearPos
2647 
2649  float MaxHitDelta(TjStuff& tjs, Trajectory& tj)
2650  {
2651  float delta, md = 0;
2652  unsigned short ii;
2653  unsigned int iht;
2654  for(auto& tp : tj.Pts) {
2655  for(ii = 0; ii < tp.Hits.size(); ++ii) {
2656  if(!tp.UseHit[ii]) continue;
2657  iht = tp.Hits[ii];
2658  delta = PointTrajDOCA(tjs, iht, tp);
2659  if(delta > md) md = delta;
2660  } // ii
2661  } // pts
2662  return md;
2663  } // MaxHitDelta
2664 
2667  {
2668  // reverse the trajectory
2669  if(tj.Pts.empty()) return;
2670 /*
2671  if(tj.AlgMod[kMat3D]) {
2672  std::cout<<"Trying to reverse 3D matched Tj "<<tj.ID<<". Need to modify other Tjs and the MatchStruct\n";
2673  return;
2674  }
2675  if(tj.AlgMod[kSetDir]) {
2676  std::cout<<"Trying to reverse Tj "<<tj.ID<<" whose direction has been set. Not doing it.\n";
2677  return;
2678  }
2679 */
2680  // reverse the crawling direction flag
2681  tj.StepDir = -tj.StepDir;
2682  tj.DirFOM = 1 - tj.DirFOM;
2683  // Vertices
2684  std::swap(tj.VtxID[0], tj.VtxID[1]);
2685  // trajectory points
2686  std::reverse(tj.Pts.begin(), tj.Pts.end());
2687  // reverse the stop flag
2688  std::reverse(tj.StopFlag.begin(), tj.StopFlag.end());
2689  std::swap(tj.dEdx[0], tj.dEdx[1]);
2690  // reverse the direction vector on all points
2691  for(unsigned short ipt = 0; ipt < tj.Pts.size(); ++ipt) {
2692  if(tj.Pts[ipt].Dir[0] != 0) tj.Pts[ipt].Dir[0] = -tj.Pts[ipt].Dir[0];
2693  if(tj.Pts[ipt].Dir[1] != 0) tj.Pts[ipt].Dir[1] = -tj.Pts[ipt].Dir[1];
2694  if(tj.Pts[ipt].Ang > 0) {
2695  tj.Pts[ipt].Ang -= M_PI;
2696  } else {
2697  tj.Pts[ipt].Ang += M_PI;
2698  }
2699  } // ipt
2700  SetEndPoints(tjs, tj);
2701  UpdateMatchStructs(tjs, tj.ID, tj.ID);
2702  } // ReverseTraj
2703 
2705  bool PointInsideEnvelope(const Point2_t& Point, const std::vector<Point2_t>& Envelope)
2706  {
2707  // returns true if the Point is within the Envelope polygon. Entries in Envelope are the
2708  // Pos[0], Pos[1] locations of the polygon vertices. This is based on the algorithm that the
2709  // sum of the angles of a vector between a point and the vertices will be 2 * pi for an interior
2710  // point and 0 for an exterior point
2711 
2712  Point2_t p1, p2;
2713  unsigned short nvx = Envelope.size();
2714  double angleSum = 0;
2715  for(unsigned short ii = 0; ii < Envelope.size(); ++ii) {
2716  p1[0] = Envelope[ii][0] - Point[0];
2717  p1[1] = Envelope[ii][1] - Point[1];
2718  p2[0] = Envelope[(ii+1)%nvx][0] - Point[0];
2719  p2[1] = Envelope[(ii+1)%nvx][1] - Point[1];
2720  angleSum += DeltaAngle(p1, p2);
2721  }
2722  if(abs(angleSum) < M_PI) return false;
2723  return true;
2724 
2725  } // InsideEnvelope
2726 
2727 
2729  bool SetMag(Vector2_t& v1, double mag)
2730  {
2731  double den = v1[0] * v1[0] + v1[1] * v1[1];
2732  if(den == 0) return false;
2733  den = sqrt(den);
2734 
2735  v1[0] *= mag / den;
2736  v1[1] *= mag / den;
2737  return true;
2738  } // SetMag
2739 
2741  void FindAlongTrans(Point2_t pos1, Vector2_t dir1, Point2_t pos2, Point2_t& alongTrans)
2742  {
2743  // Calculate the distance along and transverse to the direction vector dir1 from pos1 to pos2
2744  alongTrans[0] = 0;
2745  alongTrans[1] = 0;
2746  if(pos1[0] == pos2[0] && pos1[1] == pos2[1]) return;
2747  pos1[0] = pos2[0] - pos1[0];
2748  pos1[1] = pos2[1] - pos1[1];
2749  double sep = sqrt(pos1[0] * pos1[0] + pos1[1] * pos1[1]);
2750  if(sep < 1E-6) return;
2751  Vector2_t ptDir;
2752  ptDir[0] = pos1[0] / sep;
2753  ptDir[1] = pos1[1] / sep;
2754  SetMag(dir1, 1.0);
2755  double costh = DotProd(dir1, ptDir);
2756  if(costh > 1.0 || costh < -1.0) return;
2757  alongTrans[0] = costh * sep;
2758  double sinth = sqrt(1 - costh * costh);
2759  alongTrans[1] = sinth * sep;
2760  } // FindAlongTrans
2761 
2763  double DeltaAngle(const Point2_t& p1, const Point2_t& p2)
2764  {
2765  // angle between two points
2766  double ang1 = atan2(p1[1], p1[0]);
2767  double ang2 = atan2(p2[1], p2[0]);
2768  return DeltaAngle2(ang1, ang2);
2769  } // DeltaAngle
2770 
2772  double DeltaAngle2(double Ang1, double Ang2)
2773  {
2774  constexpr double twopi = 2 * M_PI;
2775  double dang = Ang1 - Ang2;
2776  while(dang > M_PI) dang -= twopi;
2777  while(dang < -M_PI) dang += twopi;
2778  return dang;
2779  }
2780 
2782  double DeltaAngle(double Ang1, double Ang2)
2783  {
2784  return std::abs(std::remainder(Ang1 - Ang2, M_PI));
2785  }
2786 
2789  {
2790  // Find the first (last) TPs, EndPt[0] (EndPt[1], that have charge
2791 
2792  // don't mess with showerTjs
2793  if(tj.AlgMod[kShowerTj]) return;
2794  tj.EndPt[0] = 0; tj.EndPt[1] = 0;
2795  if(tj.Pts.size() == 0) return;
2796 
2797  // check the end point pointers
2798  for(unsigned short ipt = 0; ipt < tj.Pts.size(); ++ipt) {
2799  if(tj.Pts[ipt].Chg != 0) {
2800  tj.EndPt[0] = ipt;
2801  break;
2802  }
2803  }
2804  for(unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
2805  unsigned short ipt = tj.Pts.size() - 1 - ii;
2806  if(tj.Pts[ipt].Chg != 0) {
2807  tj.EndPt[1] = ipt;
2808  break;
2809  }
2810  }
2811  } // SetEndPoints
2812 
2814  bool TrajIsClean(TjStuff& tjs, Trajectory& tj, bool prt)
2815  {
2816  // Returns true if the trajectory has low hit multiplicity and is in a
2817  // clean environment
2818  unsigned short nUsed = 0;
2819  unsigned short nTotHits = 0;
2820  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
2821  TrajPoint& tp = tj.Pts[ipt];
2822  nTotHits += tp.Hits.size();
2823  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
2824  if(tp.UseHit[ii]) ++nUsed;
2825  } // ii
2826  } // ipt
2827  if(nTotHits == 0) return false;
2828  float fracUsed = (float)nUsed / (float)nTotHits;
2829  if(prt) mf::LogVerbatim("TC")<<"TrajIsClean: nTotHits "<<nTotHits<<" nUsed "<<nUsed<<" fracUsed "<<fracUsed;
2830 
2831  if(fracUsed > 0.9) return true;
2832  return false;
2833 
2834  } // TrajIsClean
2835 
2837  short MCSMom(const TjStuff& tjs, const std::vector<int>& tjIDs)
2838  {
2839  // Find the average MCSMom of the trajectories
2840  if(tjIDs.empty()) return 0;
2841  float summ = 0;
2842  float suml = 0;
2843  for(auto tjid : tjIDs) {
2844  auto& tj = tjs.allTraj[tjid - 1];
2845  float npts = tj.EndPt[1] - tj.EndPt[0] + 1;
2846  summ += npts * tj.MCSMom;
2847  suml += npts;
2848  } // tjid
2849  return (short)(summ / suml);
2850  } // MCSMom
2851 
2853  short MCSMom(TjStuff& tjs, Trajectory& tj)
2854  {
2855  return MCSMom(tjs, tj, tj.EndPt[0], tj.EndPt[1]);
2856  } // MCSMom
2857 
2859  short MCSMom(TjStuff& tjs, Trajectory& tj, unsigned short firstPt, unsigned short lastPt)
2860  {
2861  // Estimate the trajectory momentum using Multiple Coulomb Scattering ala PDG RPP
2862 
2863  if(firstPt == lastPt) return 0;
2864  if(firstPt > lastPt) std::swap(firstPt, lastPt);
2865 
2866  firstPt = NearestPtWithChg(tjs, tj, firstPt);
2867  lastPt = NearestPtWithChg(tjs, tj, lastPt);
2868  if(firstPt >= lastPt) return 0;
2869 
2870  if(firstPt < tj.EndPt[0]) return 0;
2871  if(lastPt > tj.EndPt[1]) return 0;
2872  // Can't do this with only 2 points
2873  if(NumPtsWithCharge(tjs, tj, false, firstPt, lastPt) < 3) return 0;
2874  // Ignore junk Tjs
2875  if(tj.AlgMod[kJunkTj]) return 0;
2876 
2877  double tjLen = TrajPointSeparation(tj.Pts[firstPt], tj.Pts[lastPt]);
2878  if(tjLen < 1) return 0;
2879  // mom calculated in MeV
2880  double thetaRMS = MCSThetaRMS(tjs, tj, firstPt, lastPt);
2881  if(thetaRMS < 0.001) return 999;
2882  double mom = 13.8 * sqrt(tjLen / 14) / thetaRMS;
2883  if(mom > 999) mom = 999;
2884  return (short)mom;
2885  } // MCSMom
2886 
2887 
2889  unsigned short NearestPtWithChg(TjStuff& tjs, Trajectory& tj, unsigned short thePt)
2890  {
2891  // returns a point near thePt which has charge
2892  if(thePt > tj.EndPt[1]) return thePt;
2893  if(tj.Pts[thePt].Chg > 0) return thePt;
2894 
2895  short endPt0 = tj.EndPt[0];
2896  short endPt1 = tj.EndPt[1];
2897  for(short off = 1; off < 10; ++off) {
2898  short ipt = thePt + off;
2899  if(ipt <= endPt1 && tj.Pts[ipt].Chg > 0) return (unsigned short)ipt;
2900  ipt = thePt - off;
2901  if(ipt >= endPt0 && tj.Pts[ipt].Chg > 0) return (unsigned short)ipt;
2902  } // off
2903  return thePt;
2904  } // NearestPtWithChg
2905 
2907  float MCSThetaRMS(TjStuff& tjs, Trajectory& tj)
2908  {
2909  // This returns the MCS scattering angle expected for one WSE unit of travel along the trajectory.
2910  // It is used to define kink and vertex cuts. This should probably be named something different to
2911  // prevent confusion
2912 
2913  float tps = TrajPointSeparation(tj.Pts[tj.EndPt[0]], tj.Pts[tj.EndPt[1]]);
2914  if(tps < 1) return 1;
2915 
2916  return MCSThetaRMS(tjs, tj, tj.EndPt[0], tj.EndPt[1]) / sqrt(tps);
2917 
2918  } // MCSThetaRMS
2919 
2921  double MCSThetaRMS(TjStuff& tjs, Trajectory& tj, unsigned short firstPt, unsigned short lastPt)
2922  {
2923  // This returns the MCS scattering angle expected for the length of the trajectory
2924  // spanned by firstPt to lastPt. It is used primarily to calculate MCSMom
2925 
2926  if(firstPt < tj.EndPt[0]) return 1;
2927  if(lastPt > tj.EndPt[1]) return 1;
2928 
2929  firstPt = NearestPtWithChg(tjs, tj, firstPt);
2930  lastPt = NearestPtWithChg(tjs, tj, lastPt);
2931  if(firstPt >= lastPt) return 1;
2932 
2933  double sigmaS;
2934  unsigned short cnt;
2935  TjDeltaRMS(tjs, tj, firstPt, lastPt, sigmaS, cnt);
2936  if(sigmaS < 0) return 1;
2937  // require that cnt is a significant fraction of the total number of charged points
2938  // so that we don't get erroneously high MCSMom when there are large gaps.
2939  // This is the number of points expected in the count if there are no gaps
2940  unsigned short numPts = lastPt - firstPt - 1;
2941  // return the previously calculated value of MCSMom
2942  if(numPts > 5 && cnt < 0.7 * numPts) return tj.MCSMom;
2943  double tjLen = TrajPointSeparation(tj.Pts[firstPt], tj.Pts[lastPt]);
2944  if(tjLen < 1) return 1;
2945  // Theta_o = 4 * sqrt(3) * sigmaS / path
2946  return (6.8 * sigmaS / tjLen);
2947 
2948  } // MCSThetaRMS
2949 
2951  void TjDeltaRMS(TjStuff& tjs, Trajectory& tj, unsigned short firstPt, unsigned short lastPt, double& rms, unsigned short& cnt)
2952  {
2953  // returns the rms scatter of points around a line formed by the firstPt and lastPt of the trajectory
2954 
2955  rms = -1;
2956  if(firstPt < tj.EndPt[0]) return;
2957  if(lastPt > tj.EndPt[1]) return;
2958 
2959  firstPt = NearestPtWithChg(tjs, tj, firstPt);
2960  lastPt = NearestPtWithChg(tjs, tj, lastPt);
2961  if(firstPt >= lastPt) return;
2962 
2963  TrajPoint tmp;
2964  // make a bare trajectory point to define a line between firstPt and lastPt.
2965  // Use the position of the hits at these points
2966  TrajPoint firstTP = tj.Pts[firstPt];
2967  firstTP.Pos = firstTP.HitPos;
2968  TrajPoint lastTP = tj.Pts[lastPt];
2969  lastTP.Pos = lastTP.HitPos;
2970  if(!MakeBareTrajPoint(tjs, firstTP, lastTP, tmp)) return;
2971  // sum up the deviations^2
2972  double dsum = 0;
2973  cnt = 0;
2974  for(unsigned short ipt = firstPt + 1; ipt < lastPt; ++ipt) {
2975  if(tj.Pts[ipt].Chg == 0) continue;
2976  dsum += PointTrajDOCA2(tjs, tj.Pts[ipt].HitPos[0], tj.Pts[ipt].HitPos[1], tmp);
2977  ++cnt;
2978  } // ipt
2979  if(cnt < 2) return;
2980  rms = sqrt(dsum / (double)cnt);
2981 
2982  } // TjDeltaRMS
2983 
2985  void TagDeltaRays(TjStuff& tjs, const CTP_t& inCTP)
2986  {
2987  // DeltaRayTag vector elements
2988  // [0] = max separation of both endpoints from a muon
2989  // [1] = minimum MCSMom
2990  // [2] = maximum MCSMom
2991 
2992  if(!tjs.UseAlg[kDeltaRay]) return;
2993  if(tjs.DeltaRayTag[0] < 0) return;
2994  if(tjs.DeltaRayTag.size() < 3) return;
2995 
2996  bool prt = (debug.CTP == inCTP) && (debug.Tick == 31313);
2997 
2998  // double the user-defined separation cut. We will require that at least one of the ends of
2999  // a delta ray be within the user-defined cut and allow
3000  float maxSep = 2 * tjs.DeltaRayTag[0];
3001  float maxMinSep = tjs.DeltaRayTag[0];
3002  unsigned short minMom = tjs.DeltaRayTag[1];
3003  unsigned short maxMom = tjs.DeltaRayTag[2];
3004  unsigned short minMuonLength = 2 * tjs.Vertex2DCuts[2];
3005  unsigned short minpts = 4;
3006  if(prt) mf::LogVerbatim("TC")<<"TagDeltaRays: maxSep "<<maxSep<<" maxMinSep "<<maxMinSep<<" Mom range "<<minMom<<" to "<<maxMom<<" minpts "<<minpts;
3007 
3008  for(unsigned short itj = 0; itj < tjs.allTraj.size(); ++itj) {
3009  Trajectory& muTj = tjs.allTraj[itj];
3010  if(muTj.CTP != inCTP) continue;
3011  if(muTj.AlgMod[kKilled]) continue;
3012  if(muTj.PDGCode != 13) continue;
3013  if(prt) mf::LogVerbatim("TC")<<"TagDeltaRays: Muon T"<<muTj.ID<<" EndPts "<<PrintPos(tjs, muTj.Pts[muTj.EndPt[0]])<<"-"<<PrintPos(tjs, muTj.Pts[muTj.EndPt[1]]);
3014  // min length
3015  if(muTj.EndPt[1] - muTj.EndPt[0] < minMuonLength) continue;
3016  auto& mtp0 = muTj.Pts[muTj.EndPt[0]];
3017  auto& mtp1 = muTj.Pts[muTj.EndPt[1]];
3018  // Found a muon, now look for delta rays
3019  for(unsigned short jtj = 0; jtj < tjs.allTraj.size(); ++jtj) {
3020  if(jtj == itj) continue;
3021  Trajectory& dtj = tjs.allTraj[jtj];
3022  if(dtj.AlgMod[kKilled]) continue;
3023  if(dtj.CTP != inCTP) continue;
3024  if(dtj.PDGCode == 13) continue;
3025  // MCSMom cut
3026  if(dtj.MCSMom < minMom) continue;
3027  if(dtj.MCSMom > maxMom) continue;
3028  if(dtj.EndPt[1] - dtj.EndPt[0] < minpts) continue;
3029  // some rough cuts to require that the delta ray is within the
3030  // ends of the muon
3031  auto& dtp0 = dtj.Pts[dtj.EndPt[0]];
3032  auto& dtp1 = dtj.Pts[dtj.EndPt[1]];
3033  if(muTj.StepDir > 0) {
3034  if(dtp0.Pos[0] < mtp0.Pos[0]) continue;
3035  if(dtp1.Pos[0] > mtp1.Pos[0]) continue;
3036  } else {
3037  if(dtp0.Pos[0] > mtp0.Pos[0]) continue;
3038  if(dtp1.Pos[0] < mtp1.Pos[0]) continue;
3039  }
3040  // find the minimum separation
3041  float doca = maxMinSep;
3042  unsigned short mpt = 0;
3043  unsigned short dpt = 0;
3044  TrajTrajDOCA(tjs, muTj, dtj, mpt, dpt, doca, false);
3045  if(doca == maxMinSep) continue;
3046  auto& dTp = dtj.Pts[dpt];
3047  // cut on the distance from the muon ends
3048  if(PosSep(dTp.Pos, mtp0.Pos) < tjs.Vertex2DCuts[2]) continue;
3049  if(PosSep(dTp.Pos, mtp1.Pos) < tjs.Vertex2DCuts[2]) continue;
3050  // make an angle cut at this point. A delta-ray should have a small angle
3051  float dang = DeltaAngle(muTj.Pts[mpt].Ang, dtj.Pts[dpt].Ang);
3052  if(prt) mf::LogVerbatim("TC")<<" dRay? T"<<dtj.ID<<" at "<<PrintPos(tjs, dtj.Pts[dpt].Pos)<<" dang "<<dang<<" doca "<<doca;
3053  // ignore the angle cut if the separation is small and the delta ray MCSMom is low
3054  bool closeDeltaRay = (doca < 2 && dtj.MCSMom < 20);
3055  if(!closeDeltaRay && dang > tjs.KinkCuts[0]) continue;
3056  // Feb 7, 2018
3057 // if(dang > tjs.KinkCuts[0]) continue;
3058  unsigned short oend = 0;
3059  // check the delta at the end of the delta-ray that is farthest away from the
3060  // closest point
3061  if(dpt > 0.5 * (dtj.EndPt[0] + dtj.EndPt[1])) oend = 1;
3062  auto& farEndTP = dtj.Pts[dtj.EndPt[oend]];
3063  float farEndDelta = PointTrajDOCA(tjs, farEndTP.Pos[0], farEndTP.Pos[1], muTj.Pts[mpt]);
3064  if(prt) mf::LogVerbatim("TC")<<" farEnd "<<PrintPos(tjs, farEndTP.Pos)<<" farEndDelta "<<farEndDelta;
3065  if(farEndDelta > maxSep) continue;
3066  if(prt) mf::LogVerbatim("TC")<<" delta ray "<<dtj.ID<<" parent -> "<<muTj.ID;
3067  dtj.ParentID = muTj.ID;
3068  dtj.PDGCode = 11;
3069  dtj.AlgMod[kDeltaRay] = true;
3070  // Set the start of the delta-ray to be end 0
3071  if(oend != 1) ReverseTraj(tjs, dtj);
3072  } // jtj
3073  } // itj
3074 
3075  } // TagDeltaRays
3076 
3078  void TagMuonDirections(TjStuff& tjs, short debugWorkID)
3079  {
3080  // Determine muon directions delta-ray proximity to muon trajectories
3081 
3082  if(tjs.MuonTag[0] < 0) return;
3083 
3084  unsigned short minLen = tjs.MuonTag[3];
3085 
3086  for(unsigned short itj = 0; itj < tjs.allTraj.size(); ++itj) {
3087  Trajectory& muTj = tjs.allTraj[itj];
3088  if(muTj.AlgMod[kKilled]) continue;
3089  bool prt = (debugWorkID < 0 && muTj.WorkID == debugWorkID);
3090  if(prt) {
3091  mf::LogVerbatim("TC")<<"TagMuonDirection: Muon "<<muTj.CTP<<" "<<PrintPos(tjs, muTj.Pts[muTj.EndPt[0]])<<"-"<<PrintPos(tjs, muTj.Pts[muTj.EndPt[1]]);
3092  }
3093  if(muTj.PDGCode != 13) continue;
3094  // look for delta ray trajectories and count the number of times that
3095  unsigned short nPos = 0;
3096  unsigned short nNeg = 0;
3097  for(auto& dtj : tjs.allTraj) {
3098  if(dtj.AlgMod[kKilled]) continue;
3099  if(dtj.ParentID != muTj.ID) continue;
3100  if(dtj.EndPt[1] - dtj.EndPt[0] > minLen) continue;
3101  if(dtj.StepDir > 0) {
3102  ++nPos;
3103  } else {
3104  ++nNeg;
3105  }
3106  } // dtj
3107  if(nPos == nNeg) continue;
3108  if(nPos > nNeg) {
3109  if(muTj.StepDir < 0) ReverseTraj(tjs, muTj);
3110  } else {
3111  if(muTj.StepDir > 0) ReverseTraj(tjs, muTj);
3112  }
3113 // muTj.AlgMod[kSetDir] = true;
3114  } // itj
3115  } // TagMuonDirections
3116 
3118  void UpdateTjChgProperties(std::string inFcnLabel, TjStuff& tjs, Trajectory& tj, bool prt)
3119  {
3120  // Updates properties of the tj that are affected when the TP environment
3121  // is changed. The most likely reason for a change is when the tj is attached to a
3122  // vertex in which case the Environment kEnvOverlap bit may be set by the UpdateVxEnvironment
3123  // function in which case this function is called.
3124  // The kEnvNearShower bit may be set by TagShowerTjs but this doesn't affect the
3125  // calculation of the properties of this Tj. This function simply sets the kEnvUnusedHits bit
3126  // for all TPs.
3127  if(tj.AlgMod[kKilled]) return;
3128 
3129  std::string fcnLabel = inFcnLabel + ".UpTjProp";
3130 
3131  // first (un)set some bits
3132  for(auto& tp : tj.Pts) {
3133  if(tp.Chg <= 0) continue;
3134  tp.Environment[kEnvUnusedHits] = false;
3135  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
3136  if(tp.UseHit[ii]) continue;
3137  unsigned int iht = tp.Hits[ii];
3138  if(tjs.fHits[iht].InTraj == 0) {
3139  tp.Environment[kEnvUnusedHits] = true;
3140  } else {
3141  tp.Environment[kEnvNearTj] = true;
3142  }
3143  } // ii
3144  } // tp
3145 
3146  // Update the tj charge variables. The concept is explained by this graphic where
3147  // each column is a wire, Q = a TP with charge, q = a TP with charge that is an
3148  // EnvOverlap region, x = a wire that has a TP with Chg = 0 or a wire that has no TP
3149  // because the wire is dead, o = an EnvOverlap region, V = vertex attached to end. You should
3150  // imagine that all 3 tjs come from the same vertex
3151  // 01234567890123456789 npwc cnt range
3152  // VooooQQQQxxxQQQ 7 7 0 - 14
3153  // VqqqqQQQQxxxQQQQQQQQ 16 12 0 - 19
3154  // VooQQQ 3 3 0 - 5
3155  // The average is first calculated using Ave = sum(Q) / npwc
3156  // TotChg is calculated using
3157  tj.TotChg = 0;
3158  tj.AveChg = 0;
3159  tj.ChgRMS = 0.5;
3160 
3161  // These variables are used to calculate the average and rms using valid points with charge
3162  double vcnt = 0;
3163  double vsum = 0;
3164  double vsum2 = 0;
3165  // variables for calculating the backup quanties. These are only used if npwc < 3
3166  double bcnt = 0;
3167  double bsum = 0;
3168  double bsum2 = 0;
3169  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
3170  auto& tp = tj.Pts[ipt];
3171  if(tp.Chg <= 0) continue;
3172  // accumulate a backup sum in case most of the points are overlapped. Note that
3173  // tp.Chg has an angle correction, which is why the hit integral is summed
3174  // below. We don't care about this detail for the backup sum
3175  bsum += tp.Chg;
3176  bsum2 += tp.Chg * tp.Chg;
3177  ++bcnt;
3178  // Skip TPs that overlap with TPs on other Tjs. A correction will be made below
3179  if(tj.Pts[ipt].Environment[kEnvOverlap]) continue;
3180  ++vcnt;
3181  double tpchg = 0;
3182  for(unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
3183  if(!tp.UseHit[ii]) continue;
3184  unsigned int iht = tp.Hits[ii];
3185  tpchg += tjs.fHits[iht].Integral;
3186  } // ii
3187  vsum += tpchg;
3188  vsum2 += tpchg * tpchg;
3189  } // ipt
3190 
3191  if(bcnt == 0) return;
3192 
3193  if(vcnt < 3) {
3194  // use the backup sum
3195  tj.TotChg = bsum;
3196  tj.AveChg = bsum / bcnt;
3197  if(vcnt > 2) {
3198  double arg = bsum2 - bcnt * tj.AveChg * tj.AveChg;
3199  if(arg > 0) tj.ChgRMS = sqrt(arg / (bcnt - 1));
3200  }
3201  for(auto& tp : tj.Pts) tp.AveChg = tj.AveChg;
3202  return;
3203  } // low npwc
3204 
3205  double nWires = tj.EndPt[1] - tj.EndPt[0] + 1;
3206  if(nWires < 2) return;
3207  // correct for wires missing near vertices.
3208  // Count the number of wires between vertices at the ends and the first wire
3209  // that has charge. This code assumes that there should be one TP on each wire
3210  if(!tj.AlgMod[kPhoton]) {
3211  for(unsigned short end = 0; end < 2; ++end) {
3212  if(tj.VtxID[end] == 0) continue;
3213  auto& tp = tj.Pts[tj.EndPt[end]];
3214  auto& vx2 = tjs.vtx[tj.VtxID[end] - 1];
3215  int dw = std::abs(tp.Pos[0] - vx2.Pos[0]);
3216  // This assumes that the vertex is not inside the wire boundaries of the tj
3217  nWires += dw;
3218  } // end
3219  } // not a photon Tj
3220 
3221  tj.AveChg = vsum / vcnt;
3222  // calculate the total charge using the tj wire range
3223  tj.TotChg = nWires * tj.AveChg;
3224  // calculate the rms
3225  double arg = vsum2 - vcnt * tj.AveChg * tj.AveChg;
3226  double rms = 0.5;
3227  if(arg > 0) rms = sqrt(arg / (vcnt - 1));
3228  rms /= tj.AveChg;
3229  // don't let it be an unrealistically low value. It could be crazy large however.
3230  if(rms < 0.1) rms = 0.1;
3231  // Don't let the calculated charge RMS dominate until it is well known; after there are 5 - 10 valid TPs.
3232  // Set the starting charge rms = 0.5
3233  if(vcnt < 10) {
3234  double defFrac = 1 / vcnt;
3235  rms = defFrac * 0.5 + (1 - defFrac) * rms;
3236  }
3237  tj.ChgRMS = rms;
3238 
3239  // Update the TP charge pulls.
3240  // Don't let the calculated charge RMS dominate the default
3241  // RMS until it is well known. Start with 50% error on the
3242  // charge RMS
3243  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
3244  auto& tp = tj.Pts[ipt];
3245  if(tp.Chg <= 0) continue;
3246  tp.ChgPull = (tp.Chg / tj.AveChg - 1) / tj.ChgRMS;
3247  } // ipt
3248 
3249  // update the local charge average using NPtsAve of the preceding points.
3250  // Handle short Tjs first.
3251  if(vcnt < tjs.NPtsAve) {
3252  for(auto& tp : tj.Pts) tp.AveChg = tj.AveChg;
3253  return;
3254  }
3255 
3256  // Set the local average to 0 first
3257  for(auto& tp : tj.Pts) tp.AveChg = 0;
3258  // Enter the local average on the points where an average can be calculated
3259  unsigned short nptsave = tjs.NPtsAve;
3260  unsigned short minPt = tj.EndPt[0] + nptsave;
3261  float lastAve = 0;
3262  for(unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
3263  unsigned short ipt = tj.EndPt[1] - ii;
3264  if(ipt < minPt) break;
3265  float cnt = 0;
3266  float sum = 0;
3267  for(unsigned short iii = 0; iii < nptsave; ++iii) {
3268  unsigned short iipt = ipt - iii;
3269  // Don't include the charge of the first point
3270  if(iipt == tj.EndPt[0]) break;
3271  auto& tp = tj.Pts[iipt];
3272  if(tp.Chg <= 0) continue;
3273  sum += tp.Chg;
3274  ++cnt;
3275  } // iii
3276  if(cnt > 2) {
3277  tj.Pts[ipt].AveChg = sum / cnt;
3278  lastAve = tj.Pts[ipt].AveChg;
3279  }
3280  } // ii
3281  // Fill in the points where no average was calculated
3282  for(unsigned short ii = tj.EndPt[0]; ii <= tj.EndPt[1]; ++ii) {
3283  unsigned short ipt = tj.EndPt[1] - ii;
3284  auto& tp = tj.Pts[ipt];
3285  if(tp.AveChg == 0) {
3286  tp.AveChg = lastAve;
3287  } else {
3288  lastAve = tp.AveChg;
3289  }
3290  } // ii
3291 
3292  tj.NeedsUpdate = false;
3293 
3294  } // UpdateTjChgProperties
3295 
3297  void UpdateVxEnvironment(std::string inFcnLabel, TjStuff& tjs, VtxStore& vx2, bool prt)
3298  {
3299  // Update the Environment each TP on trajectories near the vertex. This is called when
3300  // the Tj has been added to a vertex to identify nearby trajectories that contribute to
3301  // the charge on TPs near the vertex.
3302  if(vx2.ID == 0) return;
3303  if(vx2.Stat[kOnDeadWire]) return;
3304 
3305  std::string fcnLabel = inFcnLabel + ".UpVxProp";
3306 
3307  if(prt) mf::LogVerbatim("TC")<<fcnLabel<<" UpdateTjEnvironment check Tjs attached to vx2 "<<vx2.ID;
3308 
3309  std::vector<int> tjlist;
3310  std::vector<unsigned short> tjends;
3311  if(vx2.Pos[0] < -0.4) return;
3312  unsigned int vxWire = std::nearbyint(vx2.Pos[0]);
3313  unsigned int loWire = vxWire;
3314  unsigned int hiWire = vxWire;
3315  for(auto& tj : tjs.allTraj) {
3316  if(tj.AlgMod[kKilled]) continue;
3317  if(tj.CTP != vx2.CTP) continue;
3318  // ignore photon Tjs
3319  if(tj.AlgMod[kPhoton]) continue;
3320  for(unsigned short end = 0; end < 2; ++end) {
3321  if(tj.VtxID[end] != vx2.ID) continue;
3322  tjlist.push_back(tj.ID);
3323  tjends.push_back(end);
3324  if(tj.Pts[tj.EndPt[end]].Pos[0] < -0.4) return;
3325  unsigned int endWire = std::nearbyint(tj.Pts[tj.EndPt[end]].Pos[0]);
3326  if(endWire < loWire) loWire = endWire;
3327  if(endWire > hiWire) hiWire = endWire;
3328  } // end
3329  } // tj
3330  if(tjlist.size() < 2) return;
3331  if(hiWire < loWire + 1) return;
3332  if(prt) mf::LogVerbatim("TC")<<fcnLabel<<" check Tjs on wires in the range "<<loWire<<" to "<<hiWire;
3333 
3334  // create a vector of TPs between loWire and hiWire for every tj in the list
3335  // wire TP
3336  std::vector<std::vector<TrajPoint>> wire_tjpt;
3337  // companion vector of IDs
3338  std::vector<int> tjids;
3339  // populate this vector with TPs on Tjs that are in this range
3340  unsigned short nwires = hiWire - loWire + 1;
3341  for(unsigned short itj = 0; itj < tjlist.size(); ++itj) {
3342  auto& tj = tjs.allTraj[tjlist[itj] - 1];
3343  unsigned short end = tjends[itj];
3344  std::vector<TrajPoint> tjpt(nwires);
3345  // first enter valid TPs in the range
3346  for(unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
3347  unsigned short ipt;
3348  if(end == 0) { ipt = tj.EndPt[0] + ii; } else { ipt = tj.EndPt[1] - ii; }
3349  if(ipt > tj.Pts.size() - 1) break;
3350  // Make a copy of the TP so we can alter it
3351  auto tp = tj.Pts[ipt];
3352  if(tp.Chg <= 0) continue;
3353  tp.Chg = 1;
3354  tp.Hits.clear();
3355  if(tp.Pos[0] < -0.4) continue;
3356  unsigned int wire = std::nearbyint(tp.Pos[0]);
3357  unsigned short indx = wire - loWire;
3358  if(indx > nwires - 1) break;
3359  tp.Step = ipt;
3360  // We will use NTPsFit to count the number of neighboring TPs
3361  tp.NTPsFit = 0;
3362  tjpt[indx] = tp;
3363  } // ii
3364  // next make TPs on the wires that don't have real TPs
3365  TrajPoint ltp;
3366  // put ltp at the vertex position with direction towards the end point
3367  MakeBareTrajPoint(vx2.Pos, tj.Pts[tj.EndPt[end]].Pos, ltp);
3368  if(ltp.Dir[0] == 0) continue;
3369  if(ltp.Pos[0] < -0.4) continue;
3370  unsigned int wire = std::nearbyint(ltp.Pos[0]);
3371  ltp.Chg = 0;
3372  unsigned short indx = wire - loWire;
3373  // Break if we found a real TP
3374  if(tjpt[indx].Chg == 0) tjpt[indx] = ltp;
3375  double stepSize = std::abs(1/ltp.Dir[0]);
3376  for(unsigned short ii = 0; ii < nwires; ++ii) {
3377  // move the local TP position by one step in the right direction
3378  for(unsigned short iwt = 0; iwt < 2; ++iwt) ltp.Pos[iwt] += ltp.Dir[iwt] * stepSize;
3379  if(ltp.Pos[0] < -0.4) break;
3380  wire = std::nearbyint(ltp.Pos[0]);
3381  if(wire < loWire || wire > hiWire) break;
3382  indx = wire - loWire;
3383  if(tjpt[indx].Chg > 0) continue;
3384  tjpt[indx]= ltp;
3385  } // ii
3386  if(prt) {
3387  mf::LogVerbatim myprt("TC");
3388  myprt<<fcnLabel<<" T"<<tj.ID;
3389  for(auto& tp : tjpt) myprt<<" "<<PrintPos(tjs, tp.Pos)<<"_"<<tp.Step<<"_"<<(int)tp.Chg;
3390  }
3391  wire_tjpt.push_back(tjpt);
3392  tjids.push_back(tj.ID);
3393  } // itj
3394 
3395  // iterate over the wires in the range
3396  for(unsigned short indx = 0; indx < nwires; ++indx) {
3397  // count the number of valid points on this wire
3398  unsigned short npts = 0;
3399  // count the number of points on this wire that have charge
3400  unsigned short npwc = 0;
3401  for(unsigned short itj = 0; itj < wire_tjpt.size(); ++itj) {
3402  if(wire_tjpt[itj][indx].Pos[0] == 0) continue;
3403  // found a valid point
3404  ++npts;
3405  if(wire_tjpt[itj][indx].Chg > 0) ++npwc;
3406  } // itj
3407  // no valid points
3408 // if(prt) mf::LogVerbatim("TC")<<" wire "<<loWire + indx<<" npts "<<npts<<" npwc "<<npwc;
3409  if(npts == 0) continue;
3410  // all valid points have charge
3411  if(npwc == npts) continue;
3412  // re-find the valid points with charge and set the kEnvOverlap bit
3413  for(unsigned short itj = 0; itj < wire_tjpt.size(); ++itj) {
3414  if(wire_tjpt[itj][indx].Pos[0] == 0) continue;
3415  if(wire_tjpt[itj][indx].Chg == 0) continue;
3416  auto& tj = tjs.allTraj[tjids[itj] - 1];
3417  unsigned short ipt = wire_tjpt[itj][indx].Step;
3418  tj.Pts[ipt].Environment[kEnvOverlap] = true;
3419  tj.NeedsUpdate = true;
3420  if(prt) mf::LogVerbatim("TC")<<fcnLabel<<" Set kEnvOverlap bit on T"<<tj.ID<<" ipt "<<ipt;
3421  } // itj
3422  } // indx
3423 
3424  // update the charge rms for those tjs whose environment was changed above (or elsewhere)
3425  for(auto tjid : tjids) {
3426  auto& tj = tjs.allTraj[tjid - 1];
3427  if(!tj.NeedsUpdate) continue;
3428  if(tj.CTP != vx2.CTP) continue;
3429  UpdateTjChgProperties(fcnLabel, tjs, tj, prt);
3430  } // tjid
3431 
3432  } // UpdateVxEnvironment
3433 
3436  {
3437  // Projects the space point defined by pos and dir into the CTP and returns it in the form of a trajectory point.
3438  // The TP Pos[0] is set to a negative number if the point has an invalid wire position but doesn't return an
3439  // error if the position is on a dead wire. The projection of the direction vector in CTP is stored in tp.Delta.
3440  TrajPoint tp;
3441  tp.Pos = {{0,0}};
3442  tp.Dir = {{0,1}};
3443  tp.CTP = inCTP;
3444  geo::PlaneID planeID = DecodeCTP(inCTP);
3445 
3446  tp.Pos[0] = tjs.geom->WireCoordinate(pos[1], pos[2], planeID);
3447  if(tp.Pos[0] < 0 || (!tjs.MaxPos0.empty() && tp.Pos[0] > tjs.MaxPos0[planeID.Plane])) {
3448  tp.Pos[0] = -1;
3449  return tp;
3450  }
3451  tp.Pos[1] = tjs.detprop->ConvertXToTicks(pos[0], planeID) * tjs.UnitsPerTick;
3452 
3453  // now find the direction if dir is defined
3454  if(dir[0] == 0 && dir[1] == 0 && dir[2] == 0) return tp;
3455  // Make a point at the origin and one 100 units away
3456  // BUG the double brace syntax is required to work around clang bug 21629
3457  // (https://bugs.llvm.org/show_bug.cgi?id=21629)
3458  Point3_t ori3 = {{0.0, 0.0, 0.0}};
3459  Point3_t pos3 = {{100 * dir[0], 100 * dir[1], 100 * dir[2]}};
3460  // 2D position of ori3 and the pos3 projection
3461  std::array<double, 2> ori2;
3462  std::array<double, 2> pos2;
3463  std::array<double, 2> dir2;
3464  // the wire coordinates
3465  ori2[0] = tjs.geom->WireCoordinate(ori3[1], ori3[2], planeID);
3466  pos2[0] = tjs.geom->WireCoordinate(pos3[1], pos3[2], planeID);
3467  // the time coordinates
3468  ori2[1] = tjs.detprop->ConvertXToTicks(ori3[0], planeID) * tjs.UnitsPerTick;
3469  pos2[1] = tjs.detprop->ConvertXToTicks(pos3[0], planeID) * tjs.UnitsPerTick;
3470 
3471  dir2[0] = pos2[0] - ori2[0];
3472  dir2[1] = pos2[1] - ori2[1];
3473 
3474  double norm = sqrt(dir2[0] * dir2[0] + dir2[1] * dir2[1]);
3475  tp.Dir[0] = dir2[0] / norm;
3476  tp.Dir[1] = dir2[1] / norm;
3477  tp.Ang = atan2(dir2[1], dir2[0]);
3478  tp.Delta = norm / 100;
3479 
3480  // The Orth vectors are not unit normalized so we need to correct for this
3481  double w0 = tjs.geom->WireCoordinate(0, 0, planeID);
3482  // cosine-like component
3483  double cs = tjs.geom->WireCoordinate(1, 0, planeID) - w0;
3484  // sine-like component
3485  double sn = tjs.geom->WireCoordinate(0, 1, planeID) - w0;
3486  norm = sqrt(cs * cs + sn * sn);
3487  tp.Delta /= norm;
3488 
3489  return tp;
3490  } // MakeBareTP
3491 
3493  bool MakeBareTrajPoint(const TjStuff& tjs, unsigned int fromHit, unsigned int toHit, TrajPoint& tp)
3494  {
3495  CTP_t tCTP = EncodeCTP(tjs.fHits[fromHit].ArtPtr->WireID());
3496  return MakeBareTrajPoint(tjs, (float)tjs.fHits[fromHit].ArtPtr->WireID().Wire, tjs.fHits[fromHit].PeakTime,
3497  (float)tjs.fHits[toHit].ArtPtr->WireID().Wire, tjs.fHits[toHit].PeakTime, tCTP, tp);
3498 
3499  } // MakeBareTrajPoint
3500 
3502  bool MakeBareTrajPoint(const TjStuff& tjs, float fromWire, float fromTick, float toWire, float toTick, CTP_t tCTP, TrajPoint& tp)
3503  {
3504  tp.CTP = tCTP;
3505  tp.Pos[0] = fromWire;
3506  tp.Pos[1] = tjs.UnitsPerTick * fromTick;
3507  tp.Dir[0] = toWire - fromWire;
3508  tp.Dir[1] = tjs.UnitsPerTick * (toTick - fromTick);
3509  double norm = sqrt(tp.Dir[0] * tp.Dir[0] + tp.Dir[1] * tp.Dir[1]);
3510  if(norm == 0) return false;
3511  tp.Dir[0] /= norm;
3512  tp.Dir[1] /= norm;
3513  tp.Ang = atan2(tp.Dir[1], tp.Dir[0]);
3514  return true;
3515  } // MakeBareTrajPoint
3516 
3518  bool MakeBareTrajPoint(const Point2_t& fromPos, const Point2_t& toPos, TrajPoint& tpOut)
3519  {
3520  tpOut.Pos = fromPos;
3521  tpOut.Dir = PointDirection(fromPos, toPos);
3522 /*
3523  tpOut.Dir[0] = toPos[0] - fromPos[0];
3524  tpOut.Dir[1] = toPos[1] - fromPos[1];
3525  double norm = sqrt(tpOut.Dir[0] * tpOut.Dir[0] + tpOut.Dir[1] * tpOut.Dir[1]);
3526  if(norm == 0) return false;
3527  tpOut.Dir[0] /= norm;
3528  tpOut.Dir[1] /= norm;
3529 */
3530  tpOut.Ang = atan2(tpOut.Dir[1], tpOut.Dir[0]);
3531  return true;
3532 
3533  } // MakeBareTrajPoint
3534 
3536  bool MakeBareTrajPoint(const TjStuff& tjs, const TrajPoint& tpIn1, const TrajPoint& tpIn2, TrajPoint& tpOut)
3537  {
3538  tpOut.CTP = tpIn1.CTP;
3539  tpOut.Pos = tpIn1.Pos;
3540  tpOut.Dir = PointDirection(tpIn1.Pos, tpIn2.Pos);
3541 /*
3542  tpOut.Dir[0] = tpIn2.Pos[0] - tpIn1.Pos[0];
3543  tpOut.Dir[1] = tpIn2.Pos[1] - tpIn1.Pos[1];
3544  double norm = sqrt(tpOut.Dir[0] * tpOut.Dir[0] + tpOut.Dir[1] * tpOut.Dir[1]);
3545  if(norm == 0) return false;
3546  tpOut.Dir[0] /= norm;
3547  tpOut.Dir[1] /= norm;
3548 */
3549  tpOut.Ang = atan2(tpOut.Dir[1], tpOut.Dir[0]);
3550  return true;
3551  } // MakeBareTrajPoint
3552 
3554  unsigned short FarEnd(const TjStuff& tjs, const Trajectory& tj, const Point2_t& pos)
3555  {
3556  // Returns the end (0 or 1) of the Tj that is furthest away from the position pos
3557  if(tj.ID == 0) return 0;
3558  if(PosSep2(tj.Pts[tj.EndPt[1]].Pos, pos) > PosSep2(tj.Pts[tj.EndPt[0]].Pos, pos)) return 1;
3559  return 0;
3560  } // FarEnd
3561 
3564  {
3565  // Finds the direction vector between the two points from p1 to p2
3566  Vector2_t dir;
3567  for(unsigned short xyz = 0; xyz < 2; ++xyz) dir[xyz] = p2[xyz] - p1[xyz];
3568  if(dir[0] == 0 && dir[1] == 0) return dir;
3569  double norm = sqrt(dir[0] * dir[0] + dir[1] * dir[1]);
3570  dir[0] /= norm;
3571  dir[1] /= norm;
3572  return dir;
3573  } // PointDirection
3574 
3576  float TPHitsRMSTime(TjStuff& tjs, TrajPoint& tp, HitStatus_t hitRequest)
3577  {
3578  return tjs.UnitsPerTick * TPHitsRMSTick(tjs, tp, hitRequest);
3579  } // TPHitsRMSTime
3580 
3582  float TPHitsRMSTick(TjStuff& tjs, TrajPoint& tp, HitStatus_t hitRequest)
3583  {
3584  // Estimate the RMS of all hits associated with a trajectory point
3585  // without a lot of calculation
3586  if(tp.Hits.empty()) return 0;
3587  float minVal = 9999;
3588  float maxVal = 0;
3589  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
3590  bool useit = (hitRequest == kAllHits);
3591  if(hitRequest == kUsedHits && tp.UseHit[ii]) useit = true;
3592  if(hitRequest == kUnusedHits && !tp.UseHit[ii]) useit = true;
3593  if(!useit) continue;
3594  unsigned int iht = tp.Hits[ii];
3595  float cv = tjs.fHits[iht].PeakTime;
3596  float rms = tjs.fHits[iht].RMS;
3597  float arg = cv - rms;
3598  if(arg < minVal) minVal = arg;
3599  arg = cv + rms;
3600  if(arg > maxVal) maxVal = arg;
3601  } // ii
3602  if(maxVal == 0) return 0;
3603  return (maxVal - minVal) / 2;
3604  } // TPHitsRMSTick
3605 
3607  float HitsRMSTime(TjStuff& tjs, const std::vector<unsigned int>& hitsInMultiplet, HitStatus_t hitRequest)
3608  {
3609  return tjs.UnitsPerTick * HitsRMSTick(tjs, hitsInMultiplet, hitRequest);
3610  } // HitsRMSTick
3611 
3613  float HitsRMSTick(TjStuff& tjs, const std::vector<unsigned int>& hitsInMultiplet, HitStatus_t hitRequest)
3614  {
3615  if(hitsInMultiplet.empty()) return 0;
3616 
3617  if(hitsInMultiplet.size() == 1) return tjs.fHits[hitsInMultiplet[0]].RMS;
3618 
3619  float minVal = 9999;
3620  float maxVal = 0;
3621  for(unsigned short ii = 0; ii < hitsInMultiplet.size(); ++ii) {
3622  unsigned int iht = hitsInMultiplet[ii];
3623  bool useit = (hitRequest == kAllHits);
3624  if(hitRequest == kUsedHits && tjs.fHits[iht].InTraj > 0) useit = true;
3625  if(hitRequest == kUnusedHits && tjs.fHits[iht].InTraj == 0) useit = true;
3626  if(!useit) continue;
3627  float cv = tjs.fHits[iht].PeakTime;
3628  float rms = tjs.fHits[iht].RMS;
3629  float arg = cv - rms;
3630  if(arg < minVal) minVal = arg;
3631  arg = cv + rms;
3632  if(arg > maxVal) maxVal = arg;
3633  } // ii
3634  if(maxVal == 0) return 0;
3635  return (maxVal - minVal) / 2;
3636  } // HitsRMSTick
3637 
3639  float HitsPosTime(TjStuff& tjs, const std::vector<unsigned int>& hitsInMultiplet, float& sum, HitStatus_t hitRequest)
3640  {
3641  return tjs.UnitsPerTick * HitsPosTick(tjs, hitsInMultiplet, sum, hitRequest);
3642  } // HitsPosTime
3643 
3645  float HitsPosTick(TjStuff& tjs, const std::vector<unsigned int>& hitsInMultiplet, float& sum, HitStatus_t hitRequest)
3646  {
3647  // returns the position and the charge
3648  float pos = 0;
3649  sum = 0;
3650  for(unsigned short ii = 0; ii < hitsInMultiplet.size(); ++ii) {
3651  unsigned int iht = hitsInMultiplet[ii];
3652  bool useit = (hitRequest == kAllHits);
3653  if(hitRequest == kUsedHits && tjs.fHits[iht].InTraj > 0) useit = true;
3654  if(hitRequest == kUnusedHits && tjs.fHits[iht].InTraj == 0) useit = true;
3655  if(!useit) continue;
3656  float chg = tjs.fHits[iht].Integral;
3657  pos += chg * tjs.fHits[iht].PeakTime;
3658  sum += chg;
3659  } // ii
3660  if(sum == 0) return 0;
3661  return pos / sum;
3662  } // HitsPosTick
3663 
3665  unsigned short NumUsedHitsInTj(const TjStuff& tjs, const Trajectory& tj)
3666  {
3667  if(tj.AlgMod[kKilled]) return 0;
3668  if(tj.Pts.empty()) return 0;
3669  unsigned short nhits = 0;
3670  for(auto& tp : tj.Pts) {
3671  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) if(tp.UseHit[ii]) ++nhits;
3672  } // tp
3673  return nhits;
3674  } // NumHitsInTj
3675 
3677  unsigned short NumHitsInTP(const TrajPoint& tp, HitStatus_t hitRequest)
3678  {
3679  // Counts the number of hits of the specified type in tp
3680  if(tp.Hits.empty()) return 0;
3681 
3682  if(hitRequest == kAllHits) return tp.Hits.size();
3683 
3684  unsigned short nhits = 0;
3685  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
3686  if(hitRequest == kUsedHits) {
3687  if(tp.UseHit[ii]) ++nhits;
3688  } else {
3689  // looking for unused hits
3690  if(!tp.UseHit[ii]) ++nhits;
3691  }
3692  } // ii
3693  return nhits;
3694  } // NumHitsInTP
3695 
3697  void SetPDGCode(TjStuff& tjs, unsigned short itj)
3698  {
3699  if(itj > tjs.allTraj.size() - 1) return;
3700  SetPDGCode(tjs, tjs.allTraj[itj]);
3701  }
3702 
3705  {
3706  // Sets the PDG code for the supplied trajectory. Note that the existing
3707  // PDG code is left unchanged if these cuts are not met
3708 
3709  tj.MCSMom = MCSMom(tjs, tj);
3710  if(tjs.MuonTag[0] <= 0) return;
3711  // Special handling of very long straight trajectories, e.g. uB cosmic rays
3712  unsigned short npts = tj.EndPt[1] - tj.EndPt[0];
3713  bool isAMuon = (npts > (unsigned short)tjs.MuonTag[0] && tj.MCSMom > tjs.MuonTag[1]);
3714  // anything really really long must be a muon
3715  if(npts > 500) isAMuon = true;
3716  if(isAMuon) tj.PDGCode = 13;
3717 
3718  } // SetPDGCode
3719 
3720 
3722  bool FillWireHitRange(TjStuff& tjs, const geo::TPCID& tpcid)
3723  {
3724  // fills the WireHitRange vector. Slightly modified version of the one in ClusterCrawlerAlg.
3725  // Returns false if there was a serious error
3726 
3727  // determine the number of planes
3728  geo::TPCGeo const& TPC = tjs.geom->TPC(tpcid);
3729  unsigned int cstat = tpcid.Cryostat;
3730  unsigned int tpc = tpcid.TPC;
3731  unsigned short nplanes = TPC.Nplanes();
3732  tjs.NumPlanes = nplanes;
3733  tjs.TPCID = tpcid;
3734 
3735  // Y,Z limits of the detector
3736  double local[3] = {0.,0.,0.};
3737  double world[3] = {0.,0.,0.};
3738  const geo::TPCGeo &thetpc = tjs.geom->TPC(tpc, cstat);
3739  thetpc.LocalToWorld(local,world);
3740  // reduce the active area of the TPC by 1 cm to prevent wire boundary issues
3741  tjs.XLo = world[0]-tjs.geom->DetHalfWidth(tpc,cstat) + 1;
3742  tjs.XHi = world[0]+tjs.geom->DetHalfWidth(tpc,cstat) - 1;
3743  tjs.YLo = world[1]-tjs.geom->DetHalfHeight(tpc,cstat) + 1;
3744  tjs.YHi = world[1]+tjs.geom->DetHalfHeight(tpc,cstat) - 1;
3745  tjs.ZLo = world[2]-tjs.geom->DetLength(tpc,cstat)/2 + 1;
3746  tjs.ZHi = world[2]+tjs.geom->DetLength(tpc,cstat)/2 - 1;
3747 
3748  lariov::ChannelStatusProvider const& channelStatus = art::ServiceHandle<lariov::ChannelStatusService>()->GetProvider();
3749 
3750  if(!tjs.WireHitRange.empty()) tjs.WireHitRange.clear();
3751 
3752  // initialize everything
3753  tjs.WireHitRange.resize(nplanes);
3754  tjs.FirstWire.resize(nplanes);
3755  tjs.LastWire.resize(nplanes);
3756  tjs.NumWires.resize(nplanes);
3757  tjs.MaxPos0.resize(nplanes);
3758  tjs.MaxPos1.resize(nplanes);
3759  tjs.AveHitRMS.resize(nplanes, nplanes);
3760 
3761  std::pair<int, int> flag;
3762  flag.first = -2; flag.second = -2;
3763 
3764  // Calculate tjs.UnitsPerTick, the scale factor to convert a tick into
3765  // Wire Spacing Equivalent (WSE) units where the wire spacing in this plane = 1.
3766  // Strictly speaking this factor should be calculated for each plane to handle the
3767  // case where the wire spacing is different in each plane. Deal with this later if
3768  // the approximation used here fails.
3769 
3770  raw::ChannelID_t channel = tjs.geom->PlaneWireToChannel(0, 0, (int)tpc, (int)cstat);
3771  tjs.WirePitch = tjs.geom->WirePitch(tjs.geom->View(channel));
3772  float tickToDist = tjs.detprop->DriftVelocity(tjs.detprop->Efield(),tjs.detprop->Temperature());
3773  tickToDist *= 1.e-3 * tjs.detprop->SamplingRate(); // 1e-3 is conversion of 1/us to 1/ns
3774  tjs.UnitsPerTick = tickToDist / tjs.WirePitch;
3775  for(unsigned short ipl = 0; ipl < nplanes; ++ipl) {
3776  tjs.FirstWire[ipl] = INT_MAX;
3777  tjs.LastWire[ipl] = 0;
3778  tjs.NumWires[ipl] = tjs.geom->Nwires(ipl, tpc, cstat);
3779  tjs.WireHitRange[ipl].resize(tjs.NumWires[ipl], flag);
3780  tjs.MaxPos0[ipl] = (float)tjs.NumWires[ipl] - 0.5;
3781  tjs.MaxPos1[ipl] = (float)tjs.detprop->NumberTimeSamples() * tjs.UnitsPerTick;
3782  }
3783 
3784  // overwrite with the "dead wires" condition
3785  flag.first = -1; flag.second = -1;
3786  for(unsigned short ipl = 0; ipl < nplanes; ++ipl) {
3787  for(unsigned int wire = 0; wire < tjs.NumWires[ipl]; ++wire) {
3788  raw::ChannelID_t chan = tjs.geom->PlaneWireToChannel((int)ipl, (int)wire, (int)tpc, (int)cstat);
3789  if(!channelStatus.IsGood(chan)) tjs.WireHitRange[ipl][wire] = flag;
3790  } // wire
3791  } // ipl
3792 
3793  unsigned int lastwire = 0, lastipl = 0;
3794  for(unsigned int iht = 0; iht < tjs.fHits.size(); ++iht) {
3795  if(tjs.fHits[iht].ArtPtr->WireID().Cryostat != cstat) continue;
3796  if(tjs.fHits[iht].ArtPtr->WireID().TPC != tpc) continue;
3797  unsigned short ipl = tjs.fHits[iht].ArtPtr->WireID().Plane;
3798  unsigned int wire = tjs.fHits[iht].ArtPtr->WireID().Wire;
3799  if(wire > tjs.NumWires[ipl] - 1) {
3800  mf::LogWarning("TC")<<"FillWireHitRange: Invalid wire number "<<wire<<" > "<<tjs.NumWires[ipl] - 1<<" in plane "<<ipl<<" Quitting";
3801  return false;
3802  } // too large wire number
3803  if(ipl == lastipl && wire < lastwire) {
3804  mf::LogWarning("TC")<<"FillWireHitRange: Hits are not in increasing wire order. Quitting ";
3805  return false;
3806  } // hits out of order
3807  lastwire = wire;
3808  lastipl = ipl;
3809  if(tjs.FirstWire[ipl] == INT_MAX) tjs.FirstWire[ipl] = wire;
3810  if(tjs.WireHitRange[ipl][wire].first < 0) tjs.WireHitRange[ipl][wire].first = iht;
3811  tjs.WireHitRange[ipl][wire].second = iht + 1;
3812  tjs.LastWire[ipl] = wire + 1;
3813  } // iht
3814 
3815  if(!CheckWireHitRange(tjs)) return false;
3816 
3817  // Find the average multiplicity 1 hit RMS and calculate the expected max RMS for each range
3818  if(tjs.DebugMode && (int)tpc == debug.TPC) {
3819  std::cout<<"tpc "<<tpc<<" tjs.UnitsPerTick "<<std::setprecision(3)<<tjs.UnitsPerTick<<"\n";
3820  std::cout<<"Fiducial volume (";
3821  std::cout<<std::fixed<<std::setprecision(1)<<tjs.XLo<<" < X < "<<tjs.XHi<<") (";
3822  std::cout<<std::fixed<<std::setprecision(1)<<tjs.YLo<<" < Y < "<<tjs.YHi<<") (";
3823  std::cout<<std::fixed<<std::setprecision(1)<<tjs.ZLo<<" < Z < "<<tjs.ZHi<<")\n";
3824  }
3825  for(unsigned short ipl = 0; ipl < tjs.NumPlanes; ++ipl) {
3826  float sumRMS = 0;
3827  float sumAmp = 0;
3828  unsigned int cnt = 0;
3829  for(unsigned int wire = 0; wire < tjs.NumWires[ipl]; ++wire) {
3830  if(tjs.WireHitRange[ipl][wire].first < 0) continue;
3831  unsigned int firstHit = tjs.WireHitRange[ipl][wire].first;
3832  unsigned int lastHit = tjs.WireHitRange[ipl][wire].second;
3833  // don't let noisy wires screw up the calculation
3834  if(lastHit - firstHit > 100) continue;
3835  for(unsigned int iht = firstHit; iht < lastHit; ++iht) {
3836  if(tjs.fHits[iht].Multiplicity != 1) continue;
3837  if(tjs.fHits[iht].GoodnessOfFit < 0 || tjs.fHits[iht].GoodnessOfFit > 100) continue;
3838  // don't let a lot of runt hits screw up the calculation
3839  if(tjs.fHits[iht].PeakAmplitude < 1) continue;
3840  ++cnt;
3841  sumRMS += tjs.fHits[iht].RMS;
3842  sumAmp += tjs.fHits[iht].PeakAmplitude;
3843  } // iht
3844  } // wire
3845  if(cnt < 4) continue;
3846  tjs.AveHitRMS[ipl] = sumRMS/(float)cnt;
3847  sumAmp /= (float)cnt;
3848  if(tjs.DebugMode) std::cout<<"Pln "<<ipl<<" tjs.AveHitRMS "<<tjs.AveHitRMS[ipl]<<" Ave PeakAmplitude "<<sumAmp<<"\n";
3849  } // ipl
3850  return true;
3851 
3852  } // FillWireHitRange
3853 
3855  bool CheckWireHitRange(const TjStuff& tjs)
3856  {
3857  // do a QC check
3858  for(unsigned short ipl = 0; ipl < tjs.NumPlanes; ++ipl) {
3859  for(unsigned int wire = 0; wire < tjs.NumWires[ipl]; ++wire) {
3860  // No hits or dead wire
3861  if(tjs.WireHitRange[ipl][wire].first < 0) continue;
3862  unsigned int firstHit = tjs.WireHitRange[ipl][wire].first;
3863  unsigned int lastHit = tjs.WireHitRange[ipl][wire].second;
3864  if(lastHit > tjs.fHits.size()) {
3865  mf::LogWarning("TC")<<"CheckWireHitRange: Invalid lastHit "<<lastHit<<" > fHits.size "<<tjs.fHits.size()<<" in plane "<<ipl;
3866  return false;
3867  }
3868  for(unsigned int iht = firstHit; iht < lastHit; ++iht) {
3869  if(tjs.fHits[iht].ArtPtr->WireID().Plane != ipl) {
3870  mf::LogWarning("TC")<<"CheckWireHitRange: Invalid plane "<<tjs.fHits[iht].ArtPtr->WireID().Plane<<" != "<<ipl;
3871  return false;
3872  }
3873  if(tjs.fHits[iht].ArtPtr->WireID().Wire != wire) {
3874  mf::LogWarning("TC")<<"CheckWireHitRange: Invalid wire "<<tjs.fHits[iht].ArtPtr->WireID().Wire<<" != "<<wire<<" in plane "<<ipl;
3875  return false;
3876  }
3877  } // iht
3878  } // wire
3879  } // ipl
3880 
3881  return true;
3882 
3883  } // CheckWireHitRange
3884 
3886  bool WireHitRangeOK(const TjStuff& tjs, const CTP_t& inCTP)
3887  {
3888  // returns true if the passed CTP code is consistent with the CT code of the WireHitRangeVector
3889  geo::PlaneID planeID = DecodeCTP(inCTP);
3890  if(planeID.Cryostat != tjs.TPCID.Cryostat) return false;
3891  if(planeID.TPC != tjs.TPCID.TPC) return false;
3892  return true;
3893  }
3894 
3896  bool MergeAndStore(TjStuff& tjs, unsigned int itj1, unsigned int itj2, bool doPrt)
3897  {
3898  // Merge the two trajectories in allTraj and store them. Returns true if it was successfull.
3899  // Merging is done between the end (end = 1) of tj1 and the beginning (end = 0) of tj2. This function preserves the
3900  // AlgMod state of itj1.
3901  // The itj1 -> itj2 merge order is reversed if end1 of itj2 is closer to end0 of itj1
3902 
3903  if(itj1 > tjs.allTraj.size() - 1) return false;
3904  if(itj2 > tjs.allTraj.size() - 1) return false;
3905  if(tjs.allTraj[itj1].AlgMod[kKilled] || tjs.allTraj[itj2].AlgMod[kKilled]) return false;
3906 
3907  // Merging shower Tjs requires merging the showers as well.
3908  if(tjs.allTraj[itj1].AlgMod[kShowerTj] || tjs.allTraj[itj2].AlgMod[kShowerTj]) return MergeShowerTjsAndStore(tjs, itj1, itj2, doPrt);
3909 
3910  // Ensure that the order of 3D-matched Tjs is consistent with the convention that
3911  unsigned short pfp1 = GetPFPIndex(tjs, tjs.allTraj[itj1].ID);
3912  unsigned short pfp2 = GetPFPIndex(tjs, tjs.allTraj[itj2].ID);
3913  if(pfp1 != USHRT_MAX || pfp2 != USHRT_MAX) {
3914  if(pfp1 != USHRT_MAX && pfp2 != USHRT_MAX) {
3915 // std::cout<<"MAS: Both tjs are used in a PFParticle. Need PFParticle merging code to do this. pfps size "<<tjs.pfps.size()<<"\n";
3916  return false;
3917  }
3918  // Swap so that the order of tj1 is preserved. Tj2 may be reversed to be consistent
3919  if(pfp1 == USHRT_MAX) std::swap(itj1, itj2);
3920  } // one or both used in a PFParticle
3921 
3922  // make copies so they can be trimmed as needed
3923  Trajectory tj1 = tjs.allTraj[itj1];
3924  Trajectory tj2 = tjs.allTraj[itj2];
3925 
3926  // ensure that these are in the same step order
3927  if(tj2.StepDir != tj1.StepDir) ReverseTraj(tjs, tj2);
3928 
3929  Point2_t tp1e0 = tj1.Pts[tj1.EndPt[0]].Pos;
3930  Point2_t tp1e1 = tj1.Pts[tj1.EndPt[1]].Pos;
3931  Point2_t tp2e0 = tj2.Pts[tj2.EndPt[0]].Pos;
3932  Point2_t tp2e1 = tj2.Pts[tj2.EndPt[1]].Pos;
3933 
3934  if(doPrt) {
3935  mf::LogVerbatim("TC")<<"MergeAndStore: tj1 T"<<tj1.ID<<" tj2 T"<<tj2.ID<<" at merge points "<<PrintPos(tjs, tp1e1)<<" "<<PrintPos(tjs, tp2e0);
3936  }
3937 
3938  // swap the order so that abs(tj1end1 - tj2end0) is less than abs(tj2end1 - tj1end0)
3939  if(PosSep2(tp1e1, tp2e0) > PosSep2(tp2e1, tp1e0)) {
3940  std::swap(tj1, tj2);
3941  std::swap(tp1e0, tp2e0);
3942  std::swap(tp1e1, tp2e1);
3943  if(doPrt) mf::LogVerbatim("TC")<<" swapped the order. Merge points "<<PrintPos(tjs, tp1e1)<<" "<<PrintPos(tjs, tp2e0);
3944  }
3945 
3946  // Here is what we are looking for, where - indicates a TP with charge.
3947  // Note that this graphic is in the stepping direction (+1 = +wire direction)
3948  // tj1: 0------------1
3949  // tj2: 0-----------1
3950  // Another possibility with overlap
3951  // tj1: 0-------------1
3952  // tj2: 0--------------1
3953 
3954  if(tj1.StepDir > 1) {
3955  // Not allowed
3956  // tj1: 0---------------------------1
3957  // tj2: 0------1
3958  if(tp2e0[0] > tp1e0[0] && tp2e1[0] < tp1e1[0]) return false;
3960  // tj1: 0------1
3961  // tj2: 0---------------------------1
3962  if(tp1e0[0] > tp2e0[0] && tp1e1[0] < tp2e1[0]) return false;
3963  } else {
3964  // same as above but with ends reversed
3965  if(tp2e1[0] > tp1e1[0] && tp2e0[0] < tp1e0[0]) return false;
3966  if(tp1e1[0] > tp2e1[0] && tp1e0[0] < tp2e0[0]) return false;
3967  }
3968 
3969  if(tj1.VtxID[1] > 0 && tj2.VtxID[0] == tj1.VtxID[1]) {
3970  auto& vx = tjs.vtx[tj1.VtxID[1] - 1];
3971  if(!MakeVertexObsolete(tjs, vx, false)) {
3972  if(doPrt) mf::LogVerbatim("TC")<<"MergeAndStore: Found a good vertex between Tjs "<<tj1.VtxID[1]<<" No merging";
3973  return false;
3974  }
3975  }
3976 
3977  if(tj1.StopFlag[1][kBragg]) {
3978  if(doPrt) mf::LogVerbatim("TC")<<"MergeAndStore: You are merging the end of trajectory T"<<tj1.ID<<" with a Bragg peak. Not merging\n";
3979  return false;
3980  }
3981 
3982  // remove any points at the end of tj1 that don't have used hits
3983  tj1.Pts.resize(tj1.EndPt[1] + 1);
3984 
3985  // determine if they overlap by finding the point on tj2 that is closest
3986  // to the end point of tj1.
3987  TrajPoint& endtj1TP = tj1.Pts[tj1.EndPt[1]];
3988  // Set minSep large so that dead wire regions are accounted for
3989  float minSep = 1000;
3990  unsigned short tj2ClosePt = 0;
3991  // Note that TrajPointTrajDOCA only considers TPs that have charge
3992  TrajPointTrajDOCA(tjs, endtj1TP, tj2, tj2ClosePt, minSep);
3993  if(doPrt) mf::LogVerbatim("TC")<<" Merge point tj1 "<<PrintPos(tjs, endtj1TP)<<" tj2ClosePt "<<tj2ClosePt<<" Pos "<<PrintPos(tjs, tj2.Pts[tj2ClosePt]);
3994  // check for full overlap
3995  if(tj2ClosePt > tj2.EndPt[1]) return false;
3996 
3997  // The approach is to append tj2 to tj1, store tj1 as a new trajectory,
3998  // and re-assign all hits to the new trajectory
3999 
4000  // First ensure that any hit will appear only once in the merged trajectory in the overlap region
4001  // whether it is used or unused. The point on tj2 where the merge will begin, tj2ClosePt, will be
4002  // increased until this condition is met.
4003  // Make a temporary vector of tj1 hits in the end points for simpler searching
4004  std::vector<unsigned int> tj1Hits;
4005  for(unsigned short ii = 0; ii < tj1.Pts.size(); ++ii) {
4006  // only go back a few points in tj1
4007  if(ii > 10) break;
4008  unsigned short ipt = tj1.Pts.size() - 1 - ii;
4009  tj1Hits.insert(tj1Hits.end(), tj1.Pts[ipt].Hits.begin(), tj1.Pts[ipt].Hits.end());
4010  if(ipt == 0) break;
4011  } // ii
4012 
4013  bool bumpedPt = true;
4014  while(bumpedPt) {
4015  bumpedPt = false;
4016  for(unsigned short ii = 0; ii < tj2.Pts[tj2ClosePt].Hits.size(); ++ii) {
4017  unsigned int iht = tj2.Pts[tj2ClosePt].Hits[ii];
4018  if(std::find(tj1Hits.begin(), tj1Hits.end(), iht) != tj1Hits.end()) bumpedPt = true;
4019  } // ii
4020  if(bumpedPt && tj2ClosePt < tj2.EndPt[1]) {
4021  ++tj2ClosePt;
4022  } else {
4023  break;
4024  }
4025  } // bumpedPt
4026  if(doPrt) mf::LogVerbatim("TC")<<" revised tj2ClosePt "<<tj2ClosePt;
4027  // append tj2 hits to tj1
4028 
4029  tj1.Pts.insert(tj1.Pts.end(), tj2.Pts.begin() + tj2ClosePt, tj2.Pts.end());
4030  // re-define the end points
4031  SetEndPoints(tjs, tj1);
4032  tj1.StopFlag[1] = tj2.StopFlag[1];
4033 
4034  // A more exhaustive check that hits only appear once
4035  if(HasDuplicateHits(tjs, tj1, doPrt)) {
4036  if(doPrt) {
4037  mf::LogVerbatim("TC")<<"MergeAndStore found duplicate hits. Coding error";
4038  PrintTrajectory("MAS", tjs, tj1, USHRT_MAX);
4039  PrintTrajectory("tj1", tjs, tjs.allTraj[itj1], USHRT_MAX);
4040  PrintTrajectory("tj2", tjs, tjs.allTraj[itj2], USHRT_MAX);
4041  }
4042  return false;
4043  }
4044  if(tj2.VtxID[1] > 0) {
4045  // move the end vertex of tj2 to the end of tj1
4046  tj1.VtxID[1] = tj2.VtxID[1];
4047  }
4048  // Transfer some of the AlgMod bits
4049  if(tj2.AlgMod[kMichel]) tj1.AlgMod[kMichel] = true;
4050  if(tj2.AlgMod[kDeltaRay]) {
4051  tj1.AlgMod[kDeltaRay] = true;
4052  tj1.ParentID = tj2.ParentID;
4053  }
4054  // keep track of the IDs before they are clobbered
4055  int tj1ID = tj1.ID;
4056  int tj2ID = tj2.ID;
4057  // kill the original trajectories
4058  MakeTrajectoryObsolete(tjs, itj1);
4059  MakeTrajectoryObsolete(tjs, itj2);
4060  // Do this so that StoreTraj keeps the correct WorkID (of itj1)
4061  tj1.ID = tj1.WorkID;
4062  SetPDGCode(tjs, tj1);
4063  if(!StoreTraj(tjs, tj1)) return false;
4064  int newTjID = tjs.allTraj.size();
4065  // Use the ParentID to trace which new Tj is superseding the merged ones
4066  tj1.ParentID = newTjID;
4067  tj2.ParentID = newTjID;
4068  // update match structs if they exist
4069  UpdateMatchStructs(tjs, tj1.ID, newTjID);
4070  UpdateMatchStructs(tjs, tj2.ID, newTjID);
4071  if(doPrt) mf::LogVerbatim("TC")<<" MAS success. New Tj T"<<newTjID;
4072  // Transfer the ParentIDs of any other Tjs that refer to Tj1 and Tj2 to the new Tj
4073  for(auto& tj : tjs.allTraj) if(tj.ParentID == tj1ID || tj.ParentID == tj2ID) tj.ParentID = newTjID;
4074 
4075  return true;
4076  } // MergeAndStore
4077 
4079  std::vector<int> GetAssns(const TjStuff& tjs, std::string type1Name, int id, std::string type2Name)
4080  {
4081  // returns a list of IDs of objects (tjs, vertices, pfps, etc) with type1Name that are in TJStuff with
4082  // type2Name. This is intended to be a general purpose replacement for specific functions like GetVtxTjIDs, etc
4083 
4084  std::vector<int> tmp;
4085  if(id <= 0) return tmp;
4086  unsigned int uid = id;
4087 
4088  if(type1Name == "T" && uid <= tjs.allTraj.size() && type2Name == "P") {
4089  // return a list of PFPs that have the tj in TjIDs, P -> T<ID>
4090  for(auto& pfp : tjs.pfps) {
4091  if(pfp.ID <= 0) continue;
4092  if(std::find(pfp.TjIDs.begin(), pfp.TjIDs.end(), id) != pfp.TjIDs.end()) tmp.push_back(pfp.ID);
4093  } // pf
4094  return tmp;
4095  } // P -> T
4096 
4097  if(type1Name == "P" && uid <= tjs.pfps.size() && (type2Name == "2S" || type2Name == "3S")) {
4098  // return a list of 3D or 2D showers with the assn 3S -> 2S -> T -> P<ID> or 2S -> T -> P.
4099  auto& pfp = tjs.pfps[uid - 1];
4100  // First form a list of 2S -> T -> P<ID>
4101  std::vector<int> ssid;
4102  for(auto& ss : tjs.cots) {
4103  if(ss.ID <= 0) continue;
4104  auto shared = SetIntersection(ss.TjIDs, pfp.TjIDs);
4105  if(!shared.empty() && std::find(ssid.begin(), ssid.end(), ss.ID) == ssid.end()) ssid.push_back(ss.ID);
4106  } // ss
4107  if(type2Name == "2S") return ssid;
4108  for(auto& ss3 : tjs.showers) {
4109  if(ss3.ID <= 0) continue;
4110  auto shared = SetIntersection(ss3.CotIDs, ssid);
4111  if(!shared.empty() && std::find(tmp.begin(), tmp.end(), ss3.ID) == tmp.end()) tmp.push_back(ss3.ID);
4112  } // ss3
4113  return tmp;
4114  } // 3S -> 2S -> T -> P
4115 
4116  if(type1Name == "2V" && uid <= tjs.vtx.size() && type2Name == "T" ) {
4117  // 2V -> T
4118  for(auto& tj : tjs.allTraj) {
4119  if(tj.AlgMod[kKilled]) continue;
4120  for(unsigned short end = 0; end < 2; ++end) {
4121  if(tj.VtxID[end] != id) continue;
4122  if(std::find(tmp.begin(), tmp.end(), tj.ID) == tmp.end()) tmp.push_back(tj.ID);
4123  } // end
4124  } // tj
4125  return tmp;
4126  } // 2V -> T
4127 
4128  if(type1Name == "3V" && uid <= tjs.vtx3.size() && type2Name == "P") {
4129  for(auto& pfp : tjs.pfps) {
4130  if(pfp.ID == 0) continue;
4131  for(unsigned short end = 0; end < 2; ++end) {
4132  if(pfp.Vx3ID[end] != id) continue;
4133  // encode the end with the ID
4134  if(std::find(tmp.begin(), tmp.end(), pfp.ID) == tmp.end()) tmp.push_back(pfp.ID);
4135  } // end
4136  } // pfp
4137  return tmp;
4138  } // 3V -> P
4139 
4140  if(type1Name == "3V" && uid <= tjs.vtx3.size() && type2Name == "T") {
4141  // 3V -> T
4142  for(auto& tj : tjs.allTraj) {
4143  if(tj.AlgMod[kKilled]) continue;
4144  for(unsigned short end = 0; end < 2; ++end) {
4145  if(tj.VtxID[end] > 0 && tj.VtxID[end] <= tjs.vtx.size()) {
4146  auto& vx2 = tjs.vtx[tj.VtxID[end] - 1];
4147  if(vx2.Vx3ID != id) continue;
4148  if(std::find(tmp.begin(), tmp.end(), tj.ID) == tmp.end()) tmp.push_back(tj.ID);
4149  }
4150  } // end
4151  } // tj
4152  return tmp;
4153  } // 3V -> T
4154 
4155  if(type1Name == "3V" && uid <= tjs.vtx3.size() && type2Name == "2V") {
4156  // 3V -> 2V
4157  for(auto& vx2 : tjs.vtx) {
4158  if(vx2.ID == 0) continue;
4159  if(vx2.Vx3ID == id) tmp.push_back(vx2.ID);
4160  } // vx2
4161  return tmp;
4162  } // 3V -> 2V
4163 
4164  if(type1Name == "3S" && uid <= tjs.showers.size() && type2Name == "T") {
4165  // 3S -> T
4166  auto& ss3 = tjs.showers[uid - 1];
4167  if(ss3.ID == 0) return tmp;
4168  for(auto cid : ss3.CotIDs) {
4169  auto& ss = tjs.cots[cid - 1];
4170  if(ss.ID == 0) continue;
4171  tmp.insert(tmp.end(), ss.TjIDs.begin(), ss.TjIDs.end());
4172  } // cid
4173  return tmp;
4174  } // 3S -> T
4175 
4176  // This isn't strictly necessary but do it for consistency
4177  if(type1Name == "2S" && uid <= tjs.cots.size() && type2Name == "T") {
4178  // 2S -> T
4179  auto& ss = tjs.cots[uid - 1];
4180  return ss.TjIDs;
4181  } // 2S -> T
4182 
4183  if(type1Name == "3S" && uid <= tjs.showers.size() && type2Name == "P") {
4184  // 3S -> P
4185  auto& ss3 = tjs.showers[uid - 1];
4186  if(ss3.ID == 0) return tmp;
4187  for(auto cid : ss3.CotIDs) {
4188  auto& ss = tjs.cots[cid - 1];
4189  if(ss.ID == 0) continue;
4190  for(auto tid : ss.TjIDs) {
4191  auto& tj = tjs.allTraj[tid - 1];
4192  if(tj.AlgMod[kKilled]) continue;
4193  if(!tj.AlgMod[kMat3D]) continue;
4194  for(auto& pfp : tjs.pfps) {
4195  if(pfp.ID <= 0) continue;
4196  if(std::find(pfp.TjIDs.begin(), pfp.TjIDs.end(), tj.ID) == pfp.TjIDs.end()) continue;
4197  if(std::find(tmp.begin(), tmp.end(), pfp.ID) == tmp.end()) tmp.push_back(pfp.ID);
4198  } // pf
4199  } // tid
4200  } // cid
4201  return tmp;
4202  } // 3S -> P
4203 
4204  if(type1Name == "T" && uid <= tjs.allTraj.size() && type2Name == "2S") {
4205  // T -> 2S
4206  for(auto& ss : tjs.cots) {
4207  if(ss.ID == 0) continue;
4208  if(std::find(ss.TjIDs.begin(), ss.TjIDs.end(), id) != ss.TjIDs.end()) tmp.push_back(ss.ID);
4209  } // ss
4210  return tmp;
4211  } // T -> 2S
4212 
4213  if(type1Name == "T" && uid <= tjs.allTraj.size() && type2Name == "3S") {
4214  // T -> 3S
4215  for(auto& ss : tjs.cots) {
4216  if(ss.ID == 0) continue;
4217  if(std::find(ss.TjIDs.begin(), ss.TjIDs.end(), id) == ss.TjIDs.end()) continue;
4218  if(ss.SS3ID > 0) tmp.push_back(ss.SS3ID);
4219  } // ss
4220  return tmp;
4221  } // T -> 3S
4222 
4223  std::cout<<"GetAssns doesn't know about "<<type1Name<<" -> "<<type2Name<<" assns, or id "<<id<<" is not valid.\n";
4224 
4225  return tmp;
4226 
4227  } // GetAssns
4228 
4229  // ****************************** Printing ******************************
4230 
4232  void PrintAllTraj(std::string someText, const TjStuff& tjs, const DebugStuff& debug, unsigned short itj, unsigned short ipt, bool prtVtx)
4233  {
4234 
4235  mf::LogVerbatim myprt("TC");
4236 
4237  if(prtVtx) {
4238  if(!tjs.vtx3.empty()) {
4239  // print out 3D vertices
4240  myprt<<someText<<"****** 3D vertices ******************************************__2DVtx_ID__*******\n";
4241  myprt<<someText<<" Vtx Cstat TPC X Y Z XEr YEr ZEr pln0 pln1 pln2 Wire score Prim? Nu? nTru";
4242  myprt<<" ___________2D_Pos____________ _____Tjs________\n";
4243  for(unsigned short iv = 0; iv < tjs.vtx3.size(); ++iv) {
4244  if(tjs.vtx3[iv].ID == 0) continue;
4245  const Vtx3Store& vx3 = tjs.vtx3[iv];
4246  myprt<<someText;
4247  std::string vid = "3V" + std::to_string(vx3.ID);
4248  myprt<<std::right<<std::setw(5)<<std::fixed<<vid;
4249  myprt<<std::setprecision(1);
4250  myprt<<std::right<<std::setw(7)<<vx3.TPCID.Cryostat;
4251  myprt<<std::right<<std::setw(5)<<vx3.TPCID.TPC;
4252  myprt<<std::right<<std::setw(8)<<vx3.X;
4253  myprt<<std::right<<std::setw(8)<<vx3.Y;
4254  myprt<<std::right<<std::setw(8)<<vx3.Z;
4255  myprt<<std::right<<std::setw(5)<<vx3.XErr;
4256  myprt<<std::right<<std::setw(5)<<vx3.YErr;
4257  myprt<<std::right<<std::setw(5)<<vx3.ZErr;
4258  myprt<<std::right<<std::setw(5)<<vx3.Vx2ID[0];
4259  myprt<<std::right<<std::setw(5)<<vx3.Vx2ID[1];
4260  myprt<<std::right<<std::setw(5)<<vx3.Vx2ID[2];
4261  myprt<<std::right<<std::setw(5)<<vx3.Wire;
4262  unsigned short nTruMatch = 0;
4263  for(unsigned short ipl = 0; ipl < tjs.NumPlanes; ++ipl) {
4264  if(vx3.Vx2ID[ipl] == 0) continue;
4265  unsigned short iv2 = vx3.Vx2ID[ipl] - 1;
4266  if(tjs.vtx[iv2].Stat[kVtxTruMatch]) ++nTruMatch;
4267  } // ipl
4268  myprt<<std::right<<std::setw(6)<<std::setprecision(1)<<vx3.Score;
4269  myprt<<std::setw(6)<<vx3.Primary;
4270  myprt<<std::setw(4)<<vx3.Neutrino;
4271  myprt<<std::right<<std::setw(5)<<nTruMatch;
4272  Point2_t pos;
4273  for(unsigned short plane = 0; plane < tjs.NumPlanes; ++plane) {
4274  PosInPlane(tjs, vx3, plane, pos);
4275  myprt<<" "<<PrintPos(tjs, pos);
4276  } // plane
4277  if(vx3.Wire == -2) {
4278  // find the Tjs that are attached to it
4279  for(auto& pfp : tjs.pfps) {
4280  if(pfp.Vx3ID[0] == tjs.vtx3[iv].ID) {
4281  for(auto& tjID : pfp.TjIDs) myprt<<" T"<<tjID;
4282  }
4283  if(pfp.Vx3ID[1] == tjs.vtx3[iv].ID) {
4284  for(auto& tjID : pfp.TjIDs) myprt<<" T"<<tjID;
4285  }
4286  } // ipfp
4287  } else {
4288  auto vxtjs = GetAssns(tjs, "3V", vx3.ID, "T");
4289  for(auto tjid : vxtjs) myprt<<" T"<<tjid;
4290  }
4291  myprt<<"\n";
4292  }
4293  } // tjs.vtx3.size
4294  if(!tjs.vtx.empty()) {
4295  bool foundOne = false;
4296  for(unsigned short iv = 0; iv < tjs.vtx.size(); ++iv) {
4297  auto& vx2 = tjs.vtx[iv];
4298  if(debug.Plane < 3 && debug.Plane != (int)DecodeCTP(vx2.CTP).Plane) continue;
4299  if(vx2.NTraj == 0) continue;
4300  foundOne = true;
4301  } // iv
4302  if(foundOne) {
4303  // print out 2D vertices
4304  myprt<<someText<<"************ 2D vertices ************\n";
4305  myprt<<someText<<" Vtx CTP wire err tick err ChiDOF NTj Pass Topo ChgFrac Score v3D TjIDs\n";
4306  for(auto& vx2 : tjs.vtx) {
4307  if(vx2.ID == 0) continue;
4308  if(debug.Plane < 3 && debug.Plane != (int)DecodeCTP(vx2.CTP).Plane) continue;
4309  myprt<<someText;
4310  std::string vid = "2V" + std::to_string(vx2.ID);
4311  myprt<<std::right<<std::setw(5)<<std::fixed<<vid;
4312  myprt<<std::right<<std::setw(6)<<vx2.CTP;
4313  myprt<<std::right<<std::setw(8)<<std::setprecision(0)<<std::nearbyint(vx2.Pos[0]);
4314  myprt<<std::right<<std::setw(5)<<std::setprecision(1)<<vx2.PosErr[0];
4315  myprt<<std::right<<std::setw(8)<<std::setprecision(0)<<std::nearbyint(vx2.Pos[1]/tjs.UnitsPerTick);
4316  myprt<<std::right<<std::setw(5)<<std::setprecision(1)<<vx2.PosErr[1]/tjs.UnitsPerTick;
4317  myprt<<std::right<<std::setw(7)<<vx2.ChiDOF;
4318  myprt<<std::right<<std::setw(5)<<vx2.NTraj;
4319  myprt<<std::right<<std::setw(5)<<vx2.Pass;
4320  myprt<<std::right<<std::setw(6)<<vx2.Topo;
4321  myprt<<std::right<<std::setw(9)<<std::setprecision(2)<<vx2.TjChgFrac;
4322  myprt<<std::right<<std::setw(6)<<std::setprecision(1)<<vx2.Score;
4323  myprt<<std::right<<std::setw(5)<<vx2.Vx3ID;
4324  myprt<<" ";
4325  // display the traj IDs
4326  for(unsigned short ii = 0; ii < tjs.allTraj.size(); ++ii) {
4327  auto const& aTj = tjs.allTraj[ii];
4328  if(debug.Plane < 3 && debug.Plane != (int)DecodeCTP(aTj.CTP).Plane) continue;
4329  if(aTj.AlgMod[kKilled]) continue;
4330  for(unsigned short end = 0; end < 2; ++end) {
4331  if(aTj.VtxID[end] != (short)vx2.ID) continue;
4332  std::string tid = " T" + std::to_string(aTj.ID) + "_" + std::to_string(end);
4333  myprt<<std::right<<std::setw(6)<<tid;
4334  } // end
4335  } // ii
4336  // Special flags. Ignore the first flag bit (0 = kVtxTrjTried) which is done for every vertex
4337  for(unsigned short ib = 1; ib < VtxBitNames.size(); ++ib) if(vx2.Stat[ib]) myprt<<" "<<VtxBitNames[ib];
4338  myprt<<"\n";
4339  } // iv
4340  }
4341  } // tjs.vtx.size
4342  }
4343 
4344  if(tjs.allTraj.empty()) {
4345  mf::LogVerbatim("TC")<<someText<<" No allTraj trajectories to print";
4346  return;
4347  }
4348 
4349  // Print all trajectories in tjs.allTraj if itj == USHRT_MAX
4350  // Print a single traj (itj) and a single TP (ipt) or all TPs (USHRT_MAX)
4351  if(itj == USHRT_MAX) {
4352  // Print summary trajectory information
4353  std::vector<unsigned int> tmp;
4354 // myprt<<someText<<" TRJ ID CTP Pass Pts W:T Ang CS AveQ dEdx W:T Ang CS AveQ dEdx Chg(k) chgRMS Mom SDr __Vtx__ PDG Par Pri NuPar TRuPDG E*P TruKE WorkID \n";
4355  myprt<<someText<<" ID CTP Pass Pts W:T Ang CS AveQ fnTj W:T Ang CS AveQ fnTj Chg(k) chgRMS Mom SDr __Vtx__ PDG DirFOM Par Pri NuPar TRuPDG E*P TruKE WorkID \n";
4356  for(unsigned short ii = 0; ii < tjs.allTraj.size(); ++ii) {
4357  auto& aTj = tjs.allTraj[ii];
4358  if(debug.Plane >=0 && debug.Plane < 3 && debug.Plane != (int)DecodeCTP(aTj.CTP).Plane) continue;
4359  myprt<<someText<<" ";
4360  std::string tid;
4361  if(aTj.AlgMod[kKilled]) { tid = "k" + std::to_string(aTj.ID); } else { tid = "T" + std::to_string(aTj.ID); }
4362  myprt<<std::fixed<<std::setw(5)<<tid;
4363  myprt<<std::setw(6)<<aTj.CTP;
4364  myprt<<std::setw(5)<<aTj.Pass;
4365 // myprt<<std::setw(5)<<aTj.Pts.size();
4366  myprt<<std::setw(5)<<aTj.EndPt[1] - aTj.EndPt[0] + 1;
4367  unsigned short endPt0 = aTj.EndPt[0];
4368  auto& tp0 = aTj.Pts[endPt0];
4369  int itick = tp0.Pos[1]/tjs.UnitsPerTick;
4370  if(itick < 0) itick = 0;
4371  myprt<<std::setw(6)<<(int)(tp0.Pos[0]+0.5)<<":"<<itick; // W:T
4372  if(itick < 10) { myprt<<" "; }
4373  if(itick < 100) { myprt<<" "; }
4374  if(itick < 1000) { myprt<<" "; }
4375  myprt<<std::setw(6)<<std::setprecision(2)<<tp0.Ang;
4376  myprt<<std::setw(2)<<tp0.AngleCode;
4377  if(aTj.StopFlag[0][kBragg]) {
4378  myprt<<"B";
4379  } else if(aTj.StopFlag[0][kAtVtx]) {
4380  myprt<<"V";
4381  } else if(aTj.StopFlag[0][kAtKink]) {
4382  myprt<<"K";
4383  } else if(aTj.StopFlag[0][kAtTj]) {
4384  myprt<<"T";
4385  } else {
4386  myprt<<" ";
4387  }
4388  myprt<<std::setw(5)<<(int)tp0.AveChg;
4389  // Print the fraction of points in the first half that are in a shower
4390  float frac = 0;
4391  float cnt = 0;
4392  unsigned short midPt = 0.5 * (aTj.EndPt[0] + aTj.EndPt[1]);
4393  for(unsigned short ipt = aTj.EndPt[0]; ipt < midPt; ++ipt) {
4394  auto& tp = aTj.Pts[ipt];
4395 // if(tp.Environment[kEnvNearShower]) ++frac;
4396  if(tp.Environment[kEnvNearTj]) ++frac;
4397  ++cnt;
4398  } // ipt
4399  if(cnt > 0) frac /= cnt;
4400  myprt<<std::setw(5)<<std::setprecision(1)<<frac;
4401 /* print NearInShower fraction instead
4402  unsigned short prec = 1;
4403  if(aTj.dEdx[0] > 99) prec = 0;
4404  myprt<<std::setw(5)<<std::setprecision(prec)<<aTj.dEdx[0];
4405 */
4406  unsigned short endPt1 = aTj.EndPt[1];
4407  auto& tp1 = aTj.Pts[endPt1];
4408  itick = tp1.Pos[1]/tjs.UnitsPerTick;
4409  myprt<<std::setw(6)<<(int)(tp1.Pos[0]+0.5)<<":"<<itick; // W:T
4410  if(itick < 10) { myprt<<" "; }
4411  if(itick < 100) { myprt<<" "; }
4412  if(itick < 1000) { myprt<<" "; }
4413  myprt<<std::setw(6)<<std::setprecision(2)<<tp1.Ang;
4414  myprt<<std::setw(2)<<tp1.AngleCode;
4415  if(aTj.StopFlag[1][kBragg]) {
4416  myprt<<"B";
4417  } else if(aTj.StopFlag[1][kAtVtx]) {
4418  myprt<<"V";
4419  } else {
4420  myprt<<" ";
4421  }
4422  myprt<<std::setw(5)<<(int)tp1.AveChg;
4423  // Print the fraction of points in the second half that are NearInShower
4424  frac = 0;
4425  cnt = 0;
4426  for(unsigned short ipt = midPt; ipt <= aTj.EndPt[1]; ++ipt) {
4427  auto& tp = aTj.Pts[ipt];
4428 // if(tp.Environment[kEnvNearShower]) ++frac;
4429  if(tp.Environment[kEnvNearTj]) ++frac;
4430  ++cnt;
4431  } // ipt
4432  if(cnt > 0) frac /= cnt;
4433  myprt<<std::setw(5)<<std::setprecision(1)<<frac;
4434 /*
4435  prec = 1;
4436  if(aTj.dEdx[1] > 99) prec = 0;
4437  myprt<<std::setw(5)<<std::setprecision(prec)<<aTj.dEdx[1];
4438 */
4439  myprt<<std::setw(7)<<std::setprecision(1)<<aTj.TotChg/1000;
4440  myprt<<std::setw(7)<<std::setprecision(2)<<aTj.ChgRMS;
4441  myprt<<std::setw(5)<<aTj.MCSMom;
4442  myprt<<std::setw(4)<<aTj.StepDir;
4443  myprt<<std::setw(4)<<aTj.VtxID[0];
4444  myprt<<std::setw(4)<<aTj.VtxID[1];
4445  myprt<<std::setw(5)<<aTj.PDGCode;
4446  myprt<<std::setw(7)<<std::setprecision(1)<<aTj.DirFOM;
4447  myprt<<std::setw(5)<<aTj.ParentID;
4448  myprt<<std::setw(5)<<PrimaryID(tjs, aTj);
4449  myprt<<std::setw(6)<<NeutrinoPrimaryTjID(tjs, aTj);
4450  int truKE = 0;
4451  int pdg = 0;
4452  if(aTj.MCPartListIndex < tjs.MCPartList.size()) {
4453  auto& mcp = tjs.MCPartList[aTj.MCPartListIndex];
4454  truKE = 1000 * (mcp->E() - mcp->Mass());
4455  pdg = mcp->PdgCode();
4456  }
4457  myprt<<std::setw(6)<<pdg;
4458  myprt<<std::setw(6)<<std::setprecision(2)<<aTj.EffPur;
4459  myprt<<std::setw(5)<<truKE;
4460  myprt<<std::setw(7)<<aTj.WorkID;
4461  for(unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) if(aTj.AlgMod[ib]) myprt<<" "<<AlgBitNames[ib];
4462  myprt<<"\n";
4463  } // ii
4464  return;
4465  } // itj > tjs.allTraj.size()-1
4466 
4467  if(itj > tjs.allTraj.size()-1) return;
4468 
4469  auto const& aTj = tjs.allTraj[itj];
4470 
4471  mf::LogVerbatim("TC")<<"Print tjs.allTraj["<<itj<<"]: ClusterIndex "<<aTj.ClusterIndex<<" Vtx[0] "<<aTj.VtxID[0]<<" Vtx[1] "<<aTj.VtxID[1];
4472  myprt<<"AlgBits";
4473  for(unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) if(aTj.AlgMod[ib]) myprt<<" "<<AlgBitNames[ib];
4474  myprt<<"\n";
4475 
4476  PrintHeader(someText);
4477  if(ipt == USHRT_MAX) {
4478  // print all points
4479  for(unsigned short ii = 0; ii < aTj.Pts.size(); ++ii) PrintTrajPoint(someText, tjs, ii, aTj.StepDir, aTj.Pass, aTj.Pts[ii]);
4480  } else {
4481  // print just one
4482  PrintTrajPoint(someText, tjs, ipt, aTj.StepDir, aTj.Pass, aTj.Pts[ipt]);
4483  }
4484  } // PrintAllTraj
4485 
4486 
4488  void PrintTrajectory(std::string someText, const TjStuff& tjs, const Trajectory& tj, unsigned short tPoint)
4489  {
4490  // prints one or all trajectory points on tj
4491 
4492  int trupdg = 0;
4493  if(tj.MCPartListIndex < tjs.MCPartList.size()) trupdg = tjs.MCPartList[tj.MCPartListIndex]->PdgCode();
4494 
4495  if(tPoint == USHRT_MAX) {
4496  if(tj.ID < 0) {
4497  mf::LogVerbatim myprt("TC");
4498  myprt<<someText<<" ";
4499  myprt<<"Work: ID "<<tj.ID<<" CTP "<<tj.CTP<<" StepDir "<<tj.StepDir<<" PDG "<<tj.PDGCode<<" TruPDG "<<trupdg<<" tjs.vtx "<<tj.VtxID[0]<<" "<<tj.VtxID[1]<<" nPts "<<tj.Pts.size()<<" EndPts "<<tj.EndPt[0]<<" "<<tj.EndPt[1];
4500  myprt<<" MCSMom "<<tj.MCSMom;
4501  myprt<<" StopFlags "<<PrintStopFlag(tj, 0)<<" "<<PrintStopFlag(tj, 1);
4502  myprt<<" AlgMod names:";
4503  for(unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) if(tj.AlgMod[ib]) myprt<<" "<<AlgBitNames[ib];
4504  } else {
4505  mf::LogVerbatim myprt("TC");
4506  myprt<<someText<<" ";
4507  myprt<<"tjs.allTraj: ID "<<tj.ID<<" WorkID "<<tj.WorkID<<" StepDir "<<tj.StepDir<<" PDG "<<tj.PDGCode<<" TruPDG "<<trupdg<<" tjs.vtx "<<tj.VtxID[0]<<" "<<tj.VtxID[1]<<" nPts "<<tj.Pts.size()<<" EndPts "<<tj.EndPt[0]<<" "<<tj.EndPt[1];
4508  myprt<<" MCSMom "<<tj.MCSMom;
4509  myprt<<" StopFlags "<<PrintStopFlag(tj, 0)<<" "<<PrintStopFlag(tj, 1);
4510  myprt<<" AlgMod names:";
4511  for(unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) if(tj.AlgMod[ib]) myprt<<" "<<AlgBitNames[ib];
4512  }
4513  PrintHeader(someText);
4514  for(unsigned short ipt = 0; ipt < tj.Pts.size(); ++ipt) PrintTrajPoint(someText, tjs, ipt, tj.StepDir, tj.Pass, tj.Pts[ipt]);
4515  // See if this trajectory is a shower Tj
4516  if(tj.AlgMod[kShowerTj]) {
4517  for(unsigned short ic = 0; ic < tjs.cots.size(); ++ic) {
4518  if(tjs.cots[ic].TjIDs.empty()) continue;
4519  // only print out the info for the correct Tj
4520  if(tjs.cots[ic].ShowerTjID != tj.ID) continue;
4521  const ShowerStruct& ss = tjs.cots[ic];
4522  mf::LogVerbatim myprt("TC");
4523  myprt<<"cots index "<<ic<<" ";
4524  myprt<<someText<<" Envelope";
4525  if(ss.Envelope.empty()) {
4526  myprt<<" NA";
4527  } else {
4528  for(auto& vtx : ss.Envelope) myprt<<" "<<(int)vtx[0]<<":"<<(int)(vtx[1]/tjs.UnitsPerTick);
4529  }
4530  myprt<<" Energy "<<(int)ss.Energy;
4531  myprt<<" Area "<<std::fixed<<std::setprecision(1)<<(int)ss.EnvelopeArea<<" ChgDensity "<<ss.ChgDensity;
4532  myprt<<"\nInShower TjIDs";
4533  for(auto& tjID : ss.TjIDs) {
4534  myprt<<" "<<tjID;
4535  } // tjIDA_Klystron_4U
4536 
4537  myprt<<"\n";
4538  myprt<<"NearTjIDs";
4539  for(auto& tjID : ss.NearTjIDs) {
4540  myprt<<" "<<tjID;
4541  } // tjID
4542  myprt<<"\n";
4543  myprt<<"\n";
4544  myprt<<"Angle "<<std::fixed<<std::setprecision(2)<<ss.Angle<<" +/- "<<ss.AngleErr;
4545  myprt<<" AspectRatio "<<std::fixed<<std::setprecision(2)<<ss.AspectRatio;
4546  myprt<<" DirectionFOM "<<std::fixed<<std::setprecision(2)<<ss.DirectionFOM;
4547  if(ss.ParentID > 0) {
4548  myprt<<" Parent Tj "<<ss.ParentID<<" FOM "<<ss.ParentFOM;
4549  } else {
4550  myprt<<" No parent";
4551  }
4552  myprt<<" TruParentID "<<ss.TruParentID<<" SS3ID "<<ss.SS3ID<<"\n";
4553  if(ss.NeedsUpdate) myprt<<"*********** This shower needs to be updated ***********";
4554  myprt<<"................................................";
4555  } // ic
4556  } // Shower Tj
4557  } else {
4558  // just print one traj point
4559  if(tPoint > tj.Pts.size() -1) {
4560  mf::LogVerbatim("TC")<<"Can't print non-existent traj point "<<tPoint;
4561  return;
4562  }
4563  PrintTrajPoint(someText, tjs, tPoint, tj.StepDir, tj.Pass, tj.Pts[tPoint]);
4564  }
4565  } // PrintTrajectory
4566 
4568  void PrintHeader(std::string someText)
4569  {
4570  mf::LogVerbatim("TC")<<someText<<" TRP CTP Ind Stp W:Tick Delta RMS Ang C Err Dir0 Dir1 Q AveQ Pull FitChi NTPF Hits ";
4571  } // PrintHeader
4572 
4574  void PrintTrajPoint(std::string someText, const TjStuff& tjs, unsigned short ipt, short dir, unsigned short pass, TrajPoint const& tp)
4575  {
4576  mf::LogVerbatim myprt("TC");
4577  myprt<<someText<<" TRP"<<std::fixed;
4578  myprt<<pass;
4579  if(dir > 0) { myprt<<"+"; } else { myprt<<"-"; }
4580  myprt<<std::setw(6)<<tp.CTP;
4581  myprt<<std::setw(5)<<ipt;
4582  myprt<<std::setw(5)<<tp.Step;
4583  myprt<<std::setw(7)<<std::setprecision(1)<<tp.Pos[0]<<":"<<tp.Pos[1]/tjs.UnitsPerTick; // W:T
4584  if(tp.Pos[1] < 10) { myprt<<" "; }
4585  if(tp.Pos[1] < 100) { myprt<<" "; }
4586  if(tp.Pos[1] < 1000) { myprt<<" "; }
4587  myprt<<std::setw(6)<<std::setprecision(2)<<tp.Delta;
4588  myprt<<std::setw(6)<<std::setprecision(2)<<tp.DeltaRMS;
4589  myprt<<std::setw(6)<<std::setprecision(2)<<tp.Ang;
4590  myprt<<std::setw(2)<<tp.AngleCode;
4591  myprt<<std::setw(6)<<std::setprecision(2)<<tp.AngErr;
4592  myprt<<std::setw(6)<<std::setprecision(2)<<tp.Dir[0];
4593  myprt<<std::setw(6)<<std::setprecision(2)<<tp.Dir[1];
4594  myprt<<std::setw(7)<<(int)tp.Chg;
4595  myprt<<std::setw(8)<<(int)tp.AveChg;
4596  myprt<<std::setw(6)<<std::setprecision(1)<<tp.ChgPull;
4597  myprt<<std::setw(7)<<tp.FitChi;
4598  myprt<<std::setw(6)<<tp.NTPsFit;
4599  // print the hits associated with this traj point
4600  if(tp.Hits.size() > 16) {
4601  // don't print too many hits (e.g. from a shower Tj)
4602  myprt<<" "<<tp.Hits.size()<<" shower hits";
4603  } else {
4604  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
4605  unsigned int iht = tp.Hits[ii];
4606  myprt<<" "<<tjs.fHits[iht].ArtPtr->WireID().Wire<<":"<<(int)tjs.fHits[iht].PeakTime;
4607  if(tp.UseHit[ii]) {
4608  // Distinguish used hits from nearby hits
4609  myprt<<"_";
4610  } else {
4611  myprt<<"x";
4612  }
4613  myprt<<tjs.fHits[iht].InTraj;
4614  } // iht
4615  }
4616  } // PrintTrajPoint
4617 
4619  void PrintPFP(std::string someText, const TjStuff& tjs, const PFPStruct& pfp, bool printHeader)
4620  {
4621 // if(pfp.ID == 0) return;
4622  mf::LogVerbatim myprt("TC");
4623  if(printHeader) {
4624  myprt<<someText;
4625  myprt<<" PFP sVx ________sPos_______ CS _______sDir______ ____sdEdx_____ eVx ________ePos_______ CS _______eDir______ ____edEdx____ Len nTp3 MCSMom ShLike? PDG mcpIndx Par Prim E*P\n";
4626  }
4627  myprt<<someText;
4628  std::string pid = "P" + std::to_string(pfp.ID);
4629  myprt<<std::setw(5)<<pid;
4630  // start and end stuff
4631  for(unsigned short startend = 0; startend < 2; ++startend) {
4632  myprt<<std::setw(4)<<pfp.Vx3ID[startend];
4633  myprt<<std::fixed<<std::right<<std::setprecision(1);
4634  myprt<<std::setw(7)<<pfp.XYZ[startend][0];
4635  myprt<<std::setw(7)<<pfp.XYZ[startend][1];
4636  myprt<<std::setw(7)<<pfp.XYZ[startend][2];
4637  // print character for Outside or Inside the FV
4638  if(pfp.StopFlag[startend][kOutFV]) {
4639  myprt<<" O";
4640  } else {
4641  myprt<<" I";
4642  }
4643  myprt<<std::fixed<<std::right<<std::setprecision(2);
4644  myprt<<std::setw(6)<<pfp.Dir[startend][0];
4645  myprt<<std::setw(6)<<pfp.Dir[startend][1];
4646  myprt<<std::setw(6)<<pfp.Dir[startend][2];
4647  for(auto& dedx : pfp.dEdx[startend]) {
4648  if(dedx < 50) {
4649  myprt<<std::setw(5)<<std::setprecision(1)<<dedx;
4650  } else {
4651  myprt<<std::setw(5)<<std::setprecision(0)<<dedx;
4652  }
4653  } // dedx
4654  if (pfp.dEdx[startend].size()<3){
4655  for(size_t i = 0; i<3-pfp.dEdx[startend].size(); ++i){
4656  myprt<<std::setw(6)<<' ';
4657  }
4658  }
4659  } // startend
4660  // global stuff
4661 // myprt<<std::setw(5)<<pfp.BestPlane;
4662  float length = PosSep(pfp.XYZ[0], pfp.XYZ[1]);
4663  if(length < 100) {
4664  myprt<<std::setw(5)<<std::setprecision(1)<<length;
4665  } else {
4666  myprt<<std::setw(5)<<std::setprecision(0)<<length;
4667  }
4668  myprt<<std::setw(5)<<std::setprecision(2)<<pfp.Tp3s.size();
4669  myprt<<std::setw(7)<<MCSMom(tjs, pfp.TjIDs);
4670  myprt<<std::setw(5)<<IsShowerLike(tjs, pfp.TjIDs);
4671  myprt<<std::setw(5)<<pfp.PDGCode;
4672  if(pfp.MCPartListIndex < tjs.MCPartList.size()) {
4673  myprt<<std::setw(8)<<pfp.MCPartListIndex;
4674  } else {
4675  myprt<<" NA";
4676  }
4677  myprt<<std::setw(4)<<pfp.ParentID;
4678  myprt<<std::setw(5)<<PrimaryID(tjs, pfp);
4679  myprt<<std::setw(5)<<std::setprecision(2)<<pfp.EffPur;
4680  if(!pfp.TjIDs.empty()) {
4681  for(auto& tjID : pfp.TjIDs) myprt<<" T"<<tjID;
4682  }
4683  if(!pfp.DtrIDs.empty()) {
4684  myprt<<" dtrs";
4685  for(auto& dtrID : pfp.DtrIDs) myprt<<" P"<<dtrID;
4686  }
4687  } // PrintPFP
4688 
4690  void PrintPFPs(std::string someText, const TjStuff& tjs)
4691  {
4692  if(tjs.pfps.empty()) return;
4693 
4694  mf::LogVerbatim myprt("TC");
4695  myprt<<someText;
4696  myprt<<" PFP sVx ________sPos_______ ______sDir______ ______sdEdx_____ eVx ________ePos_______ ______eDir______ ______edEdx_____ BstPln PDG TruPDG Par Prim E*P\n";
4697  bool printHeader = true;
4698  for(auto& pfp : tjs.pfps) {
4699 // if(pfp.ID == 0) continue;
4700  PrintPFP(someText, tjs, pfp, printHeader);
4701  printHeader = false;
4702  } // im
4703 
4704  } // PrintPFPs
4705 
4707  std::string PrintStopFlag(const Trajectory& tj, unsigned short end)
4708  {
4709  if(end > 1) return "Invalid end";
4710  std::string tmp;
4711  bool first = true;
4712  for(unsigned short ib = 0; ib < StopFlagNames.size(); ++ib) {
4713  if(tj.StopFlag[end][ib]) {
4714  if(first) {
4715  tmp = std::to_string(end) + ":" + StopFlagNames[ib];
4716  first = false;
4717  } else {
4718  tmp += "," + StopFlagNames[ib];
4719  }
4720  }
4721  } // ib
4722  return tmp;
4723  } // PrintStopFlag
4724 
4726  std::string PrintHitShort(const TCHit& hit)
4727  {
4728  return std::to_string(hit.ArtPtr->WireID().Plane) + ":" + std::to_string(hit.ArtPtr->WireID().Wire) + ":" + std::to_string((int)hit.PeakTime);
4729  } // PrintHit
4730 
4732  std::string PrintHit(const TCHit& hit)
4733  {
4734  return std::to_string(hit.ArtPtr->WireID().Plane) + ":" + std::to_string(hit.ArtPtr->WireID().Wire) + ":" + std::to_string((int)hit.PeakTime) + "_" + std::to_string(hit.InTraj);
4735  } // PrintHit
4736 
4738  std::string PrintPos(const TjStuff& tjs, const TrajPoint& tp)
4739  {
4740  return std::to_string(tp.CTP) + ":" + PrintPos(tjs, tp.Pos);
4741  } // PrintPos
4742 
4744  std::string PrintPos(const TjStuff& tjs, const Point2_t& pos)
4745  {
4746  unsigned int wire = 0;
4747  if(pos[0] > -0.4) wire = std::nearbyint(pos[0]);
4748  int time = std::nearbyint(pos[1]/tjs.UnitsPerTick);
4749  return std::to_string(wire) + ":" + std::to_string(time);
4750  } // PrintPos
4751 
4752 
4753 } // namespace tca
4754 
Float_t x
Definition: compare.C:6
std::bitset< 16 > UseHit
Definition: DataStructs.h:163
geo::Length_t WireCoordinate(double YPos, double ZPos, geo::PlaneID const &planeid) const
Returns the index of the nearest wire to the specified position.
float AveChg
Calculated using ALL hits.
Definition: DataStructs.h:174
Point2_t Pos
Definition: DataStructs.h:147
unsigned short MatchVecIndex(const TjStuff &tjs, int tjID)
Definition: Utils.cxx:1058
Float_t s
Definition: plot.C:23
MaybeLogger_< ELseverityLevel::ELsev_info, true > LogVerbatim
Point2_t dEdx
dE/dx for 3D matched trajectories
Definition: DataStructs.h:180
std::array< std::vector< float >, 2 > dEdxErr
Definition: DataStructs.h:245
Functions to help with numbers.
std::vector< float > Vertex2DCuts
Max position pull, max Position error rms.
Definition: DataStructs.h:509
void ChkChgAsymmetry(TjStuff &tjs, Trajectory &tj, bool prt)
Definition: Utils.cxx:1436
float TPHitsRMSTime(TjStuff &tjs, TrajPoint &tp, HitStatus_t hitRequest)
Definition: Utils.cxx:3576
constexpr auto const & right(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:112
Double_t xx
Definition: macro.C:12
geo::Length_t DetHalfWidth(geo::TPCID const &tpcid) const
Returns the half width of the active volume of the specified TPC.
struct of temporary 2D vertices (end points)
Definition: DataStructs.h:83
void PrintTrajectory(std::string someText, const TjStuff &tjs, const Trajectory &tj, unsigned short tPoint)
Definition: Utils.cxx:4488
int PrimaryID(const TjStuff &tjs, const PFPStruct &pfp)
Definition: Utils.cxx:404
float OverlapFraction(TjStuff &tjs, const Trajectory &tj1, const Trajectory &tj2)
Definition: Utils.cxx:584
const std::vector< std::string > AlgBitNames
Definition: DataStructs.cxx:4
CTP_t CTP
Cryostat, TPC, Plane code.
Definition: DataStructs.h:145
Float_t E
Definition: plot.C:23
unsigned short NTPsFit
Definition: DataStructs.h:159
float HitSep2(TjStuff &tjs, unsigned int iht, unsigned int jht)
Definition: Utils.cxx:2110
art::Ptr< recob::Hit > ArtPtr
Definition: DataStructs.h:206
std::vector< int > NearTjIDs
Definition: DataStructs.h:276
std::array< double, 3 > Point3_t
Definition: DataStructs.h:35
std::bitset< 64 > AlgMod
Bit set if algorithm AlgBit_t modifed the trajectory.
Definition: DataStructs.h:171
unsigned int index
Definition: TCShower.cxx:5
void UpdateVxEnvironment(std::string inFcnLabel, TjStuff &tjs, VtxStore &vx2, bool prt)
Definition: Utils.cxx:3297
float PosSep(const Point2_t &pos1, const Point2_t &pos2)
Definition: Utils.cxx:2220
void RestoreObsoleteTrajectory(TjStuff &tjs, unsigned int itj)
Definition: Utils.cxx:1808
std::vector< Point2_t > Envelope
Definition: DataStructs.h:282
void PrintAllTraj(std::string someText, const TjStuff &tjs, const DebugStuff &debug, unsigned short itj, unsigned short ipt, bool prtVtx)
Definition: Utils.cxx:4232
bool HasDuplicateHits(TjStuff const &tjs, Trajectory const &tj, bool prt)
Definition: Utils.cxx:2335
unsigned short Step
Definition: DataStructs.h:160
geo::WireID WireID() const
Initial tdc tick for hit.
Definition: Hit.h:234
unsigned int Nplanes() const
Number of planes in this tpc.
Definition: TPCGeo.h:145
Float_t den
Definition: plot.C:37
std::vector< unsigned int > PutTrajHitsInVector(Trajectory const &tj, HitStatus_t hitRequest)
Definition: Utils.cxx:2283
void TagJunkTj(TjStuff const &tjs, Trajectory &tj, bool prt)
Definition: Utils.cxx:2309
vertex position fixed manually - no fitting done
Definition: DataStructs.h:102
std::vector< float > AveHitRMS
average RMS of an isolated hit
Definition: DataStructs.h:483
unsigned short NearestPtWithChg(TjStuff &tjs, Trajectory &tj, unsigned short thePt)
Definition: Utils.cxx:2889
unsigned short PDGCodeIndex(TjStuff &tjs, int PDGCode)
Definition: Utils.cxx:1783
Float_t y
Definition: compare.C:6
std::vector< float > MaxPos1
Definition: DataStructs.h:472
Int_t B
Definition: plot.C:25
float TpSumHitChg(TjStuff &tjs, TrajPoint const &tp)
Definition: Utils.cxx:1678
unsigned short NumDeltaRays(const TjStuff &tjs, std::vector< int > &tjIDs)
Definition: Utils.cxx:348
std::array< std::bitset< 8 >, 2 > StopFlag
Definition: DataStructs.h:190
The data type to uniquely identify a Plane.
Definition: geo_types.h:250
Geometry information for a single TPC.
Definition: TPCGeo.h:37
std::vector< PFPStruct > pfps
Definition: DataStructs.h:505
int ParentID
ID of the parent, or the ID of the Tj this one was merged with if it is killed.
Definition: DataStructs.h:173
std::vector< unsigned int > Hits
Definition: DataStructs.h:162
std::vector< int > TjIDs
Definition: DataStructs.h:275
float TotChg
Total including an estimate for dead wires.
Definition: DataStructs.h:175
Float_t ss
Definition: plot.C:23
float DeadWireCount(const TjStuff &tjs, const float &inWirePos1, const float &inWirePos2, CTP_t tCTP)
Definition: Utils.cxx:1762
unsigned short Pass
Definition: DataStructs.h:87
void FindAlongTrans(Point2_t pos1, Vector2_t dir1, Point2_t pos2, Point2_t &alongTrans)
Definition: Utils.cxx:2741
unsigned short FarEnd(const TjStuff &tjs, const Trajectory &tj, const Point2_t &pos)
Definition: Utils.cxx:3554
float HitsRMSTime(TjStuff &tjs, const std::vector< unsigned int > &hitsInMultiplet, HitStatus_t hitRequest)
Definition: Utils.cxx:3607
virtual double SamplingRate() const =0
Returns the period of the TPC readout electronics clock.
float HitsRMSTick(TjStuff &tjs, const std::vector< unsigned int > &hitsInMultiplet, HitStatus_t hitRequest)
Definition: Utils.cxx:3613
CryostatID_t Cryostat
Index of cryostat.
Definition: geo_types.h:130
void TrimEndPts(std::string fcnLabel, TjStuff &tjs, Trajectory &tj, const std::vector< float > &fQualityCuts, bool prt)
Definition: Utils.cxx:1342
bool TrajClosestApproach(Trajectory const &tj, float x, float y, unsigned short &closePt, float &DOCA)
Definition: Utils.cxx:2244
float TrajPointSeparation(TrajPoint &tp1, TrajPoint &tp2)
Definition: Utils.cxx:2235
float length
Definition: TCShower.cxx:6
WireID_t Wire
Index of the wire within its plane.
Definition: geo_types.h:313
std::vector< ShowerStruct3D > showers
Definition: DataStructs.h:508
void FitTraj(TjStuff &tjs, Trajectory &tj, unsigned short originPt, unsigned short npts, short fitDir, TrajPoint &tpFit)
Definition: Utils.cxx:686
Float_t tmp
Definition: plot.C:37
void SetAngleCode(TjStuff &tjs, TrajPoint &tp)
Definition: Utils.cxx:642
bool SetMag(Vector2_t &v1, double mag)
Definition: Utils.cxx:2729
MaybeLogger_< ELseverityLevel::ELsev_error, false > LogError
unsigned short NumPtsWithCharge(const TjStuff &tjs, const Trajectory &tj, bool includeDeadWires, unsigned short firstPt, unsigned short lastPt)
Definition: Utils.cxx:1746
std::array< std::bitset< 8 >, 2 > StopFlag
Definition: DataStructs.h:258
bool MergeTjIntoPFP(TjStuff &tjs, int mtjid, PFPStruct &pfp, bool prt)
Definition: Utils.cxx:428
unsigned short NumPlanes
Definition: DataStructs.h:475
HitStatus_t
Definition: Utils.h:37
std::array< int, 2 > Vx3ID
Definition: DataStructs.h:246
std::array< double, 2 > Dir
Definition: DataStructs.h:148
geo::Length_t WirePitch(geo::PlaneID const &planeid) const
Returns the distance between two consecutive wires.
void MergeGhostTjs(TjStuff &tjs, CTP_t inCTP)
Definition: Utils.cxx:1830
bool MakeBareTrajPoint(const TjStuff &tjs, const TrajPoint &tpIn1, const TrajPoint &tpIn2, TrajPoint &tpOut)
Definition: Utils.cxx:3536
std::vector< TrajPoint3 > Tp3s
Definition: DataStructs.h:239
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.
double DeltaAngle2(double Ang1, double Ang2)
Definition: Utils.cxx:2772
std::vector< T > SetIntersection(const std::vector< T > &set1, const std::vector< T > &set2)
Definition: Utils.h:214
bool SignalAtTp(TjStuff &tjs, const TrajPoint &tp)
Definition: Utils.cxx:1639
virtual double ConvertXToTicks(double X, int p, int t, int c) const =0
unsigned short Pass
the pass on which it was created
Definition: DataStructs.h:187
void TagDeltaRays(TjStuff &tjs, const CTP_t &inCTP)
Definition: Utils.cxx:2985
void CheckTrajBeginChg(TjStuff &tjs, unsigned short itj, bool prt)
Definition: Utils.cxx:1272
bool TrajIsClean(TjStuff &tjs, Trajectory &tj, bool prt)
Definition: Utils.cxx:2814
void ReleaseHits(TjStuff &tjs, Trajectory &tj)
Definition: Utils.cxx:1070
void ReverseTraj(TjStuff &tjs, Trajectory &tj)
Definition: Utils.cxx:2666
bool IsShowerLike(const TjStuff &tjs, const std::vector< int > TjIDs)
Definition: TCShower.cxx:2071
float ChgFracBetween(TjStuff &tjs, TrajPoint tp, float toPos0, bool prt)
Definition: Utils.cxx:1527
tagged as a vertex between Tjs that are matched to MC truth neutrino interaction particles ...
Definition: DataStructs.h:105
unsigned int Hit
set to the hit index in fHits if a Plane:Wire:Tick match is found
Definition: DebugStruct.h:33
std::vector< ShowerStruct > cots
Definition: DataStructs.h:506
struct of temporary 3D vertices
Definition: DataStructs.h:111
bool CompatibleMerge(TjStuff &tjs, const Trajectory &tj1, const Trajectory &tj2, bool prt)
Definition: Utils.cxx:526
bool CheckWireHitRange(const TjStuff &tjs)
Definition: Utils.cxx:3855
geo::Length_t DetHalfHeight(geo::TPCID const &tpcid) const
Returns the half height of the active volume of the specified TPC.
TCanvas * c1
Definition: plotHisto.C:7
bool MakeVertexObsolete(TjStuff &tjs, VtxStore &vx2, bool forceKill)
Definition: TCVertex.cxx:2917
TCanvas * c2
Definition: plot_hist.C:75
std::array< float, 2 > Point2_t
Definition: DataStructs.h:37
int PDGCodeVote(TjStuff &tjs, std::vector< int > &tjIDs, bool prt)
Definition: Utils.cxx:267
std::array< Point3_t, 2 > XYZ
Definition: DataStructs.h:241
const geo::GeometryCore * geom
Definition: DataStructs.h:526
geo::TPCID TPCID
Definition: DataStructs.h:120
float PointTrajDOCA2(TjStuff const &tjs, float wire, float time, TrajPoint const &tp)
Definition: Utils.cxx:2153
DebugStuff debug
Definition: DebugStruct.cxx:4
CTP_t CTP
Cryostat, TPC, Plane code.
Definition: DataStructs.h:170
float HitsPosTime(TjStuff &tjs, const std::vector< unsigned int > &hitsInMultiplet, float &sum, HitStatus_t hitRequest)
Definition: Utils.cxx:3639
void UpdateTjChgProperties(std::string inFcnLabel, TjStuff &tjs, Trajectory &tj, bool prt)
Definition: Utils.cxx:3118
std::vector< unsigned int > LastWire
the last wire with a hit
Definition: DataStructs.h:474
std::array< Vector3_t, 2 > DirErr
Definition: DataStructs.h:243
std::vector< VtxStore > vtx
2D vertices
Definition: DataStructs.h:502
std::tuple< double, double, const reco::ClusterHit3D * > Point
Definitions used by the VoronoiDiagram algorithm.
Definition: DCEL.h:34
const std::vector< std::string > StopFlagNames
Definition: DataStructs.cxx:71
void UpdateMatchStructs(TjStuff &tjs, int oldTj, int newTj)
Definition: PFPUtils.cxx:16
std::vector< TrajPoint > Pts
Trajectory points.
Definition: DataStructs.h:169
short MCSMom(TjStuff &tjs, Trajectory &tj, unsigned short firstPt, unsigned short lastPt)
Definition: Utils.cxx:2859
TrajPoint MakeBareTP(TjStuff &tjs, Point3_t &pos, Vector3_t &dir, CTP_t inCTP)
Definition: Utils.cxx:3435
virtual double Temperature() const =0
bool TrajTrajDOCA(const TjStuff &tjs, const Trajectory &tj1, const Trajectory &tj2, unsigned short &ipt1, unsigned short &ipt2, float &minSep, bool considerDeadWires)
Definition: Utils.cxx:2052
int Plane
Select plane.
Definition: DebugStruct.h:29
std::array< Vector3_t, 2 > Dir
Definition: DataStructs.h:242
float MaxTjLen(TjStuff const &tjs, std::vector< int > &tjIDs)
Definition: Utils.cxx:2189
unsigned short CloseEnd(TjStuff &tjs, const Trajectory &tj, const Point2_t &pos)
Definition: Utils.cxx:2120
std::vector< int > FindCloseTjs(const TjStuff &tjs, const TrajPoint &fromTp, const TrajPoint &toTp, const float &maxDelta)
Definition: Utils.cxx:2466
float TrajLength(Trajectory &tj)
Definition: Utils.cxx:2204
std::vector< Trajectory > allTraj
vector of all trajectories in each plane
Definition: DataStructs.h:493
std::bitset< 64 > UseAlg
Allow user to mask off specific algorithms.
Definition: DataStructs.h:525
std::array< unsigned short, 2 > EndPt
First and last point in the trajectory that has charge.
Definition: DataStructs.h:182
unsigned short PDGCode
shower-like or track-like {default is track-like}
Definition: DataStructs.h:185
bool PointInsideEnvelope(const Point2_t &Point, const std::vector< Point2_t > &Envelope)
Definition: Utils.cxx:2705
std::vector< float > MaxPos0
Definition: DataStructs.h:471
geo::Length_t DetLength(geo::TPCID const &tpcid) const
Returns the length of the active volume of the specified TPC.
void DefineTjParents(TjStuff &tjs, const geo::TPCID &tpcid, bool prt)
Definition: Utils.cxx:17
std::vector< unsigned int > FirstWire
the first wire with a hit
Definition: DataStructs.h:473
Point2_t Pos
Definition: DataStructs.h:84
std::vector< MatchStruct > matchVec
3D matching vector
Definition: DataStructs.h:504
bool TestBeam
Expect tracks entering from the front face. Don&#39;t create neutrino PFParticles.
Definition: DataStructs.h:534
std::string PrintHit(const TCHit &hit)
Definition: Utils.cxx:4732
float UnitsPerTick
scale factor from Tick to WSE equivalent units
Definition: DataStructs.h:468
virtual unsigned int NumberTimeSamples() const =0
The data type to uniquely identify a TPC.
Definition: geo_types.h:195
PlaneID_t Plane
Index of the plane within its TPC.
Definition: geo_types.h:258
float EffPur
Efficiency * Purity.
Definition: DataStructs.h:253
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:38
geo::TPCID TPCID
Definition: DataStructs.h:469
unsigned short NumHitsInTP(const TrajPoint &tp, HitStatus_t hitRequest)
Definition: Utils.cxx:3677
void TagMuonDirections(TjStuff &tjs, short debugWorkID)
Definition: Utils.cxx:3078
std::vector< int > GetVtxTjIDs(const TjStuff &tjs, const VtxStore &vx2)
Definition: TCVertex.cxx:3022
std::array< unsigned short, 2 > VtxID
ID of 2D vertex.
Definition: DataStructs.h:181
Detector simulation of raw signals on wires.
void TjDeltaRMS(TjStuff &tjs, Trajectory &tj, unsigned short firstPt, unsigned short lastPt, double &rms, unsigned short &cnt)
Definition: Utils.cxx:2951
void WatchHit(std::string someText, TjStuff &tjs, const unsigned int &wHit, short &wInTraj, const unsigned short &tjID)
Definition: Utils.cxx:984
std::vector< int > DtrIDs
Definition: DataStructs.h:250
float PosSep2(const Point2_t &pos1, const Point2_t &pos2)
Definition: Utils.cxx:2226
std::vector< Vtx3Store > vtx3
3D vertices
Definition: DataStructs.h:503
float ChgFracNearPos(TjStuff &tjs, const Point2_t &pos, const std::vector< int > &tjIDs)
Definition: Utils.cxx:2614
float TwoTPAngle(TrajPoint &tp1, TrajPoint &tp2)
Definition: Utils.cxx:2274
bool MergeShowerTjsAndStore(TjStuff &tjs, unsigned short istj, unsigned short jstj, bool prt)
Definition: TCShower.cxx:3128
unsigned int MCPartListIndex
Definition: DataStructs.h:189
std::bitset< 16 > Stat
Vertex status bits using kVtxBit_t.
Definition: DataStructs.h:97
int ID
set to 0 if killed
Definition: DataStructs.h:93
std::array< int, 3 > Vx2ID
Definition: DataStructs.h:121
unsigned short AngleCode
Definition: DataStructs.h:161
int Tick
Select hit PeakTime for debugging (< 0 for vertex finding)
Definition: DebugStruct.h:32
int TPC
Select TPC.
Definition: DebugStruct.h:28
float val
Definition: Utils.cxx:8
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:80
std::vector< short > MuonTag
min length and min MCSMom for a muon tag
Definition: DataStructs.h:514
float PointTrajDOCA(TjStuff const &tjs, float wire, float time, TrajPoint const &tp)
Definition: Utils.cxx:2147
std::string PrintPos(const TjStuff &tjs, const Point2_t &pos)
Definition: Utils.cxx:4744
double DotProd(const Vector3_t &v1, const Vector3_t &v2)
Definition: PFPUtils.h:60
unsigned int CTP_t
Definition: DataStructs.h:41
bool InTrajOK(TjStuff &tjs, std::string someText)
Definition: Utils.cxx:1199
std::vector< std::vector< std::pair< int, int > > > WireHitRange
Definition: DataStructs.h:499
int NeutrinoPrimaryTjID(const TjStuff &tjs, const Trajectory &tj)
Definition: Utils.cxx:363
bool FillWireHitRange(TjStuff &tjs, const geo::TPCID &tpcid)
Definition: Utils.cxx:3722
std::string to_string(Flag_t< Storage > const flag)
Convert a flag into a stream (shows its index).
Definition: BitMask.h:187
Float_t norm
std::vector< TCHit > fHits
Definition: DataStructs.h:495
unsigned int MCPartListIndex
Definition: DataStructs.h:254
PFPStruct CreatePFP(const TjStuff &tjs, const geo::TPCID &tpcid)
Definition: PFPUtils.cxx:1943
std::string PrintHitShort(const TCHit &hit)
Definition: Utils.cxx:4726
TDirectory * dir
Definition: macro.C:5
virtual double DriftVelocity(double efield=0., double temperature=0.) const =0
short StepDir
-1 = going US (-> small wire#), 1 = going DS (-> large wire#)
Definition: DataStructs.h:188
float WirePitch
Definition: DataStructs.h:482
unsigned short NTraj
Definition: DataStructs.h:86
geo::PlaneID DecodeCTP(CTP_t CTP)
Definition: DataStructs.cxx:89
std::vector< float > Match3DCuts
3D matching cuts
Definition: DataStructs.h:517
float ExpectedHitsRMS(TjStuff &tjs, const TrajPoint &tp)
Definition: Utils.cxx:1627
void Reverse3DMatchTjs(TjStuff &tjs, PFPStruct &pfp, bool prt)
Definition: Utils.cxx:996
Vector2_t PointDirection(const Point2_t p1, const Point2_t p2)
Definition: Utils.cxx:3563
bool SplitTraj(TjStuff &tjs, unsigned short itj, unsigned short pos, unsigned short ivx, bool prt)
Definition: Utils.cxx:1918
float MaxChargeAsymmetry(TjStuff &tjs, std::vector< int > &tjIDs)
Definition: Utils.cxx:235
std::vector< int > TjIDs
Definition: DataStructs.h:237
MaybeLogger_< ELseverityLevel::ELsev_warning, false > LogWarning
void SetVx2Score(TjStuff &tjs, bool prt)
Definition: TCVertex.cxx:2322
void SetEndPoints(TjStuff &tjs, Trajectory &tj)
Definition: Utils.cxx:2788
CTP_t EncodeCTP(unsigned int cryo, unsigned int tpc, unsigned int plane)
Definition: DataStructs.h:45
std::vector< simb::MCParticle * > MCPartList
Definition: DataStructs.h:520
bool MergeAndStore(TjStuff &tjs, unsigned int itj1, unsigned int itj2, bool doPrt)
Definition: Utils.cxx:3896
float PointTrajSep2(float wire, float time, TrajPoint const &tp)
Definition: Utils.cxx:2131
std::array< double, 3 > Vector3_t
Definition: DataStructs.h:36
bool SignalBetween(TjStuff &tjs, TrajPoint tp, float toPos0, const float &MinWireSignalFraction, bool prt)
Definition: Utils.cxx:1520
float MaxHitDelta(TjStuff &tjs, Trajectory &tj)
Definition: Utils.cxx:2649
bool WireHitRangeOK(const TjStuff &tjs, const CTP_t &inCTP)
Definition: Utils.cxx:3886
std::array< std::vector< float >, 2 > dEdx
Definition: DataStructs.h:244
unsigned short AngleRange(TjStuff &tjs, float angle)
Definition: Utils.cxx:659
float PeakTime
Definition: DataStructs.h:198
void PosInPlane(const TjStuff &tjs, const Vtx3Store &vx3, unsigned short plane, Point2_t &pos)
Definition: TCVertex.cxx:3083
unsigned short GetPFPIndex(const TjStuff &tjs, int tjID)
Definition: Utils.cxx:1045
std::string PrintStopFlag(const Trajectory &tj, unsigned short end)
Definition: Utils.cxx:4707
std::vector< short > DeltaRayTag
min length, min MCSMom and min separation (WSE) for a delta ray tag
Definition: DataStructs.h:513
std::vector< evd::details::RawDigitInfo_t >::const_iterator end(RawDigitCacheDataClass const &cache)
void MakeTrajectoryObsolete(TjStuff &tjs, unsigned int itj)
Definition: Utils.cxx:1797
TPCGeo const & TPC(unsigned int const tpc=0, unsigned int const cstat=0) const
Returns the specified TPC.
float TjDirFOM(const TjStuff &tjs, const Trajectory &tj, bool prt)
Definition: Utils.cxx:906
void PrintPFP(std::string someText, const TjStuff &tjs, const PFPStruct &pfp, bool printHeader)
Definition: Utils.cxx:4619
void PrintPFPs(std::string someText, const TjStuff &tjs)
Definition: Utils.cxx:4690
void UnsetUsedHits(TjStuff &tjs, TrajPoint &tp)
Definition: Utils.cxx:1082
bool FindCloseHits(TjStuff const &tjs, TrajPoint &tp, float const &maxDelta, HitStatus_t hitRequest)
Definition: Utils.cxx:2418
std::vector< float > AngleRanges
list of max angles for each angle range
Definition: DataStructs.h:500
float HitsPosTick(TjStuff &tjs, const std::vector< unsigned int > &hitsInMultiplet, float &sum, HitStatus_t hitRequest)
Definition: Utils.cxx:3645
void MoveTPToWire(TrajPoint &tp, float wire)
Definition: Utils.cxx:2351
bool StoreVertex(TjStuff &tjs, VtxStore &vx)
Definition: TCVertex.cxx:1911
std::vector< float > KinkCuts
kink angle, nPts fit, (alternate) kink angle significance
Definition: DataStructs.h:516
virtual double Efield(unsigned int planegap=0) const =0
Returns the nominal electric field in the specified volume.
unsigned int ChannelID_t
Type representing the ID of a readout channel.
Definition: RawTypes.h:27
TPCID_t TPC
Index of the TPC within its cryostat.
Definition: geo_types.h:203
void TrajIntersection(TrajPoint const &tp1, TrajPoint const &tp2, float &x, float &y)
Definition: Utils.cxx:2171
bool StoreTraj(TjStuff &tjs, Trajectory &tj)
Definition: Utils.cxx:1095
float TPHitsRMSTick(TjStuff &tjs, TrajPoint &tp, HitStatus_t hitRequest)
Definition: Utils.cxx:3582
bool valIncreasing(SortEntry c1, SortEntry c2)
Definition: Utils.cxx:12
void PrintTrajPoint(std::string someText, const TjStuff &tjs, unsigned short ipt, short dir, unsigned short pass, TrajPoint const &tp)
Definition: Utils.cxx:4574
double DeltaAngle(double Ang1, double Ang2)
Definition: Utils.cxx:2782
void PrintHeader(std::string someText)
Definition: Utils.cxx:4568
Float_t w
Definition: plot.C:23
bool NeedsUpdate
Set true when the Tj needs to be updated (only for the TP Environment right now)
Definition: DataStructs.h:191
void TrajPointTrajDOCA(TjStuff &tjs, TrajPoint const &tp, Trajectory const &tj, unsigned short &closePt, float &minSep)
Definition: Utils.cxx:2026
bool StorePFP(TjStuff &tjs, PFPStruct &pfp)
Definition: PFPUtils.cxx:2660
bool TrajHitsOK(TjStuff &tjs, const unsigned int iht, const unsigned int jht)
Definition: Utils.cxx:1603
double MCSThetaRMS(TjStuff &tjs, Trajectory &tj, unsigned short firstPt, unsigned short lastPt)
Definition: Utils.cxx:2921
Point2_t HitPos
Definition: DataStructs.h:146
bool DebugMode
print additional info when in debug mode
Definition: DataStructs.h:535
void LocalToWorld(const double *tpc, double *world) const
Transform point from local TPC frame to world frame.
Definition: TPCGeo.h:490
std::vector< int > GetAssns(const TjStuff &tjs, std::string type1Name, int id, std::string type2Name)
Definition: Utils.cxx:4079
std::vector< unsigned int > NumWires
Definition: DataStructs.h:470
bool valDecreasing(SortEntry c1, SortEntry c2)
Definition: Utils.cxx:11
short StepDir
the normal user-defined stepping direction = 1 (US -> DS) or -1 (DS -> US)
Definition: DataStructs.h:531
CTP_t CTP
set to an invalid CTP
Definition: DebugStruct.h:30
constexpr Point origin()
Returns a origin position with a point of the specified type.
Definition: geo_vectors.h:230
unsigned short NumUsedHitsInTj(const TjStuff &tjs, const Trajectory &tj)
Definition: Utils.cxx:3665
void SetPDGCode(TjStuff &tjs, Trajectory &tj)
Definition: Utils.cxx:3704
float DirFOM
Normalized RMS using ALL hits. Assume it is 50% to start.
Definition: DataStructs.h:177
const detinfo::DetectorProperties * detprop
Definition: DataStructs.h:527