LArSoft  v06_85_00
Liquid Argon Software toolkit - http://larsoft.org/
TCVertex.cxx
Go to the documentation of this file.
2 
3 namespace tca {
4 
5  struct SortEntry{
6  unsigned int index;
7  float val;
8  };
9 
10  bool valDecreasing (SortEntry c1, SortEntry c2) { return (c1.val > c2.val);}
11  bool valIncreasing (SortEntry c1, SortEntry c2) { return (c1.val < c2.val);}
12 
14  void MakeJunkVertices(TjStuff& tjs, const CTP_t& inCTP)
15  {
16  // Vertices between poorly reconstructed tjs (especially junk tjs) and normal
17  // tjs can fail because the junk tj trajectory parameters are inaccurate. This function
18  // uses proximity and not pointing to make junk vertices
19  // Don't use this if standard vertex reconstruction is disabled
20  if(tjs.Vertex2DCuts[0] <= 0) return;
21  if(!tjs.UseAlg[kJunkVx]) return;
22  if(tjs.allTraj.size() < 2) return;
23 
24  // Look for tjs that are within maxSep of the end of a Tj
25  constexpr float maxSep = 4;
26 
27  geo::PlaneID planeID = DecodeCTP(inCTP);
28  bool prt = (debug.Plane == (int)planeID.Plane && debug.Tick == 99999);
29  if(prt) {
30  mf::LogVerbatim("TC")<<"MakeJunkVertices: prt set for plane "<<planeID.Plane<<" maxSep btw tjs "<<maxSep;
31 // PrintAllTraj("MJTi", tjs, debug, USHRT_MAX, tjs.allTraj.size());
32  }
33 
34  // make a template vertex
35  VtxStore junkVx;
36  junkVx.CTP = inCTP;
37  junkVx.Topo = 9;
38  junkVx.Stat[kJunkVx] = true;
39  junkVx.Stat[kFixed] = true;
40  // set an invalid ID
41  junkVx.ID = USHRT_MAX;
42  // put in generous errors
43  // BUG the double brace syntax is required to work around clang bug 21629
44  // (https://bugs.llvm.org/show_bug.cgi?id=21629)
45  junkVx.PosErr = {{2.0, 2.0}};
46  // define a minimal score so it won't get clobbered
47  junkVx.Score = tjs.Vertex2DCuts[7] + 0.1;
48 
49  // look at both ends of long tjs
50  for(unsigned short it1 = 0; it1 < tjs.allTraj.size() - 1; ++it1) {
51  auto& tj1 = tjs.allTraj[it1];
52  if(tj1.AlgMod[kKilled]) continue;
53  if(tj1.SSID > 0 || tj1.AlgMod[kShowerLike]) continue;
54  if(tj1.CTP != inCTP) continue;
55  if(tj1.AlgMod[kJunkTj]) continue;
56  if(TrajLength(tj1) < 10) continue;
57  if(tj1.MCSMom < 100) continue;
58  for(unsigned short end1 = 0; end1 < 2; ++end1) {
59  // existing vertex?
60  if(tj1.VtxID[end1] > 0) continue;
61  auto& tp1 = tj1.Pts[tj1.EndPt[end1]];
62  // get a list of tjs in this vicinity
63  auto tjlist = FindCloseTjs(tjs, tp1, tp1, maxSep);
64  if(tjlist.empty()) continue;
65  // set to an invalid ID
66  junkVx.ID = USHRT_MAX;
67  for(auto tj2id : tjlist) {
68  auto& tj2 = tjs.allTraj[tj2id - 1];
69  if(tj2.CTP != inCTP) continue;
70  if(tj2id == tj1.ID) continue;
71  if(tj2.SSID > 0 || tj2.AlgMod[kShowerLike]) continue;
72  float close = maxSep;
73  unsigned short closeEnd = USHRT_MAX;
74  for(unsigned short end2 = 0; end2 < 2; ++end2) {
75  auto& tp2 = tj2.Pts[tj2.EndPt[end2]];
76  float sep = PosSep(tp1.Pos, tp2.Pos);
77  if(sep < close) {
78  close = sep;
79  closeEnd = end2;
80  } // sep
81  } // end2
82  if(closeEnd > 1) continue;
83  auto& tp2 = tj2.Pts[tj2.EndPt[closeEnd]];
84  bool signalBetween = SignalBetween(tjs, tp1, tp2, 0.8, prt);
85  if(!signalBetween) continue;
86  if(junkVx.ID == USHRT_MAX) {
87  // define the new vertex
88  junkVx.ID = tjs.vtx.size() + 1;
89  junkVx.Pos = tp1.Pos;
90  } // new vertex
91  tj2.VtxID[closeEnd] = junkVx.ID;
92  tj1.VtxID[end1] = junkVx.ID;
93  } // tjid
94  if(junkVx.ID == USHRT_MAX) continue;
95  if(!StoreVertex(tjs, junkVx)) {
96  mf::LogVerbatim("TC")<<"MJV: StoreVertex failed";
97  for(auto& tj : tjs.allTraj) {
98  if(tj.AlgMod[kKilled]) continue;
99  if(tj.VtxID[0] == junkVx.ID) tj.VtxID[0] = 0;
100  if(tj.VtxID[1] == junkVx.ID) tj.VtxID[1] = 0;
101  } // tj
102  continue;
103  } // StoreVertex failed
104  if(prt) {
105  mf::LogVerbatim("TC")<<" New junk 2V"<<junkVx.ID<<" at "<<std::fixed<<std::setprecision(1)<<junkVx.Pos[0]<<":"<<junkVx.Pos[1]/tjs.UnitsPerTick;
106  } // prt
107  junkVx.ID = USHRT_MAX;
108  } // end1
109  } // it1
110 
111  } // MakeJunkVertices
112 
114  void Find2DVertices(TjStuff& tjs, const CTP_t& inCTP)
115  {
116  // Find 2D vertices between pairs of tjs that have a same-end topology. Using an example
117  // where StepDir = 1 (end 0 is at small wire number) vertices will be found with Topo = 0
118  // with a vertex US of the ends (<) or Topo = 2 with a vertex DS of the ends (>). This is reversed
119  // if StepDir = -1. Vertices with Topo = 1 (/\) and (\/) are found in EndMerge.
120 
121  // tjs.Vertex2DCuts fcl input usage
122  // 0 = maximum length of a short trajectory
123  // 1 = max vertex - trajectory separation for short trajectories
124  // 2 = max vertex - trajectory separation for long trajectories
125  // 3 = max position pull for adding TJs to a vertex
126  // 4 = max allowed vertex position error
127  // 5 = min MCSMom
128  // 6 = min Pts/Wire fraction
129  // 7 min Score
130  // 8 Min charge fraction near a merge point (not a vertex)
131  // 9 max MCSmom asymmetry for a merge
132 
133  if(tjs.Vertex2DCuts[0] <= 0) return;
134 
135  if(tjs.allTraj.size() < 2) return;
136 
137  geo::PlaneID planeID = DecodeCTP(inCTP);
138 
139  bool prt = (debug.Plane == (int)planeID.Plane && debug.Tick < 0);
140  if(prt) {
141  mf::LogVerbatim("TC")<<"prt set for plane "<<planeID.Plane<<" in Find2DVertices";
142  PrintAllTraj("F2DVi", tjs, debug, USHRT_MAX, tjs.allTraj.size());
143  }
144 
145  unsigned short maxShortTjLen = tjs.Vertex2DCuts[0];
146  for(unsigned short it1 = 0; it1 < tjs.allTraj.size() - 1; ++it1) {
147  auto& tj1 = tjs.allTraj[it1];
148  if(tj1.AlgMod[kKilled]) continue;
149  if(tj1.SSID > 0 || tj1.AlgMod[kShowerLike]) continue;
150  if(tj1.CTP != inCTP) continue;
151  bool tj1Short = (TrajLength(tj1) < maxShortTjLen);
152  for(unsigned short end1 = 0; end1 < 2; ++end1) {
153  // vertex assignment exists?
154  if(tj1.VtxID[end1] > 0) continue;
155  // default condition is to use the end point to define the trajectory and direction
156  // at the end
157  short endPt1 = tj1.EndPt[end1];
158  float wire1 = tj1.Pts[endPt1].Pos[0];
159  // unless there are few points fitted, indicating that the trajectory fit
160  // may have been biased by the presence of another trajectory at the vertex or by
161  // other close unresolved tracks
162  if(tj1.Pts.size() > 6 && tj1.Pts[endPt1].NTPsFit < 4) {
163  if(end1 == 0 && endPt1 < int(tj1.Pts.size()) - 3) {
164  endPt1 += 3;
165  } else if (end1 == 1 && endPt1 >=3 ) {
166  endPt1 -= 3;
167  }
168  if(tj1.Pts[endPt1].Chg == 0) endPt1 = NearestPtWithChg(tjs, tj1, endPt1);
169  } // few points fit at end1
170  TrajPoint tp1 = tj1.Pts[endPt1];
171  MoveTPToWire(tp1, wire1);
172  // re-purpose endPt1 to reference the end point. This will be used the find the point on
173  // tj1 that is closest to the vertex position
174  endPt1 = tj1.EndPt[end1];
175  short oendPt1 = tj1.EndPt[1-end1];
176  // reference to the other end of tj1
177  auto& otp1 = tj1.Pts[oendPt1];
178  for(unsigned short it2 = it1 + 1; it2 < tjs.allTraj.size(); ++it2) {
179  auto& tj2 = tjs.allTraj[it2];
180  if(tj2.AlgMod[kKilled]) continue;
181  if(tj2.SSID > 0 || tj2.AlgMod[kShowerLike]) continue;
182  if(tj2.CTP != inCTP) continue;
183  if(tj1.VtxID[end1] > 0) continue;
184  if(tj1.MCSMom < tjs.Vertex2DCuts[5] && tj2.MCSMom < tjs.Vertex2DCuts[5]) continue;
185  bool tj2Short = (TrajLength(tj2) < maxShortTjLen);
186  // find the end that is closer to tp1
187  unsigned short end2 = 0;
188  if(PosSep2(tj2.Pts[tj2.EndPt[1]].Pos, tp1.Pos) < PosSep2(tj2.Pts[tj2.EndPt[0]].Pos, tp1.Pos)) end2 = 1;
189  if(tj2.VtxID[end2] > 0) continue;
190  // check for a vertex between these tjs at the other ends
191  if(tj1.VtxID[1 - end1] > 0 && tj1.VtxID[1 - end1] == tj2.VtxID[1 - end2]) continue;
192  // see if the other ends are closer
193  unsigned short oendPt2 = tj2.EndPt[1-end2];
194  auto& otp2 = tj2.Pts[oendPt2];
195  if(PosSep2(otp1.Pos, otp2.Pos) < PosSep2(tp1.Pos, tj2.Pts[tj2.EndPt[end2]].Pos)) continue;
196  short endPt2 = tj2.EndPt[end2];
197  float wire2 = tj2.Pts[endPt2].Pos[0];
198  if(tj2.Pts.size() > 6 && tj2.Pts[endPt2].NTPsFit < 4) {
199  if(end2 == 0 && endPt2 < int(tj2.Pts.size()) - 3) {
200  endPt2 += 3;
201  } else if (end2 == 1 && endPt2 >= 3){
202  endPt2 -= 3;
203  }
204  if(tj2.Pts[endPt2].Chg == 0) endPt2 = NearestPtWithChg(tjs, tj2, endPt2);
205  } // few points fit at end1
206  TrajPoint tp2 = tj2.Pts[endPt2];
207  MoveTPToWire(tp2, wire2);
208  // re-purpose endPt2
209  endPt2 = tj2.EndPt[end2];
210  // Rough first cut on the separation between the end points of the
211  // two trajectories
212  float sepCut = 100;
213  if(std::abs(tp1.Pos[0] - tp2.Pos[0]) > sepCut) continue;
214  if(std::abs(tp1.Pos[1] - tp2.Pos[1]) > sepCut) continue;
215  float wint, tint;
216  TrajIntersection(tp1, tp2, wint, tint);
217  // make sure this is inside the TPC
218  if(wint < 0 || wint > tjs.MaxPos0[planeID.Plane]) continue;
219  if(tint < 0 || tint > tjs.MaxPos1[planeID.Plane]) continue;
220  // Next cut on separation between the TPs and the intersection point
221  if(tj1Short || tj2Short) { sepCut = tjs.Vertex2DCuts[1]; } else { sepCut = tjs.Vertex2DCuts[2]; }
222  Point2_t vPos {{wint, tint}};
223  float vt1Sep = PosSep(vPos, tp1.Pos);
224  float vt2Sep = PosSep(vPos, tp2.Pos);
225  float dwc1 = DeadWireCount(tjs, wint, tp1.Pos[0], tp1.CTP);
226  float dwc2 = DeadWireCount(tjs, wint, tp2.Pos[0], tp1.CTP);
227  vt1Sep -= dwc1;
228  vt2Sep -= dwc2;
229  bool vtxOnDeadWire = (DeadWireCount(tjs, wint, wint, tp1.CTP) == 1);
230  if(prt && vt1Sep < 200 && vt2Sep < 200) {
231  mf::LogVerbatim myprt("TC");
232  myprt<<"F2DV candidate T"<<tj1.ID<<"_"<<end1<<"-T"<<tj2.ID<<"_"<<end2;
233  myprt<<" vtx pos "<<(int)wint<<":"<<(int)(tint/tjs.UnitsPerTick)<<" tp1 "<<PrintPos(tjs, tp1)<<" tp2 "<<PrintPos(tjs, tp2);
234  myprt<<" dwc1 "<<dwc1<<" dwc2 "<<dwc2<<" on dead wire? "<<vtxOnDeadWire;
235  myprt<<" vt1Sep "<<vt1Sep<<" vt2Sep "<<vt2Sep<<" sepCut "<<sepCut;
236  }
237  if(vt1Sep > sepCut || vt2Sep > sepCut) continue;
238  // make sure that the other end isn't closer
239  if(PosSep(vPos, tjs.allTraj[it1].Pts[oendPt1].Pos) < vt1Sep) {
240  if(prt) mf::LogVerbatim("TC")<<" tj1 other end "<<PrintPos(tjs, tj1.Pts[oendPt1])<<" is closer to the vertex";
241  continue;
242  }
243  if(PosSep(vPos, tjs.allTraj[it2].Pts[oendPt2].Pos) < vt2Sep) {
244  if(prt) mf::LogVerbatim("TC")<<" tj2 other end "<<PrintPos(tjs, tj2.Pts[oendPt2])<<" is closer to the vertex";
245  continue;
246  }
247  // Ensure that the vertex position is close to the end of each Tj
248  unsigned short closePt1;
249  float doca1 = sepCut;
250  if(!TrajClosestApproach(tj1, wint, tint, closePt1, doca1)) continue;
251  // dpt1 (and dpt2) will be 0 if the vertex is at the end
252  short dpt1 = tjs.StepDir * (closePt1 - endPt1);
253  if(prt) mf::LogVerbatim("TC")<<" endPt1 "<<endPt1<<" closePt1 "<<closePt1<<" dpt1 "<<dpt1<<" doca1 "<<doca1;
254  // BB April 19, 2018: require vertex to be near the end
255  if(dpt1 < -1) continue;
256  if(tjs.allTraj[it1].EndPt[1] > 4) {
257  if(dpt1 > 3) continue;
258  } else {
259  // tighter cut for short trajectories
260  if(dpt1 > 2) continue;
261  }
262  unsigned short closePt2;
263  float doca2 = sepCut;
264  if(!TrajClosestApproach(tj2, wint, tint, closePt2, doca2)) continue;
265  short dpt2 = tjs.StepDir * (closePt2 - endPt2);
266  if(prt) mf::LogVerbatim("TC")<<" endPt2 "<<endPt2<<" closePt2 "<<closePt2<<" dpt2 "<<dpt2<<" doca2 "<<doca2;
267  // BB April 19, 2018: require vertex to be near the end
268  if(dpt2 < -1) continue;
269  if(tjs.allTraj[it2].EndPt[1] > 4) {
270  if(dpt2 > 3) continue;
271  } else {
272  // tighter cut for short trajectories
273  if(dpt2 > 2) continue;
274  }
275  if(prt) mf::LogVerbatim("TC")<<" wint:tint "<<(int)wint<<":"<<(int)(tint/tjs.UnitsPerTick);
276  // ensure that there is a signal between these TPs and the vertex on most of the wires
277  bool signalBetween = true;
278  bool fixVxPos = false;
279  short dpt = abs(wint - tp1.Pos[0]);
280  if(dpt > 2 && !SignalBetween(tjs, tp1, wint, tjs.Vertex2DCuts[6], prt)) {
281  if(prt) mf::LogVerbatim("TC")<<" Fails SignalBetween for tp1 "<<dpt;
282  signalBetween = false;
283  }
284  dpt = abs(wint - tp2.Pos[0]);
285  if(dpt > 2 && !SignalBetween(tjs, tp2, wint, tjs.Vertex2DCuts[6], prt)) {
286  if(prt) mf::LogVerbatim("TC")<<" Fails SignalBetween for tp2 "<<dpt;
287  signalBetween = false;
288  }
289  // consider the case where the intersection point is wrong because the
290  // end TP angles are screwed up but the Tjs are close to each other near the end
291  if(!signalBetween) {
292  unsigned short ipt1, ipt2;
293  float maxSep = 3;
294  bool isClose = TrajTrajDOCA(tjs, tj1, tj2, ipt1, ipt2, maxSep, false);
295  if(prt) mf::LogVerbatim("TC")<<" TrajTrajDOCA close? "<<isClose<<" minSep "<<maxSep;
296  if(isClose) {
297  // put the vertex at the TP that is closest to the intersection point
298  Point2_t vpos = {{wint, tint}};
299  if(PosSep2(tp1.Pos, vpos) < PosSep2(tp2.Pos, vpos)) {
300  wint = tp1.Pos[0];
301  tint = tp1.Pos[1];
302  } else {
303  wint = tp2.Pos[0];
304  tint = tp2.Pos[1];
305  }
306  fixVxPos = true;
307  if(prt) mf::LogVerbatim("TC")<<" new wint:tint "<<(int)wint<<":"<<(int)(tint/tjs.UnitsPerTick);
308  } else {
309  // closest approach > 3
310  continue;
311  }
312  } // no signal between
313  // make a new temporary vertex
314  VtxStore aVtx;
315  aVtx.Pos[0] = wint;
316  aVtx.Pos[1] = tint;
317  aVtx.NTraj = 0;
318  aVtx.Pass = tj1.Pass;
319  // Topo 0 has this topology (<) and Topo 2 has this (>)
320  aVtx.Topo = 2 * end1;
321  aVtx.ChiDOF = 0;
322  aVtx.CTP = inCTP;
323  aVtx.Stat[kOnDeadWire] = vtxOnDeadWire;
324  // fix the vertex position if we needed to move it significantly, or if it is on a dead wire
325  aVtx.Stat[kFixed] = fixVxPos;
326 // if(close2 > 1) aVtx.Stat[kFixed] = true;
327  // try to fit it. We need to give it an ID to do that. Take the next
328  // available ID
329  unsigned short newVtxID = tjs.vtx.size() + 1;
330  aVtx.ID = newVtxID;
331  tj1.VtxID[end1] = newVtxID;
332  tj2.VtxID[end2] = newVtxID;
333  if(!FitVertex(tjs, aVtx, prt)) {
334  tj1.VtxID[end1] = 0;
335  tj2.VtxID[end2] = 0;
336  continue;
337  }
338  // check proximity to nearby vertices
339  unsigned short mergeMeWithVx = IsCloseToVertex(tjs, aVtx);
340  if(mergeMeWithVx > 0 && MergeWithVertex(tjs, aVtx, mergeMeWithVx, prt)) {
341  if(prt) mf::LogVerbatim("TC")<<" Merged with close vertex "<<mergeMeWithVx;
342  continue;
343  }
344  // Save it
345  if(!StoreVertex(tjs, aVtx)) continue;
346  if(prt) {
347  mf::LogVerbatim myprt("TC");
348  myprt<<" New vtx 2V"<<aVtx.ID;
349  myprt<<" Tjs "<<tj1.ID<<"_"<<end1<<"-"<<tj2.ID<<"_"<<end2;
350  myprt<<" at "<<std::fixed<<std::setprecision(1)<<aVtx.Pos[0]<<":"<<aVtx.Pos[1]/tjs.UnitsPerTick;
351  }
352  AttachAnyTrajToVertex(tjs, tjs.vtx.size() - 1, prt);
353  SetVx2Score(tjs, prt);
354  } // it2
355  } // end1
356  } // it1
357 
358  // check the consistency of the Tjs for the newly added vertices
359  ChkVxTjs(tjs, inCTP, prt);
360 
361  FindHammerVertices(tjs, inCTP);
362  FindHammerVertices2(tjs, inCTP);
363 
364  if(prt) PrintAllTraj("F2DVo", tjs, debug, USHRT_MAX, USHRT_MAX);
365 
366  } // Find2DVertices
367 
369  void FindNeutralVertices(TjStuff& tjs, const geo::TPCID& tpcid)
370  {
371  // Look for 2D neutral vertices between Tjs
372  if(!tjs.UseAlg[kVxNeutral]) return;
373  if(tjs.NeutralVxCuts.size() < 4) return;
374  if(tjs.NumPlanes < 3) return;
375  if(tjs.pfps.size() < 2) return;
376 
377  bool prt = (debug.Plane >= 0 && debug.Tick == 88888);
378 
379  struct CandVx {
380  unsigned short ip1;
381  unsigned short end1;
382  unsigned short ip2;
383  unsigned short end2;
384  Point3_t intersect;
385  float sepSum;
386  bool isValid;
387  };
388  std::vector<CandVx> candVxs;
389 
390  for(unsigned short ip1 = 0; ip1 < tjs.pfps.size() - 1; ++ip1) {
391  auto& p1 = tjs.pfps[ip1];
392  if(p1.ID == 0) continue;
393  if(p1.Tp3s.empty()) continue;
394  if(p1.TPCID != tpcid) continue;
395  float len1 = PosSep(p1.XYZ[0], p1.XYZ[1]);
396  if(len1 < tjs.NeutralVxCuts[3]) continue;
397  for(unsigned short end1 = 0; end1 < 2; ++end1) {
398  float cfne1 = ChgFracNearEnd(tjs, p1, end1);
399  if(cfne1 < tjs.NeutralVxCuts[0]) continue;
400  for(unsigned short ip2 = ip1 + 1; ip2 < tjs.pfps.size(); ++ip2) {
401  auto& p2 = tjs.pfps[ip2];
402  if(p2.ID == 0) continue;
403  if(p2.Tp3s.empty()) continue;
404  if(p2.TPCID != tpcid) continue;
405  float len2 = PosSep(p2.XYZ[0], p2.XYZ[1]);
406  if(len2 < tjs.NeutralVxCuts[3]) continue;
407  for(unsigned short end2 = 0; end2 < 2; ++end2) {
408  float cfne2 = ChgFracNearEnd(tjs, p2, end2);
409  if(cfne2 < tjs.NeutralVxCuts[0]) continue;
410  float vxDOCA = 1E6;
411  Point3_t intersect;
412  if(!PointDirIntersect(p1.XYZ[end1], p1.Dir[end1], p2.XYZ[end2], p2.Dir[end2], intersect, vxDOCA)) continue;
413  if(intersect[0] < tjs.XLo || intersect[0] > tjs.XHi) continue;
414  if(intersect[1] < tjs.YLo || intersect[1] > tjs.YHi) continue;
415  if(intersect[2] < tjs.ZLo || intersect[2] > tjs.ZHi) continue;
416  // ensure that the pfp end and the vertex are consistent
417  float sep1 = PosSep(intersect, p1.XYZ[end1]);
418  if(PosSep(intersect, p1.XYZ[1-end1]) < sep1) continue;
419  float sep2 = PosSep(intersect, p2.XYZ[end2]);
420  if(PosSep(intersect, p2.XYZ[1-end2]) < sep2) continue;
421  if(vxDOCA > tjs.NeutralVxCuts[1]) continue;
422 /*
423  // find the DOCA between these two
424  unsigned short closePt1, closePt2;
425  float pfpDOCA = PFPDOCA(p1, p2, closePt1, closePt2);
426  if(closePt1 == USHRT_MAX) continue;
427 */
428  // ensure that there isn't a lot of charge between the end of each pfp and the intersection point
429  float cfb1 = ChgFracBetween(tjs, p1.XYZ[end1], intersect, p1.TPCID);
430  float cfb2 = ChgFracBetween(tjs, p2.XYZ[end2], intersect, p2.TPCID);
431  if(prt) {
432  mf::LogVerbatim myprt("TC");
433  myprt<<"FNV: P"<<p1.ID<<"_"<<end1<<" sep1 "<<std::fixed<<std::setprecision(2)<<sep1;
434  myprt<<" cfne1 "<<cfne1<<" cfb1 "<<cfb1;
435  myprt<<" P"<<p2.ID<<"_"<<end2<<" sep2 "<<sep2<<" cfne2 "<<cfne2<<" cfb2 "<<cfb2;
436  myprt<<" intersect "<<intersect[0]<<" "<<intersect[1]<<" "<<intersect[2];
437  myprt<<" vxDOCA "<<vxDOCA;
438  } // prt
439  if(cfb1 > tjs.NeutralVxCuts[2]) continue;
440  if(cfb2 > tjs.NeutralVxCuts[2]) continue;
441  // check existing candidates
442  float sepSum = sep1 + sep2;
443  bool skipit = false;
444  for(auto& candVx : candVxs) {
445  if(!candVx.isValid) continue;
446  if(candVx.ip1 != ip1 && candVx.ip2 != ip2) continue;
447  // see if the separation sum is smaller
448  if(sepSum < candVx.sepSum) {
449  // flag the saved one as not valid
450  candVx.isValid = false;
451  } else {
452  skipit = true;
453  break;
454  }
455  } // candVx
456  if(skipit) continue;
457  CandVx candVx;
458  candVx.ip1 = ip1;
459  candVx.end1 = end1;
460  candVx.ip2 = ip2;
461  candVx.end2 = end2;
462  candVx.intersect = intersect;
463  candVx.sepSum = sep1 + sep2;
464  candVx.isValid = true;
465  candVxs.push_back(candVx);
466  if(prt) mf::LogVerbatim("TC")<<" candidate";
467  } // end2
468  } // ip2
469  } // end1
470  } // ip1
471 
472  if(candVxs.empty()) return;
473 
474  // Make vertices with the valid candidates
475  for(auto& candVx : candVxs) {
476  if(!candVx.isValid) continue;
477  Vtx3Store vx3;
478  auto& p1 = tjs.pfps[candVx.ip1];
479  auto& p2 = tjs.pfps[candVx.ip2];
480  vx3.TPCID = p1.TPCID;
481  vx3.ID = tjs.vtx3.size() + 1;
482  // try to improve the vertex position by fitting the Tjs in 2D
483  std::vector<TrajPoint> vxTps;
484  Vector3_t dir = {{0, 0, 0}};
485  for(unsigned short plane = 0; plane < tjs.NumPlanes; ++plane) {
486  vxTps.clear();
487  CTP_t inCTP = EncodeCTP(p1.TPCID.Cryostat, p1.TPCID.TPC, plane);
488  // Need the position in this plane to find the closest end
489  auto vtp = MakeBareTP(tjs, candVx.intersect, dir, inCTP);
490  if(vtp.Pos[0] < 0) continue;
491  for(auto tid : p1.TjIDs) {
492  auto& tj = tjs.allTraj[tid - 1];
493  if(tj.CTP != vtp.CTP) continue;
494  unsigned short end = CloseEnd(tjs, tj, vtp.Pos);
495  auto vxTp = tj.Pts[tj.EndPt[end]];
496  vxTp.Step = tid;
497  vxTp.AngleCode = end;
498  vxTps.push_back(vxTp);
499  } // tid
500  for(auto tid : p2.TjIDs) {
501  auto& tj = tjs.allTraj[tid - 1];
502  if(tj.CTP != vtp.CTP) continue;
503  unsigned short end = CloseEnd(tjs, tj, vtp.Pos);
504  auto vxTp = tj.Pts[tj.EndPt[end]];
505  vxTp.Step = tid;
506  vxTp.AngleCode = end;
507  vxTps.push_back(vxTp);
508  } // tid
509  if(vxTps.size() < 2) continue;
510  VtxStore vx2;
511  vx2.CTP = inCTP;
512  vx2.Topo = 11;
513  if(!FitVertex(tjs, vx2, vxTps, prt)) {
514  if(prt) mf::LogVerbatim("TC")<<"FNV: vertex fit failed";
515  continue;
516  } // fit failed
517  vx2.ID = tjs.vtx.size() + 1;
518  for(auto& vxTp : vxTps) {
519  int tid = vxTp.Step;
520  auto& tj = tjs.allTraj[tid - 1];
521  tj.VtxID[vxTp.AngleCode] = vx2.ID;
522  }
523  if(prt) mf::LogVerbatim("TC")<<"FNV: 2V fit inCTP "<<inCTP<<" pos "<<PrintPos(tjs, vx2.Pos);
524  vx2.Vx3ID = vx3.ID;
525  vx3.Vx2ID[plane] = vx2.ID;
526  SetVx2Score(tjs, vx2, prt);
527  if(!StoreVertex(tjs, vx2)) {
528  if(prt) mf::LogVerbatim("TC")<<"FNV: store vertex failed";
529  continue;
530  } // StoreVertex failed
531  } // plane
532  vx3.X = candVx.intersect[0];
533  vx3.Y = candVx.intersect[1];
534  vx3.Z = candVx.intersect[2];
535  vx3.Primary = true;
536  SetVx3Score(tjs, vx3, prt);
537  tjs.vtx3.push_back(vx3);
538  if(prt) mf::LogVerbatim("TC")<<"FNV: P"<<p1.ID<<"_"<<candVx.end1<<" P"<<p2.ID<<"_"<<candVx.end2<<" -> 3V"<<vx3.ID;
539  } // candVx
540 
541  } // FindNeutralVertices
542 
544  bool MergeWithVertex(TjStuff& tjs, VtxStore& vx, unsigned short oVxID, bool prt)
545  {
546  // Attempts to merge the trajectories attached to vx with an existing 2D vertex
547  // referenced by existingVxID. This function doesn't use the existing end0/end1 vertex association.
548  // It returns true if the merging was successful in which case the calling function should
549  // not store vx. The calling function needs to have set VtxID to vx.ID for tjs that are currently attached
550  // to vx. It assumed that vx hasn't yet been pushed onto tjs.vtx
551 
552  if(!tjs.UseAlg[kVxMerge]) return false;
553 
554  if(oVxID > tjs.vtx.size()) return false;
555  auto& oVx = tjs.vtx[oVxID - 1];
556  if(vx.CTP != oVx.CTP) return false;
557 
558  // get a list of tjs attached to both vertices
559  std::vector<int> tjlist = GetVtxTjIDs(tjs, vx);
560 // auto tjlist = GetAssns(tjs, "2V", vx.ID, "T");
561  if(tjlist.empty()) return false;
562  std::vector<int> tmp = GetVtxTjIDs(tjs, oVx);
563 // auto tmp = GetAssns(tjs, "2V", oVx.ID, "T");
564  if(tmp.empty()) return false;
565  for(auto tjid : tmp) {
566  if(std::find(tjlist.begin(), tjlist.end(), tjid) == tjlist.end()) tjlist.push_back(tjid);
567  } // tjid
568  if(tjlist.size() < 2) return false;
569  // handle the simple case
570  if(tjlist.size() == 2) {
571  // Unset the fixed bit
572  vx.Stat[kFixed] = false;
573  oVx.Stat[kFixed] = false;
574  // assign the vx tjs to oVx
575  for(auto tjid : tjlist) {
576  auto& tj = tjs.allTraj[tjid - 1];
577  for(unsigned short end = 0; end < 2; ++end) {
578  if(tj.VtxID[end] == vx.ID) tj.VtxID[end] = oVx.ID;
579  } // end
580  } // tjid
581  if(!FitVertex(tjs, oVx, prt)) {
582  if(prt) mf::LogVerbatim("TC")<<"MWV: merge failed "<<vx.ID<<" and existing "<<oVx.ID;
583  return false;
584  }
585  return true;
586  } // size = 2
587 
588  // sort by decreasing length
589  std::vector<SortEntry> sortVec(tjlist.size());
590  for(unsigned int indx = 0; indx < sortVec.size(); ++indx) {
591  sortVec[indx].index = indx;
592  auto& tj = tjs.allTraj[tjlist[indx] - 1];
593  sortVec[indx].val = tj.Pts.size();
594  } // indx
595  std::sort(sortVec.begin(), sortVec.end(), valDecreasing);
596  // re-order the list of Tjs
597  auto ttl = tjlist;
598  for(unsigned short ii = 0; ii < sortVec.size(); ++ii) tjlist[ii] = ttl[sortVec[ii].index];
599  // Create a local vertex using the two longest tjs, then add the shorter ones
600  // until the pull reaches the cut
601  VtxStore aVx;
602  aVx.CTP = vx.CTP;
603  std::vector<TrajPoint> tjpts(tjlist.size());
604  // determine which point on each Tj that will be used in the vertex fit and stash it in
605  // the traj point Step variable. This requires knowing the real position of the merged vertex
606  // which we estimate by averaging
607  std::array<float, 2> vpos;
608  vpos[0] = 0.5 * (vx.Pos[0] + oVx.Pos[0]);
609  vpos[1] = 0.5 * (vx.Pos[1] + oVx.Pos[1]);
610  for(unsigned short ii = 0; ii < tjpts.size(); ++ii) {
611  auto& tj = tjs.allTraj[tjlist[ii] - 1];
612  unsigned short npwc = NumPtsWithCharge(tjs, tj, false);
613  unsigned short end = CloseEnd(tjs, tj, vpos);
614  // assume that we will use the end point of the tj
615  unsigned short endPt = tj.EndPt[end];
616  if(npwc > 6 && tj.Pts[endPt].NTPsFit < 4) {
617  if(end == 0) {
618  endPt += 3;
619  } else {
620  endPt -= 3;
621  }
622  endPt = NearestPtWithChg(tjs, tj, endPt);
623  } // few points fit at the end
624  if(endPt < tj.EndPt[0]) endPt = tj.EndPt[0];
625  if(endPt > tj.EndPt[1]) endPt = tj.EndPt[1];
626  // define tjpts
627  tjpts[ii].CTP = tj.CTP;
628  tjpts[ii].Pos = tj.Pts[endPt].Pos;
629  tjpts[ii].Dir = tj.Pts[endPt].Dir;
630  tjpts[ii].Ang = tj.Pts[endPt].Ang;
631  tjpts[ii].AngErr = tj.Pts[endPt].AngErr;
632  // stash the point in Step
633  tjpts[ii].Step = endPt;
634  // and the end in AngleCode
635  tjpts[ii].AngleCode = end;
636  // stash the ID in Hits
637  tjpts[ii].Hits.resize(1, tj.ID);
638  } // tjid
639  if(prt) {
640  mf::LogVerbatim myprt("TC");
641  myprt<<"MWV: "<<oVxID;
642  myprt<<" Fit TPs";
643  for(unsigned short ii = 0; ii < tjpts.size(); ++ii) {
644  auto& tjpt = tjpts[ii];
645  myprt<<" "<<tjlist[ii]<<"_"<<tjpt.Step<<"_"<<PrintPos(tjs, tjpt.Pos);
646  }
647  } // prt
648  // create a subset of the first two for the first fit
649  auto fitpts = tjpts;
650  fitpts.resize(2);
651  if(!FitVertex(tjs, aVx, fitpts, prt)) {
652  if(prt) mf::LogVerbatim("TC")<<"MWV: first fit failed ";
653  return false;
654  }
655  // Fit and add tjs to the vertex
656  bool needsUpdate = false;
657  for(unsigned short ii = 2; ii < tjlist.size(); ++ii) {
658  fitpts.push_back(tjpts[ii]);
659  if(FitVertex(tjs, aVx, fitpts, prt)) {
660  needsUpdate = false;
661  } else {
662  // remove the last Tj point and keep going
663  fitpts.pop_back();
664  needsUpdate = true;
665  }
666  } // ii
667 
668  if(needsUpdate) FitVertex(tjs, aVx, fitpts, prt);
669  if(prt) mf::LogVerbatim("TC")<<"MWV: done "<<vx.ID<<" and existing "<<oVx.ID;
670 
671  // update. Remove old associations
672  for(auto& tj : tjs.allTraj) {
673  if(tj.AlgMod[kKilled]) continue;
674  if(tj.CTP != vx.CTP) continue;
675  for(unsigned short end = 0; end < 2; ++end) {
676  if(tj.VtxID[end] == vx.ID) tj.VtxID[end] = 0;
677  if(tj.VtxID[end] == oVxID) tj.VtxID[end] = 0;
678  }
679  } // tj
680  // set the new associations
681  for(unsigned short ii = 0; ii < fitpts.size(); ++ii) {
682  auto& tjpt = fitpts[ii];
683  unsigned short end = tjpt.AngleCode;
684  auto& tj = tjs.allTraj[tjpt.Hits[0] - 1];
685  if(tj.VtxID[end] != 0) {
686  std::cout<<"MWV: coding error. tj "<<tj.ID<<" end "<<end<<" VtxID "<<tj.VtxID[end]<<" != 0\n";
687  return false;
688  }
689  tj.VtxID[end] = oVxID;
690  } // ii
691 
692  // Update oVx
693  oVx.Pos = aVx.Pos;
694  oVx.PosErr = aVx.PosErr;
695  oVx.ChiDOF = aVx.ChiDOF;
696  oVx.NTraj = fitpts.size();
697  // Update the score and the charge fraction
698  SetVx2Score(tjs, oVx, prt);
699  oVx.Stat[kVtxMerged] = true;
700  oVx.Stat[kFixed] = false;
701  if(prt) {
702  mf::LogVerbatim myprt("TC");
703  myprt<<"MWV: "<<oVxID;
704  myprt<<" Done TPs";
705  for(unsigned short ii = 0; ii < fitpts.size(); ++ii) {
706  auto& tjpt = fitpts[ii];
707  myprt<<" "<<tjpt.Hits[0]<<"_"<<tjpt.AngleCode<<"_"<<PrintPos(tjs, tjpt.Pos);
708  }
709  } // prt
710 
711  return true;
712  } // MergeWithVertex
713 
715  void ChkVxTjs(TjStuff& tjs, const CTP_t& inCTP, bool prt)
716  {
717  //
718 
719  if(!tjs.UseAlg[kChkVxTj]) return;
720 
721  for(unsigned short ivx = 0; ivx < tjs.vtx.size(); ++ivx) {
722  auto& vx2 = tjs.vtx[ivx];
723  if(vx2.ID == 0) continue;
724  if(vx2.CTP != inCTP) continue;
725  auto vxtjs = GetAssns(tjs, "2V", vx2.ID, "T");
726  if(vxtjs.size() < 2) continue;
727  // BB added June 4, 2018
728  // find the closest separation between the vertex and the ends of the tjs
729  float close = 200;
730  int closeID = 0;
731  unsigned short closeEnd = 0;
732  for(auto tid : vxtjs) {
733  auto& tj = tjs.allTraj[tid - 1];
734  unsigned short nearEnd = 1 - FarEnd(tjs, tj, vx2.Pos);
735  if(tj.VtxID[nearEnd] != vx2.ID) continue;
736  float sep = PosSep(tj.Pts[tj.EndPt[nearEnd]].Pos, vx2.Pos);
737  if(sep > close) continue;
738  close = sep;
739  closeID = tid;
740  closeEnd = nearEnd;
741  } // tid
742  if(close > 1.5 && closeID > 0) {
743  auto& closeTj = tjs.allTraj[closeID - 1];
744  auto& closeTP = closeTj.Pts[closeTj.EndPt[closeEnd]];
745  if(prt) mf::LogVerbatim("TC")<<"CVTjs: moved 2V"<<vx2.ID<<" from "<<PrintPos(tjs, vx2.Pos)<<" to "<<PrintPos(tjs, closeTP);
746  vx2.Pos = closeTP.Pos;
747  vx2.Stat[kFixed] = true;
748  }
749  for(unsigned short it1 = 0; it1 < vxtjs.size() - 1; ++it1) {
750  auto& tj1 = tjs.allTraj[vxtjs[it1] - 1];
751  if(tj1.AlgMod[kKilled]) continue;
752  unsigned short end1 = 0;
753  if(tj1.VtxID[1] == vx2.ID) end1 = 1;
754  auto& vtp1 = tj1.Pts[tj1.EndPt[end1]];
755  auto& otp1 = tj1.Pts[tj1.EndPt[1 - end1]];
756  float tj1sep = PosSep(vtp1.Pos, vx2.Pos);
757  for(unsigned short it2 = it1 + 1; it2 < vxtjs.size(); ++it2) {
758  auto& tj2 = tjs.allTraj[vxtjs[it2] - 1];
759  if(tj2.AlgMod[kKilled]) continue;
760  unsigned short end2 = 0;
761  if(tj2.VtxID[2] == vx2.ID) end2 = 1;
762  auto& vtp2 = tj2.Pts[tj2.EndPt[end2]];
763  auto& otp2 = tj2.Pts[tj2.EndPt[1 - end2]];
764  float tj2sep = PosSep(vtp2.Pos, vx2.Pos);
765  float otj1tj2 = PosSep(otp1.Pos, vtp2.Pos);
766  float delta12 = PointTrajDOCA(tjs, otp1.Pos[0], otp1.Pos[1], vtp2);
767  float dang12 = DeltaAngle(otp1.Ang, vtp2.Ang);
768  if(otj1tj2 < tj2sep && delta12 < 1 && otj1tj2 < 4) {
769  if(prt) {
770  mf::LogVerbatim myprt("TC");
771  myprt<<"CVTjs: "<<vx2.ID<<" tj1 "<<tj1.ID<<" tj2 "<<tj2.ID;
772  myprt<<" otj1tj2 "<<otj1tj2;
773  myprt<<" delta12 "<<delta12;
774  myprt<<" dang12 "<<dang12;
775  myprt<<" Try to merge";
776  }
777  // End 1 of tj1 is closer to end0 of tj2 than tj2 is to the vertex
778  tj2.VtxID[end2] = 0;
779  if(CompatibleMerge(tjs, tj1, tj2, prt) && MergeAndStore(tjs, vxtjs[it1], vxtjs[it2], prt)) {
780  auto& newTj = tjs.allTraj[tjs.allTraj.size()-1];
781  newTj.AlgMod[kChkVxTj] = true;
782  if(prt) mf::LogVerbatim("TC")<<"CVTjs: Merged tjs "<<tj1.ID<<" and "<<tj2.ID<<" -> "<<newTj.ID;
783  } else {
784  if(prt) mf::LogVerbatim("TC")<<"CVTjs: Merge failed";
785  }
786  continue;
787  } // other end is closer
788  // now check the other end of tj2
789  float tj1otj2 = PosSep(vtp1.Pos, otp2.Pos);
790  if(tj1otj2 < tj1sep && delta12 < 1 && tj1otj2 < 4) {
791  // End 1 of tj1 is closer to end0 of tj2 than tj2 is to the vertex
792  tj1.VtxID[end1] = 0;
793  if(CompatibleMerge(tjs, tj2, tj1, prt) && MergeAndStore(tjs, vxtjs[it2], vxtjs[it1], prt)) {
794  auto& newTj = tjs.allTraj[tjs.allTraj.size()-1];
795  newTj.AlgMod[kChkVxTj] = true;
796  if(prt) mf::LogVerbatim("TC")<<"CVTjs: Merged tjs "<<tj1.ID<<" and "<<tj2.ID<<" -> "<<newTj.ID;
797  } else {
798  if(prt) mf::LogVerbatim("TC")<<"CVTjs: Merge failed";
799  }
800  } // the other end is closer
801  } // it2
802  } // it1
803  // Check for delta-rays that have a vertex when they should have been merged
804  if(vx2.Topo == 1 && vxtjs.size() == 2) {
805  auto& tj1 = tjs.allTraj[vxtjs[0] - 1];
806  auto& tj2 = tjs.allTraj[vxtjs[1] - 1];
807  // ensure that these weren't killed above
808  if(tj1.AlgMod[kKilled] || tj2.AlgMod[kKilled]) continue;
809  if(tj1.AlgMod[kDeltaRay] || tj2.AlgMod[kDeltaRay]) {
810  if(prt) mf::LogVerbatim("TC")<<"CVTjs: Merge delta rays "<<tj1.ID<<" and "<<tj2.ID<<" CompatibleMerge? "<<CompatibleMerge(tjs, tj1, tj2, prt);
811  MakeVertexObsolete(tjs, vx2, true);
812  MergeAndStore(tjs, vxtjs[0] - 1, vxtjs[1] - 1, prt);
813  } // one is a tagged delta-ray
814  } // delta-ray check
815  } // ivx
816  } // ChkVxTjs
817 
819  void FindHammerVertices2(TjStuff& tjs, const CTP_t& inCTP)
820  {
821  // Variant of FindHammerVertices with slightly different requirements:
822  // 1) tj1 is a straight trajectory with most of the points fit
823  // 2) No angle requirement between tj1 and tj2
824  // 3) Large charge near the intersection point X on tj2
825  // tj2 ---X---
826  // tj1 /
827  // tj1 /
828  // tj1 /
829  // minimum^2 DOCA of tj1 endpoint with tj2
830 
831  if(!tjs.UseAlg[kHamVx2]) return;
832 
833  bool prt = (debug.Plane >= 0 && debug.Tick == 66666);
834  if(prt) mf::LogVerbatim("TC")<<"Inside FindHammerVertices2";
835 
836  for(unsigned short it1 = 0; it1 < tjs.allTraj.size(); ++it1) {
837  if(tjs.allTraj[it1].CTP != inCTP) continue;
838  if(tjs.allTraj[it1].AlgMod[kKilled]) continue;
839  if(tjs.allTraj[it1].AlgMod[kHamVx]) continue;
840  if(tjs.allTraj[it1].AlgMod[kHamVx2]) continue;
841  if(tjs.allTraj[it1].AlgMod[kJunkTj]) continue;
842  unsigned short numPtsWithCharge1 = NumPtsWithCharge(tjs, tjs.allTraj[it1], false);
843  if(numPtsWithCharge1 < 6) continue;
844  // Check each end of tj1
845  bool didaSplit = false;
846  for(unsigned short end1 = 0; end1 < 2; ++end1) {
847  // vertex assignment exists?
848  if(tjs.allTraj[it1].VtxID[end1] > 0) continue;
849  unsigned short endPt1 = tjs.allTraj[it1].EndPt[end1];
850  for(unsigned short it2 = 0; it2 < tjs.allTraj.size(); ++it2) {
851  if(it1 == it2) continue;
852  if(tjs.allTraj[it2].AlgMod[kKilled]) continue;
853  if(tjs.allTraj[it2].AlgMod[kHamVx]) continue;
854  if(tjs.allTraj[it2].AlgMod[kHamVx2]) continue;
855  // require that both be in the same CTP
856  if(tjs.allTraj[it2].CTP != inCTP) continue;
857  if(tjs.allTraj[it2].AlgMod[kShowerLike]) continue;
858  if(tjs.allTraj[it2].AlgMod[kJunkTj]) continue;
859  unsigned short numPtsWithCharge2 = NumPtsWithCharge(tjs, tjs.allTraj[it2], true);
860  if(numPtsWithCharge2 < 6) continue;
861  // ignore if tj1 is a lot shorter than tj2
862  // ignore if ChgRMS isn't known
863  // Jan 22. Try this
864 // if(tjs.allTraj[it2].ChgRMS == 0) continue;
865 // if(numPtsWithCharge1 < 0.2 * numPtsWithCharge2) continue;
866  // Find the minimum separation between tj1 and tj2
867  float minDOCA = 5;
868  float doca = minDOCA;
869  unsigned short closePt2 = 0;
870  TrajPointTrajDOCA(tjs, tjs.allTraj[it1].Pts[endPt1], tjs.allTraj[it2], closePt2, doca);
871  if(doca == minDOCA) continue;
872  if(prt) {
873  mf::LogVerbatim myprt("TC");
874  auto& tj1 = tjs.allTraj[it1];
875  auto& tj2 = tjs.allTraj[it2];
876  myprt<<" FHV2 CTP"<<tj1.CTP;
877  myprt<<" T"<<tj1.ID<<"_"<<end1<<" MCSMom "<<tj1.MCSMom<<" ChgRMS "<<tj1.ChgRMS;
878  myprt<<" split T"<<tj2.ID<<"? MCSMom "<<tj2.MCSMom<<" ChgRMS "<<tj2.ChgRMS;
879  myprt<<" doca "<<doca<<" tj2.EndPt[0] "<<tj2.EndPt[0]<<" closePt2 "<<closePt2;
880  myprt<<" tj2.EndPt[1] "<<tj2.EndPt[1];
881  } // prt
882  // ensure that the closest point is not near an end
883  if(closePt2 < tjs.allTraj[it2].EndPt[0] + 3) continue;
884  if(closePt2 > tjs.allTraj[it2].EndPt[1] - 3) continue;
885  // Find the intersection point between the tj1 end and tj2 closest Pt
886  float wint, tint;
887  TrajIntersection(tjs.allTraj[it1].Pts[endPt1], tjs.allTraj[it2].Pts[closePt2], wint, tint);
888  // make an angle cut
889  float dang = DeltaAngle(tjs.allTraj[it1].Pts[endPt1].Ang, tjs.allTraj[it2].Pts[closePt2].Ang);
890  if(dang < 0.2) continue;
891  // ensure that tj1 doesn't cross tj2 but ensuring that the closest point on tj1 is at closePt1
892  doca = 5;
893  unsigned short closePt1 = 0;
894  TrajPointTrajDOCA(tjs, tjs.allTraj[it2].Pts[closePt2], tjs.allTraj[it1], closePt1, doca);
895  if(closePt1 != endPt1) continue;
896  if(prt) mf::LogVerbatim("TC")<<" intersection W:T "<<(int)wint<<":"<<(int)(tint/tjs.UnitsPerTick)<<" dang "<<dang;
897  // Find the point on tj2 that is closest to this point, overwriting closePt
898  doca = minDOCA;
899  // the point on tj2 that is closest to the intersection point
900  unsigned short intPt2;
901  TrajClosestApproach(tjs.allTraj[it2], wint, tint, intPt2, doca);
902  if(prt) mf::LogVerbatim("TC")<<" intPt2 on tj2 "<<intPt2<<" at Pos "<<PrintPos(tjs, tjs.allTraj[it2].Pts[intPt2])<<" doca "<<doca;
903  if(doca == minDOCA) continue;
904  // find the MCSMom for both sections of tj2
905  float mcsmom = tjs.allTraj[it2].MCSMom;
906  float mcsmom1 = MCSMom(tjs, tjs.allTraj[it2], tjs.allTraj[it2].EndPt[0], intPt2);
907  float mcsmom2 = MCSMom(tjs, tjs.allTraj[it2], intPt2, tjs.allTraj[it2].EndPt[1]);
908  // require that the both MCSMoms be greater than
909  if(prt) mf::LogVerbatim("TC")<<" Check MCSMom after split: mcsmom1 "<<mcsmom1<<" mcsmom2 "<<mcsmom2;
910  if(mcsmom1 < mcsmom || mcsmom2 < mcsmom) continue;
911  // start scanning for the point on tj2 that has the best IP with the end of tj1 in the direction
912  // from closePt2 -> endPt2
913  short dir = 1;
914  if(intPt2 < closePt2) dir = -1;
915  unsigned short nit = 0;
916  unsigned short ipt = intPt2;
917  float mostChg = tjs.allTraj[it2].Pts[ipt].Chg;
918  if(prt) mf::LogVerbatim("TC")<<" ipt "<<ipt<<" at Pos "<<PrintPos(tjs, tjs.allTraj[it2].Pts[ipt])<<" chg "<<mostChg;
919  while(nit < 20) {
920  ipt += dir;
921  if(ipt < 3 || ipt > tjs.allTraj[it2].Pts.size() - 4) break;
922  float delta = PointTrajDOCA(tjs, tjs.allTraj[it2].Pts[ipt].Pos[0], tjs.allTraj[it2].Pts[ipt].Pos[1], tjs.allTraj[it1].Pts[endPt1]);
923  float sep = PosSep(tjs.allTraj[it2].Pts[ipt].Pos, tjs.allTraj[it1].Pts[endPt1].Pos);
924  float dang = delta / sep;
925  float pull = dang / tjs.allTraj[it1].Pts[endPt1].DeltaRMS;
926  // if(prt) mf::LogVerbatim("TC")<<" intPt2 "<<intPt2<<" at Pos "<<PrintPos(tjs, tjs.allTraj[it2].Pts[ipt])<<" delta "<<delta<<" chg "<<tjs.allTraj[it2].Pts[ipt].Chg<<" pull "<<pull;
927  if(pull < 2 && tjs.allTraj[it2].Pts[ipt].Chg > mostChg) {
928  mostChg = tjs.allTraj[it2].Pts[ipt].Chg;
929  intPt2 = ipt;
930  }
931  }
932  // require a lot of charge in tj2 in this vicinity compared with the average.
933  float chgPull = (mostChg - tjs.allTraj[it2].AveChg) / tjs.allTraj[it2].ChgRMS;
934  if(prt) mf::LogVerbatim("TC")<<" chgPull at intersection point "<<chgPull;
935  if(chgPull < 10) continue;
936  // require a signal on every wire between it1 and intPt2
937  TrajPoint ltp = tjs.allTraj[it1].Pts[endPt1];
938  if(tjs.allTraj[it1].Pts[endPt1].Pos[0] < -0.4) continue;
939  unsigned int fromWire = std::nearbyint(tjs.allTraj[it1].Pts[endPt1].Pos[0]);
940  if(tjs.allTraj[it2].Pts[intPt2].Pos[0] < -0.4) continue;
941  unsigned int toWire = std::nearbyint(tjs.allTraj[it2].Pts[intPt2].Pos[0]);
942  if(fromWire > toWire) {
943  unsigned int tmp = fromWire;
944  fromWire = toWire;
945  toWire = tmp;
946  }
947  bool skipIt = false;
948  for(unsigned int wire = fromWire + 1; wire < toWire; ++wire) {
949  MoveTPToWire(ltp, (float)wire);
950  if(!SignalAtTp(tjs, ltp)) {
951  skipIt = true;
952  break;
953  }
954  } // wire
955  if(skipIt) continue;
956  // we have a winner
957  // create a new vertex
958  VtxStore aVtx;
959  aVtx.Pos = tjs.allTraj[it2].Pts[intPt2].Pos;
960  aVtx.NTraj = 3;
961  aVtx.Pass = tjs.allTraj[it2].Pass;
962  aVtx.Topo = 6;
963  aVtx.ChiDOF = 0;
964  aVtx.CTP = inCTP;
965  aVtx.ID = tjs.vtx.size() + 1;
966  unsigned short ivx = tjs.vtx.size();
967  if(!StoreVertex(tjs, aVtx)) continue;
968  if(!SplitTraj(tjs, it2, intPt2, ivx, prt)) {
969  if(prt) mf::LogVerbatim("TC")<<"FHV2: Failed to split trajectory";
970  // we can just remove the vertex since no Tj VtxID association has been made yet
971  tjs.vtx.pop_back();
972  continue;
973  }
974  tjs.allTraj[it1].VtxID[end1] = tjs.vtx[ivx].ID;
975  tjs.allTraj[it1].AlgMod[kHamVx2] = true;
976  tjs.allTraj[it2].AlgMod[kHamVx2] = true;
977  unsigned short newTjIndex = tjs.allTraj.size() - 1;
978  tjs.allTraj[newTjIndex].AlgMod[kHamVx2] = true;
979  AttachAnyTrajToVertex(tjs, ivx, prt);
980  SetVx2Score(tjs, prt);
981  // Update the PDGCode for the chopped trajectory
982  SetPDGCode(tjs, it2);
983  // and for the new trajectory
984  SetPDGCode(tjs, newTjIndex);
985  if(prt) mf::LogVerbatim("TC")<<" FHV2: New vtx 2V"<<tjs.vtx[ivx].ID<<" Score "<<tjs.vtx[ivx].Score;
986  didaSplit = true;
987  break;
988  } // it2
989  if(didaSplit) break;
990  } // end1
991  } // it1
992  } // FindHammerVertices2
993 
995  void FindHammerVertices(TjStuff& tjs, const CTP_t& inCTP)
996  {
997  // Look for a trajectory that intersects another. Split
998  // the trajectory and make a vertex. The convention used
999  // is shown pictorially here. Trajectory tj1 must be longer
1000  // than tj2
1001  // tj2 ------
1002  // tj1 /
1003  // tj1 /
1004  // tj1 /
1005 
1006  if(!tjs.UseAlg[kHamVx]) return;
1007 
1008  bool prt = (debug.Plane >= 0 && debug.Tick == 55555);
1009 
1010  for(unsigned short it1 = 0; it1 < tjs.allTraj.size(); ++it1) {
1011  if(tjs.allTraj[it1].CTP != inCTP) continue;
1012  if(tjs.allTraj[it1].AlgMod[kKilled]) continue;
1013  if(tjs.allTraj[it1].AlgMod[kShowerLike]) continue;
1014  if(tjs.allTraj[it1].AlgMod[kJunkTj]) continue;
1015  // minimum length requirements
1016  unsigned short tj1len = tjs.allTraj[it1].EndPt[1] - tjs.allTraj[it1].EndPt[0];
1017  if(tj1len < 6) continue;
1018  // Check each end of tj1
1019  bool didaSplit = false;
1020  for(unsigned short end1 = 0; end1 < 2; ++end1) {
1021  // vertex assignment exists?
1022  if(tjs.allTraj[it1].VtxID[end1] > 0) continue;
1023  unsigned short endPt1 = tjs.allTraj[it1].EndPt[end1];
1024  for(unsigned short it2 = 0; it2 < tjs.allTraj.size(); ++it2) {
1025  if(tjs.allTraj[it2].CTP != inCTP) continue;
1026  if(it1 == it2) continue;
1027  if(tjs.allTraj[it2].AlgMod[kKilled]) continue;
1028  if(tjs.allTraj[it2].AlgMod[kJunkTj]) continue;
1029  // length of tj2 cut
1030  unsigned short tj2len = tjs.allTraj[it2].EndPt[1] - tjs.allTraj[it2].EndPt[0];
1031  if(tj2len < 10) continue;
1032  // ignore if tj1 is a lot shorter than tj2
1033  if(tj1len < 0.5 * tj2len) continue;
1034  // ignore very long straight trajectories (probably a cosmic muon)
1035  unsigned short end20 = tjs.allTraj[it2].EndPt[0];
1036  unsigned short end21 = tjs.allTraj[it2].EndPt[1];
1037  if(tj2len > 100 && DeltaAngle(tjs.allTraj[it2].Pts[end20].Ang, tjs.allTraj[it2].Pts[end21].Ang) < 0.2) continue;
1038  // Require no vertex associated with itj2
1039  if(tjs.allTraj[it2].VtxID[0] > 0 || tjs.allTraj[it2].VtxID[1] > 0) continue;
1040  float minDOCA = 3;
1041  float doca = minDOCA;
1042  unsigned short closePt2 = 0;
1043  TrajPointTrajDOCA(tjs, tjs.allTraj[it1].Pts[endPt1], tjs.allTraj[it2], closePt2, doca);
1044  if(doca == minDOCA) continue;
1045  // ensure that the closest point is not near an end
1046  if(prt) mf::LogVerbatim("TC")<<"FindHammerVertices: Candidate "<<tjs.allTraj[it1].ID<<" "<<tjs.allTraj[it2].ID<<" doca "<<doca<<" tj2.EndPt[0] "<<tjs.allTraj[it2].EndPt[0]<<" closePt2 "<<closePt2<<" tj2.EndPt[1] "<<tjs.allTraj[it2].EndPt[1];
1047  if(closePt2 < tjs.allTraj[it2].EndPt[0] + 3) continue;
1048  if(closePt2 > tjs.allTraj[it2].EndPt[1] - 3) continue;
1049  // make an angle cut
1050  float dang = DeltaAngle(tjs.allTraj[it1].Pts[endPt1].Ang, tjs.allTraj[it2].Pts[closePt2].Ang);
1051  if(prt) mf::LogVerbatim("TC")<<" dang "<<dang<<" imposing a hard cut of 0.4 for now ";
1052  if(dang < 0.4) continue;
1053  // we have a winner
1054  // create a new vertex
1055  VtxStore aVtx;
1056  aVtx.Pos = tjs.allTraj[it2].Pts[closePt2].Pos;
1057  aVtx.NTraj = 3;
1058  aVtx.Pass = tjs.allTraj[it2].Pass;
1059  aVtx.Topo = 5;
1060  aVtx.ChiDOF = 0;
1061  aVtx.CTP = inCTP;
1062  aVtx.ID = tjs.vtx.size() + 1;
1063  unsigned short ivx = tjs.vtx.size();
1064  if(!StoreVertex(tjs, aVtx)) continue;
1065  if(!SplitTraj(tjs, it2, closePt2, ivx, prt)) {
1066  if(prt) mf::LogVerbatim("TC")<<"FindHammerVertices: Failed to split trajectory";
1067  tjs.vtx.pop_back();
1068  continue;
1069  }
1070  tjs.allTraj[it1].VtxID[end1] = tjs.vtx[ivx].ID;
1071  tjs.allTraj[it1].AlgMod[kHamVx] = true;
1072  tjs.allTraj[it2].AlgMod[kHamVx] = true;
1073  unsigned short newTjIndex = tjs.allTraj.size() - 1;
1074  tjs.allTraj[newTjIndex].AlgMod[kHamVx] = true;
1075  AttachAnyTrajToVertex(tjs, ivx, prt);
1076  SetVx2Score(tjs, prt);
1077  // Update the PDGCode for the chopped trajectory
1078  SetPDGCode(tjs, it2);
1079  // and for the new trajectory
1080  SetPDGCode(tjs, newTjIndex);
1081  didaSplit = true;
1082  break;
1083  } // tj2
1084  if(didaSplit) break;
1085  } // end1
1086  } // tj1
1087 
1088  } // FindHammerVertices
1089 
1092  {
1093  // This is kind of self-explanatory...
1094 
1095  if(!tjs.UseAlg[kSplitTjCVx]) return;
1096 
1097  if(tjs.vtx.empty()) return;
1098  if(tjs.allTraj.empty()) return;
1099 
1100  constexpr float docaCut = 4;
1101 
1102  bool prt = (debug.Plane >= 0 && debug.Tick == 77777);
1103  if(prt) mf::LogVerbatim("TC")<<"Inside SplitTrajCrossingVertices inCTP "<<inCTP;
1104 
1105  geo::PlaneID planeID = DecodeCTP(inCTP);
1106 
1107  unsigned short nTraj = tjs.allTraj.size();
1108  for(unsigned short itj = 0; itj < nTraj; ++itj) {
1109  // NOTE: Don't use a reference variable because it may get lost if the tj is split
1110 // auto& tj = tjs.allTraj[itj];
1111  if(tjs.allTraj[itj].CTP != inCTP) continue;
1112  // obsolete trajectory
1113  if(tjs.allTraj[itj].AlgMod[kKilled]) continue;
1114  if(tjs.allTraj[itj].AlgMod[kSplitTjCVx]) continue;
1115  // too short
1116  if(tjs.allTraj[itj].EndPt[1] < 6) continue;
1117  for(unsigned short iv = 0; iv < tjs.vtx.size(); ++iv) {
1118  auto& vx2 = tjs.vtx[iv];
1119  // obsolete vertex
1120  if(vx2.NTraj == 0) continue;
1121  // trajectory already associated with vertex
1122  if(tjs.allTraj[itj].VtxID[0] == vx2.ID ||
1123  tjs.allTraj[itj].VtxID[1] == vx2.ID) continue;
1124  // not in the cryostat/tpc/plane
1125  if(tjs.allTraj[itj].CTP != vx2.CTP) continue;
1126  // poor quality
1127  if(vx2.Score < tjs.Vertex2DCuts[7]) continue;
1128  float doca = docaCut;
1129  // make the cut significantly larger if the vertex is in a dead
1130  // wire gap to get the first TP that is just outside the gap.
1131  if(vx2.Stat[kOnDeadWire]) doca = 100;
1132  unsigned short closePt = 0;
1133  if(!TrajClosestApproach(tjs.allTraj[itj], vx2.Pos[0], vx2.Pos[1], closePt, doca)) continue;
1134  if(vx2.Stat[kOnDeadWire]) {
1135  // special handling for vertices in dead wire regions. Find the IP between
1136  // the closest point on the Tj and the vertex
1137  doca = PointTrajDOCA(tjs, vx2.Pos[0], vx2.Pos[1], tjs.allTraj[itj].Pts[closePt]);
1138  }
1139  if(doca > docaCut) continue;
1140  if(prt) mf::LogVerbatim("TC")<<" doca "<<doca<<" btw T"<<tjs.allTraj[itj].ID<<" and 2V"<<tjs.vtx[iv].ID<<" closePt "<<closePt<<" in plane "<<planeID.Plane;
1141  // compare the length of the Tjs used to make the vertex with the length of the
1142  // Tj that we want to split. Don't allow a vertex using very short Tjs to split a long
1143  // Tj in the 3rd plane
1144  auto vxtjs = GetVtxTjIDs(tjs, vx2);
1145  if(vxtjs.empty()) continue;
1146  unsigned short maxPts = 0;
1147  // ensure that there is a large angle between a Tj already attached to the vertex and the
1148  // tj that we want to split. We might be considering a delta-ray here
1149  float maxdang = 0;
1150  float tjAng = tjs.allTraj[itj].Pts[closePt].Ang;
1151  for(auto tjid : vxtjs) {
1152  auto& vtj = tjs.allTraj[tjid - 1];
1153  if(vtj.AlgMod[kDeltaRay]) continue;
1154  unsigned short npwc = NumPtsWithCharge(tjs, vtj, false);
1155  if(npwc > maxPts) maxPts = npwc;
1156  unsigned short end = 0;
1157  if(vtj.VtxID[1] == tjs.vtx[iv].ID) end = 1;
1158  auto& vtp = vtj.Pts[vtj.EndPt[end]];
1159  float dang = DeltaAngle(vtp.Ang, tjAng);
1160  if(dang > maxdang) maxdang = dang;
1161  } // tjid
1162  // skip this operation if any of the Tjs in the split list are > 3 * maxPts
1163  maxPts *= 3;
1164  bool skipit = false;
1165  if(NumPtsWithCharge(tjs, tjs.allTraj[itj], false) > maxPts && maxPts < 100) skipit = true;
1166  if(!skipit && maxdang < tjs.KinkCuts[0]) skipit = true;
1167  if(prt) mf::LogVerbatim("TC")<<" maxPts "<<maxPts<<" vxtjs[0] "<<vxtjs[0]<<" maxdang "<<maxdang<<" skipit? "<<skipit;
1168  if(skipit) {
1169  // kill the vertex?
1170  if(doca < 1) MakeVertexObsolete(tjs, vx2, true);
1171  continue;
1172  }
1173 
1174  // make some adjustments to closePt
1175  if(vx2.Stat[kOnDeadWire]) {
1176  // ensure that the tj will be split at the gap. The closePt point may be
1177  // on the wrong side of it
1178  auto& closeTP = tjs.allTraj[itj].Pts[closePt];
1179  if(tjs.allTraj[itj].StepDir > 0 && closePt > tjs.allTraj[itj].EndPt[0]) {
1180  if(closeTP.Pos[0] > vx2.Pos[0]) --closePt;
1181  } else if(tjs.allTraj[itj].StepDir < 0 && closePt < tjs.allTraj[itj].EndPt[1]) {
1182  if(closeTP.Pos[0] < vx2.Pos[0]) ++closePt;
1183  }
1184  } else {
1185  // improve closePt based on vertex position
1186  // check if closePt and EndPt[1] are the two sides of vertex
1187  // take dot product of closePt-vtx and EndPt[1]-vtx
1188  if ((tjs.allTraj[itj].Pts[closePt].Pos[0]-vx2.Pos[0])*(tjs.allTraj[itj].Pts[tjs.allTraj[itj].EndPt[1]].Pos[0]-vx2.Pos[0]) + (tjs.allTraj[itj].Pts[closePt].Pos[1]-vx2.Pos[1])*(tjs.allTraj[itj].Pts[tjs.allTraj[itj].EndPt[1]].Pos[1]-vx2.Pos[1]) <0 && closePt < tjs.allTraj[itj].EndPt[1] - 1) ++closePt;
1189  else if ((tjs.allTraj[itj].Pts[closePt].Pos[0]-vx2.Pos[0])*(tjs.allTraj[itj].Pts[tjs.allTraj[itj].EndPt[0]].Pos[0]-vx2.Pos[0]) + (tjs.allTraj[itj].Pts[closePt].Pos[1]-vx2.Pos[1])*(tjs.allTraj[itj].Pts[tjs.allTraj[itj].EndPt[0]].Pos[1]-tjs.vtx[iv].Pos[1]) <0 && closePt > tjs.allTraj[itj].EndPt[0] + 1) --closePt;
1190  }
1191 
1192 
1193  if(prt) {
1194  mf::LogVerbatim("TC")<<"Good doca "<<doca<<" btw T"<<tjs.allTraj[itj].ID<<" and 2V"<<vx2.ID<<" closePt "<<closePt<<" in plane "<<planeID.Plane<<" CTP "<<tjs.vtx[iv].CTP;
1195  PrintTrajPoint("STCV", tjs, closePt, 1, tjs.allTraj[itj].Pass, tjs.allTraj[itj].Pts[closePt]);
1196  }
1197  // ensure that the closest point is not near an end
1198  if(closePt < tjs.allTraj[itj].EndPt[0] + 3) continue;
1199  if(closePt > tjs.allTraj[itj].EndPt[1] - 3) continue;
1200  if(!SplitTraj(tjs, itj, closePt, iv, prt)) {
1201  if(prt) mf::LogVerbatim("TC")<<"SplitTrajCrossingVertices: Failed to split trajectory";
1202  continue;
1203  }
1204  tjs.allTraj[itj].AlgMod[kSplitTjCVx] = true;
1205  unsigned short newTjIndex = tjs.allTraj.size() - 1;
1206  tjs.allTraj[newTjIndex].AlgMod[kSplitTjCVx] = true;
1207  // re-fit the vertex position
1208  FitVertex(tjs, vx2, prt);
1209  } // iv
1210  } // itj
1211 
1212  } // SplitTrajCrossingVertices
1213 
1215  void Find3DVertices(TjStuff& tjs, const geo::TPCID& tpcid)
1216  {
1217  // Create 3D vertices from 2D vertices. 3D vertices that are matched
1218  // in all three planes have Vtx2ID > 0 for all planes. This function re-scores all
1219  // 2D and 3D vertices and flags Tjs that have high-score 3D vertices
1220 
1221  if(tjs.Vertex3DCuts[0] < 0) return;
1222  if(tjs.vtx.size() < 2) return;
1223 
1224  const unsigned int cstat = tpcid.Cryostat;
1225  const unsigned int tpc = tpcid.TPC;
1226 
1227  // create a array/vector of 2D vertex indices in each plane
1228  std::vector<std::vector<unsigned short>> vIndex(3);
1229  for(unsigned short ivx = 0; ivx < tjs.vtx.size(); ++ivx) {
1230  // obsolete vertex
1231  if(tjs.vtx[ivx].ID == 0) continue;
1232  geo::PlaneID planeID = DecodeCTP(tjs.vtx[ivx].CTP);
1233  if(planeID.TPC != tpc || planeID.Cryostat != cstat) continue;
1234  unsigned short plane = planeID.Plane;
1235  if(plane > 2) continue;
1236  vIndex[plane].push_back(ivx);
1237  }
1238 
1239  unsigned short vtxInPln = 0;
1240  for(unsigned short plane = 0; plane < tjs.NumPlanes; ++plane) if(vIndex[plane].size() > 0) ++vtxInPln;
1241  if(vtxInPln < 2) return;
1242 
1243  bool prt = (debug.Plane >= 0) && (debug.Tick == 2222);
1244 
1245  float thirdPlanedXCut = 2 * tjs.Vertex3DCuts[0];
1246 
1247  if(prt) {
1248  mf::LogVerbatim("TC")<<"Inside Find3DVertices. dX cut "<<tjs.Vertex3DCuts[0]<<" thirdPlanedXCut "<<thirdPlanedXCut;
1249  PrintAllTraj("F3DV", tjs, debug, USHRT_MAX, tjs.allTraj.size());
1250  }
1251 
1252  // wire spacing in cm
1253  float wirePitch = tjs.geom->WirePitch(0, tpcid.TPC, tpcid.Cryostat);
1254 
1255  size_t vsize = tjs.vtx.size();
1256  // vector of 2D vertices -> 3D vertices.
1257  std::vector<short> vPtr(vsize, -1);
1258  // fill temp vectors of 2D vertex X and X errors
1259  std::vector<float> vX(vsize, -100);
1260 
1261  for(unsigned short ivx = 0; ivx < vsize; ++ivx) {
1262  if(tjs.vtx[ivx].ID == 0) continue;
1263  geo::PlaneID planeID = DecodeCTP(tjs.vtx[ivx].CTP);
1264  if(planeID.TPC != tpc || planeID.Cryostat != cstat) continue;
1265  int plane = planeID.Plane;
1266  if(tjs.vtx[ivx].Pos[0] < -0.4) continue;
1267  unsigned int wire = std::nearbyint(tjs.vtx[ivx].Pos[0]);
1268  if(!tjs.geom->HasWire(geo::WireID(cstat, tpc, plane, wire))) continue;
1269  // Convert 2D vertex time error to X error
1270  double ticks = tjs.vtx[ivx].Pos[1] / tjs.UnitsPerTick;
1271  vX[ivx] = tjs.detprop->ConvertTicksToX(ticks, plane, (int)tpc, (int)cstat);
1272  } // ivx
1273 
1274  // temp vector of all 2D vertex matches
1275  std::vector<Vtx3Store> v3temp;
1276 
1277  TrajPoint tp;
1278  float maxScore = 0;
1279  constexpr float maxSep = 4;
1280  // i, j, k indicates 3 different wire planes
1281  // compare vertices in each view
1282  for(unsigned short ipl = 0; ipl < 2; ++ipl) {
1283  for(unsigned short ii = 0; ii < vIndex[ipl].size(); ++ii) {
1284  unsigned short ivx = vIndex[ipl][ii];
1285  if(vX[ivx] < 0) continue;
1286  auto& ivx2 = tjs.vtx[ivx];
1287  if(ivx2.Pos[0] < -0.4) continue;
1288  unsigned int iWire = std::nearbyint(ivx2.Pos[0]);
1289  for(unsigned short jpl = ipl + 1; jpl < 3; ++jpl) {
1290  for(unsigned short jj = 0; jj < vIndex[jpl].size(); ++jj) {
1291  unsigned short jvx = vIndex[jpl][jj];
1292  if(vX[jvx] < 0) continue;
1293  auto& jvx2 = tjs.vtx[jvx];
1294  if(jvx2.Pos[0] < -0.4) continue;
1295  unsigned int jWire = std::nearbyint(jvx2.Pos[0]);
1296  float dX = std::abs(vX[ivx] - vX[jvx]);
1297  if(dX > tjs.Vertex3DCuts[0]) continue;
1298  if(prt) {
1299  mf::LogVerbatim("TC")<<"F3DV: ipl "<<ipl<<" i2V"<<ivx2.ID<<" iX "<<vX[ivx]
1300  <<" jpl "<<jpl<<" j2V"<<jvx2.ID<<" jvX "<<vX[jvx]<<" W:T "<<(int)jvx2.Pos[0]<<":"<<(int)jvx2.Pos[1]<<" dX "<<dX;
1301  }
1302  double y = -1000, z = -1000;
1303  tjs.geom->IntersectionPoint(iWire, jWire, ipl, jpl, cstat, tpc, y, z);
1304  if(y < tjs.YLo || y > tjs.YHi || z < tjs.ZLo || z > tjs.ZHi) continue;
1305  unsigned short kpl = 3 - ipl - jpl;
1306  float kX = 0.5 * (vX[ivx] + vX[jvx]);
1307  int kWire = -1;
1308  if(tjs.NumPlanes > 2) {
1309  kWire = (int)(tjs.geom->WireCoordinate(y, z, kpl, tpc, cstat) + 0.5);
1310  if(kWire < 0 || (unsigned int)kWire > tjs.NumWires[kpl]) continue;
1311  if(!tjs.geom->HasWire(geo::WireID(cstat, tpc, kpl, kWire))) continue;
1312  std::array<int, 2> wireWindow;
1313  std::array<float, 2> timeWindow;
1314  wireWindow[0] = kWire - maxSep;
1315  wireWindow[1] = kWire + maxSep;
1316  float time = tjs.detprop->ConvertXToTicks(kX, kpl, (int)tpc, (int)cstat) * tjs.UnitsPerTick;
1317  timeWindow[0] = time - maxSep;
1318  timeWindow[1] = time + maxSep;
1319  bool hitsNear;
1320  std::vector<unsigned int> closeHits = FindCloseHits(tjs, wireWindow, timeWindow, kpl, kAllHits, true, hitsNear);
1321  if(prt) {
1322  mf::LogVerbatim myprt("TC");
1323  myprt<<" Hits near "<<kpl<<":"<<kWire<<":"<<(int)(time/tjs.UnitsPerTick)<<" = ";
1324  for(auto iht : closeHits) myprt<<" "<<PrintHit(tjs.fHits[iht]);
1325  }
1326  if(!hitsNear) continue;
1327  } // 3-plane TPC
1328 /* Don't require a Tj near this position. Look for hits instead
1329  if(tjs.NumPlanes > 2) {
1330  kWire = (int)(tjs.geom->WireCoordinate(y, z, kpl, tpc, cstat) + 0.5);
1331  if(kWire < 0 || (unsigned int)kWire > tjs.NumWires[kpl]) continue;
1332  if(!tjs.geom->HasWire(geo::WireID(cstat, tpc, kpl, kWire))) continue;
1333  tp.Pos[0] = kWire;
1334  // See if there is a wire signal nearby in kpl
1335  tp.Pos[1] = tjs.detprop->ConvertXToTicks(kX, kpl, tpc, cstat) * tjs.UnitsPerTick;
1336  tp.CTP = EncodeCTP(cstat, tpc, kpl);
1337  auto tjlist = FindCloseTjs(tjs, tp, tp, maxSep);
1338  if(prt) {
1339  mf::LogVerbatim myprt("TC");
1340  myprt<<" Tjs near "<<kpl<<":"<<PrintPos(tjs, tp);
1341  for(auto tjid : tjlist) myprt<<" T"<<tjid;
1342  }
1343  if(tjlist.empty()) continue;
1344  }
1345 */
1346  // save this incomplete 3D vertex
1347  Vtx3Store v3d;
1348  v3d.Vx2ID[ipl] = ivx + 1;
1349  v3d.Vx2ID[jpl] = jvx + 1;
1350  v3d.Vx2ID[kpl] = 0;
1351  // see if this is already in the list
1352  bool gotit = false;
1353  for(unsigned short i3t = 0; i3t < v3temp.size(); ++i3t) {
1354  if(v3temp[i3t].Vx2ID[0] == v3d.Vx2ID[0] && v3temp[i3t].Vx2ID[1] == v3d.Vx2ID[1] && v3temp[i3t].Vx2ID[2] == v3d.Vx2ID[2]) {
1355  gotit = true;
1356  break;
1357  }
1358  } // i3t
1359  if(gotit) continue;
1360  v3d.X = kX;
1361  // Use XErr to store dX
1362  v3d.XErr = dX;
1363  v3d.Y = y;
1364  v3d.Z = z;
1365  v3d.Wire = kWire;
1366  v3d.Score = dX / tjs.Vertex3DCuts[0];
1367  v3d.TPCID = tpcid;
1368  // push the incomplete vertex onto the list
1369  v3temp.push_back(v3d);
1370 
1371  if(prt) mf::LogVerbatim("TC")<<"F3DV: 2 Plane match i2V"<<tjs.vtx[ivx].ID<<" P:W:T "<<ipl<<":"<<(int)tjs.vtx[ivx].Pos[0]<<":"<<(int)tjs.vtx[ivx].Pos[1]<<" j2V"<<tjs.vtx[jvx].ID<<" P:W:T "<<jpl<<":"<<(int)tjs.vtx[jvx].Pos[0]<<":"<<(int)tjs.vtx[jvx].Pos[1]<<" dX "<<dX;
1372 
1373  if(tjs.NumPlanes == 2) continue;
1374 
1375  // look for a 3 plane match
1376  for(unsigned short kk = 0; kk < vIndex[kpl].size(); ++kk) {
1377  unsigned short kvx = vIndex[kpl][kk];
1378  if(vX[kvx] < 0) continue;
1379  float dX = std::abs(vX[kvx] - v3d.X);
1380  // Wire difference error
1381  float dW = wirePitch * std::abs(tjs.vtx[kvx].Pos[0] - kWire);
1382  if(prt) mf::LogVerbatim("TC")<<" k2V"<<kvx+1<<" dX "<<dX<<" dW "<<dW;
1383  if(dX > thirdPlanedXCut) continue;
1384  if(dW > tjs.Vertex3DCuts[1]) continue;
1385  // put the Y,Z difference in YErr and ZErr
1386  double y = -1000, z = -1000;
1387  tjs.geom->IntersectionPoint(iWire, kWire, ipl, kpl, cstat, tpc, y, z);
1388  v3d.YErr = y - v3d.Y;
1389  v3d.ZErr = z - v3d.Z;
1390  v3d.Vx2ID[kpl] = kvx + 1;
1391  v3d.Wire = -1;
1392  // hijack the Score variable to hold the separation^2, weighted by the
1393  // vertex3DCuts
1394  dX = (vX[kvx] - v3d.X) / tjs.Vertex3DCuts[0];
1395  float dY = v3d.YErr / tjs.Vertex3DCuts[1];
1396  float dZ = v3d.ZErr / tjs.Vertex3DCuts[1];
1397  v3d.Score = dX * dX + dY * dY + dZ * dZ;
1398  if(v3d.Score > maxScore) maxScore = v3d.Score;
1399  v3temp.push_back(v3d);
1400  } // kk
1401  } // jj
1402  } // jpl
1403  } // ii
1404  } // ipl
1405 
1406  if(v3temp.empty()) return;
1407 
1408  // We will sort this list by increasing score. First add the maxScore for 2-plane matches so that
1409  // they are considered after the 3-plane matches
1410  maxScore += 1;
1411  for(auto& v3 : v3temp) if(v3.Wire >= 0) v3.Score += maxScore;
1412 
1413  if(prt) {
1414  mf::LogVerbatim("TC")<<"v3temp list";
1415  for(auto& v3 : v3temp) {
1416  mf::LogVerbatim("TC")<<v3.Vx2ID[0]<<" "<<v3.Vx2ID[1]<<" "<<v3.Vx2ID[2]<<" wire "<<v3.Wire<<" "<<v3.Score;
1417  } // v3
1418  }
1419  SortEntry sEntry;
1420  std::vector<SortEntry> sortVec(v3temp.size());
1421  for(unsigned short ivx = 0; ivx < v3temp.size(); ++ivx) {
1422  sEntry.index = ivx;
1423  sEntry.val = v3temp[ivx].Score;
1424  sortVec[ivx] = sEntry;
1425  } // ivx
1426  if(sortVec.size() > 1) std::sort(sortVec.begin(), sortVec.end(), valIncreasing);
1427  // create a new vector of selected 3D vertices
1428  std::vector<Vtx3Store> v3sel;
1429  for(unsigned short ii = 0; ii < sortVec.size(); ++ii) {
1430  unsigned short ivx = sortVec[ii].index;
1431  // ensure that all 2D vertices are unique
1432  bool skipit = false;
1433  for(auto& v3 : v3sel) {
1434  for(unsigned short ipl = 0; ipl < tjs.NumPlanes; ++ipl) {
1435  if(v3temp[ivx].Vx2ID[ipl] == 0) continue;
1436  if(v3temp[ivx].Vx2ID[ipl] == v3.Vx2ID[ipl]) {
1437  skipit = true;
1438  break;
1439  }
1440  } // ipl
1441  if(skipit) break;
1442  } // v3
1443  if(skipit) continue;
1444  v3sel.push_back(v3temp[ivx]);
1445  } // ii
1446  v3temp.clear();
1447 
1448  if(prt) {
1449  mf::LogVerbatim("TC")<<"v3sel list";
1450  for(auto& v3d : v3sel) {
1451  mf::LogVerbatim("TC")<<v3d.Vx2ID[0]<<" "<<v3d.Vx2ID[1]<<" "<<v3d.Vx2ID[2]<<" wire "<<v3d.Wire<<" "<<v3d.Score;
1452  } // v3d
1453  }
1454 
1455  // Count the number of incomplete vertices and store
1456  unsigned short ninc = 0;
1457  for(auto& vx3 : v3sel) {
1458  if(tjs.NumPlanes == 2) {
1459  vx3.Vx2ID[2] = 666;
1460  } else {
1461  if(vx3.Wire >= 0) ++ninc;
1462  }
1463  vx3.ID = tjs.vtx3.size() + 1;
1464  if(prt) mf::LogVerbatim("TC")<<" 3V"<<vx3.ID<<" 2V"<<vx3.Vx2ID[0]<<" 2V"<<vx3.Vx2ID[1]<<" 2V"<<vx3.Vx2ID[2]
1465  <<" wire "<<vx3.Wire;
1466  tjs.vtx3.push_back(vx3);
1467  // make the 2D -> 3D associations
1468  for(unsigned short ipl = 0; ipl < tjs.NumPlanes; ++ipl) {
1469  if(vx3.Vx2ID[ipl] == 0) continue;
1470  VtxStore& vx2 = tjs.vtx[vx3.Vx2ID[ipl]-1];
1471  vx2.Vx3ID = vx3.ID;
1472  } // ipl
1473  } // ivx
1474 
1475  // Try to complete incomplete vertices
1476  if(ninc > 0) {
1478  CompleteIncomplete3DVertices(tjs, tpcid);
1479  }
1480 
1481  // Score and flag Tjs that are attached to high-score vertices
1482  // First remove Tj vertex flags
1483  for(auto& tj : tjs.allTraj) {
1484  if(tj.AlgMod[kKilled]) continue;
1485  geo::PlaneID planeID = DecodeCTP(tj.CTP);
1486  if(planeID.TPC != tpc || planeID.Cryostat != cstat) continue;
1487  tj.AlgMod[kTjHiVx3Score] = false;
1488  } // tj
1489  // Score the 2D vertices
1490  for(auto& vx2 : tjs.vtx) {
1491  if(vx2.ID == 0) continue;
1492  geo::PlaneID planeID = DecodeCTP(vx2.CTP);
1493  if(planeID.TPC != tpc || planeID.Cryostat != cstat) continue;
1494  SetVx2Score(tjs, vx2, prt);
1495  } // vx2
1496  // and the 3D vertices
1497  for(auto& vx3 : tjs.vtx3) {
1498  if(vx3.ID == 0) continue;
1499  if(vx3.TPCID != tpcid) continue;
1500  SetVx3Score(tjs, vx3, prt);
1501  } // vx3
1502 
1503  } // Find3DVertices
1504 
1506  void Match3DVtxTjs(TjStuff& tjs, const geo::TPCID& tpcid, bool prt)
1507  {
1508  // Matches Tjs that are attached to 2D vertices that are matched in 3D. This function does not attempt
1509  // to determine the appropriate ends of matched Tjs when there is a 3D vertex at both ends. This is done
1510  // in Find3DEndPoints
1511 
1512  if(tjs.vtx3.empty()) return;
1513  if(tjs.matchVec.empty()) return;
1514 
1515  // sort the vertices by decreasing score
1516  std::vector<SortEntry> sortVec;
1517  for(unsigned short iv3 = 0; iv3 < tjs.vtx3.size(); ++iv3) {
1518  auto& vx3 = tjs.vtx3[iv3];
1519  if(vx3.ID == 0) continue;
1520  if(vx3.TPCID != tpcid) continue;
1521  // put the TjIDs into a vector for matching
1522  float score = 0;
1523  auto v3TjIDs = GetVtxTjIDs(tjs, vx3, score);
1524  if(v3TjIDs.empty()) continue;
1525  if(score < tjs.Vertex2DCuts[7]) continue;
1526 /*
1527  if(prt) {
1528  mf::LogVerbatim myprt("TC");
1529  myprt<<"M3DVTj 3V"<<vx3.ID<<" score "<<score<<" TjIDs";
1530  for(auto& tjID : v3TjIDs) myprt<<" T"<<std::to_string(tjID);
1531  }
1532 */
1533  SortEntry se;
1534  se.index = iv3;
1535  se.val = score;
1536  sortVec.push_back(se);
1537  } // vx3
1538  if(sortVec.empty()) return;
1539  if(sortVec.size() > 1) std::sort(sortVec.begin(), sortVec.end(), valDecreasing);
1540 
1541  for(unsigned short ii = 0; ii < sortVec.size(); ++ii) {
1542  auto& vx3 = tjs.vtx3[sortVec[ii].index];
1543  float score = 0;
1544  std::vector<int> v3TjIDs = GetVtxTjIDs(tjs, vx3, score);
1545  if(prt) {
1546  mf::LogVerbatim myprt("TC");
1547  myprt<<"M3DVTj 3V"<<vx3.ID<<" score "<<score<<" Tjs";
1548  for(auto& tjID : v3TjIDs) myprt<<" T"<<tjID;
1549  }
1550  for(unsigned int ims = 0; ims < tjs.matchVec.size(); ++ims) {
1551  auto& ms = tjs.matchVec[ims];
1552  if(ms.Count == 0) continue;
1553  bool skipit = false;
1554  for(auto tjid : ms.TjIDs) {
1555  auto& tj = tjs.allTraj[tjid - 1];
1556  if(tj.AlgMod[kMat3D]) skipit = true;
1557  if(tj.AlgMod[kKilled]) skipit = true;
1558  }
1559  if(skipit) continue;
1560  std::vector<int> shared = SetIntersection(ms.TjIDs, v3TjIDs);
1561  if(shared.size() < 2) continue;
1562  if(prt) mf::LogVerbatim("TC")<<" ims "<<ims<<" shared size "<<shared.size();
1563  PFPStruct pfp = CreatePFP(tjs, tpcid);
1564  pfp.TjIDs = ms.TjIDs;
1565  pfp.Vx3ID[0] = vx3.ID;
1566  // note that the ms position is the average position of all 3D matched Tp3s at this point.
1567  // It is not the start position. This will be determined in DefinePFP.
1568  pfp.XYZ[0] = ms.Pos;
1569  pfp.Dir[0] = ms.Dir;
1570  pfp.MatchVecIndex = ims;
1571  if(prt) mf::LogVerbatim("TC")<<"M3DVTj: pfp P"<<pfp.ID<<" 3V"<<vx3.ID<<" ims "<<ims;
1572  // Set the PDGCode so DefinePFP can ignore incompatible matches
1573  pfp.PDGCode = PDGCodeVote(tjs, pfp.TjIDs, prt);
1574  // make a copy of the TjIDs to test for changes
1575  auto saveTjIDs = pfp.TjIDs;
1576  if(!DefinePFP("M3DVTj1", tjs, pfp, prt)) continue;
1577  // separation distance (cm) for kink detection.
1578  double sep = 1;
1579  bool didSplit = Split3DKink(tjs, pfp, sep, prt);
1580  if(prt) PrintPFP("M3D", tjs, pfp, true);
1581  if(!didSplit && shared.size() != ms.TjIDs.size()) {
1582  // Try to repair the PFParticle by merging the Tj that was in the match list but
1583  // wasn't attached to the vertex. Hopefully there aren't more than one...
1584  auto tjNotInVx = SetDifference(ms.TjIDs, shared);
1585  }
1586  AnalyzePFP(tjs, pfp, prt);
1587  if(!StorePFP(tjs, pfp)) continue;
1588  if(pfp.TjIDs != saveTjIDs) {
1589  // v3TjIDs is wrong if Tjs were merged so re-make it.
1590  auto tmp = GetVtxTjIDs(tjs, vx3, score);
1591  v3TjIDs.clear();
1592  // then re-build it
1593  for(auto tjid : tmp) {
1594  auto& tj = tjs.allTraj[tjid - 1];
1595  if(!tj.AlgMod[kMat3D]) v3TjIDs.push_back(tjid);
1596  } // tjid
1597  if(v3TjIDs.size() < 2) break;
1598  } // pfp.TjIDs != saveTjIDs
1599  ms.Count = 0;
1600  // clobber MatchStructs that use the Tjs in this pfp
1601  for(auto& allms : tjs.matchVec) {
1602  auto mfpfp = SetIntersection(allms.TjIDs, pfp.TjIDs);
1603  if(!mfpfp.empty()) allms.Count = 0;
1604  } // allms
1605  auto leftover = SetDifference(v3TjIDs, pfp.TjIDs);
1606  if(prt) {
1607  mf::LogVerbatim myprt("TC");
1608  myprt<<" leftover";
1609  for(auto tjid : leftover) myprt<<" T"<<tjid;
1610  }
1611  if(leftover.size() < 2) break;
1612  // keep looking using the leftovers
1613  v3TjIDs = leftover;
1614  } // ims
1615  if(v3TjIDs.size() > 1 && v3TjIDs.size() <= tjs.NumPlanes) {
1616  // a last-ditch attempt
1617  PFPStruct pfp = CreatePFP(tjs, tpcid);
1618  // put the available leftovers in
1619  for(auto& tjid : v3TjIDs) {
1620  auto& tj = tjs.allTraj[tjid - 1];
1621  if(tj.AlgMod[kMat3D]) continue;
1622  if(tj.AlgMod[kKilled]) continue;
1623  pfp.TjIDs.push_back(tjid);
1624  } // tjid
1625  if(pfp.TjIDs.size() < 2) continue;
1626  pfp.Vx3ID[0] = vx3.ID;
1627  if(!DefinePFP("M3DVTj2", tjs, pfp, prt)) continue;
1628  Split3DKink(tjs, pfp, 1, prt);
1629  AnalyzePFP(tjs, pfp, prt);
1630  if(prt) PrintPFP("left", tjs, pfp, true);
1631  if(!StorePFP(tjs, pfp)) continue;
1632  }
1633  } // ims
1634  } // Match3DVtxTjs
1635 
1637  unsigned short TPNearVertex(TjStuff& tjs, const TrajPoint& tp)
1638  {
1639  // Returns the index of a vertex if tp is nearby
1640  for(unsigned short ivx = 0; ivx < tjs.vtx.size(); ++ivx) {
1641  if(tjs.vtx[ivx].ID == 0) continue;
1642  if(tjs.vtx[ivx].CTP != tp.CTP) continue;
1643  if(std::abs(tjs.vtx[ivx].Pos[0] - tp.Pos[0]) > 1.2) continue;
1644  if(std::abs(tjs.vtx[ivx].Pos[1] - tp.Pos[1]) > 1.2) continue;
1645  return ivx;
1646  } // ivx
1647  return USHRT_MAX;
1648  } // TPNearVertex
1649 
1651  bool AttachPFPToVertex(TjStuff& tjs, PFPStruct& pfp, unsigned short end, unsigned short vx3ID, bool prt)
1652  {
1653  if(vx3ID > int(tjs.vtx3.size())) return false;
1654  if(pfp.ID > int(tjs.pfps.size())) return false;
1655  if(pfp.PDGCode == 22) return false;
1656  if(end > 1) return false;
1657 
1658  auto& vx3 = tjs.vtx3[vx3ID - 1];
1659 
1660  pfp.Vx3ID[end] = vx3.ID;
1661 
1662  // We are done if this a PFP-only vertex
1663  if(vx3.Wire == -2) return true;
1664 
1665  // Update the 2D and 3D vertex and tj associations
1666  for(auto tjid : pfp.TjIDs) {
1667  auto& tj = tjs.allTraj[tjid - 1];
1668  unsigned short plane = DecodeCTP(tj.CTP).Plane;
1669  // TODO: Check to see if the Tjs have been ordered correctly?
1670  if(tj.VtxID[end] == 0) {
1671  // tj is available to be attached to a 2D vertex. See if the 3D vertex is matched to
1672  // an existing 2D vertex in this plane
1673  if(vx3.Vx2ID[plane] == 0) {
1674  // not matched. Look for one
1675  std::array<float, 2> pos;
1676  PosInPlane(tjs, vx3, plane, pos);
1677 // if(prt) std::cout<<" tj "<<tj.ID<<" has no 2D vertex. Look for one vertex near "<<tj.CTP<<":"<<PrintPos(tjs, pos)<<" Events processed "<<tjs.EventsProcessed<<"\n";
1678  } else {
1679  // Existing 2D vertex matched to the 3D vertex
1680 // if(prt) std::cout<<" tj "<<tj.ID<<" has no 2D vertex in CTP "<<tj.CTP<<" but vx3 is matched to 2D vertex"<<vx3.Vx2ID[plane]<<". Attach it? Events processed "<<tjs.EventsProcessed<<"\n";
1681  }
1682  }
1683  } // tjid
1684 
1685  return true;
1686  } // AttachPFPToVertex
1687 
1689  bool AttachAnyTrajToVertex(TjStuff& tjs, unsigned short ivx, bool prt)
1690  {
1691 
1692  if(ivx > tjs.vtx.size() - 1) return false;
1693  if(tjs.vtx[ivx].ID == 0) return false;
1694  if(tjs.Vertex2DCuts[0] < 0) return false;
1695 
1696  VtxStore& vx = tjs.vtx[ivx];
1697 
1698  unsigned short nadd = 0;
1699  for(auto& tj : tjs.allTraj) {
1700  if(tj.AlgMod[kKilled]) continue;
1701  if(tj.CTP != vx.CTP) continue;
1702  if(tj.VtxID[0] == vx.ID || tj.VtxID[1] == vx.ID) continue;
1703  if(AttachTrajToVertex(tjs, tj, vx, prt)) ++nadd;
1704  } // tj
1705  if(prt) mf::LogVerbatim("TC")<<" AttachAnyTrajToVertex: nadd "<<nadd;
1706  if(nadd == 0) return false;
1707  return true;
1708 
1709  } // AttachAnyTrajToVertex
1710 
1712  bool AttachTrajToVertex(TjStuff& tjs, Trajectory& tj, VtxStore& vx, bool prt)
1713  {
1714  // Note that this function does not require a signal between the end of the Tj and the vertex
1715 
1716  // tjs.Vertex2DCuts fcl input usage
1717  // 0 = maximum length of a short trajectory
1718  // 1 = max vertex - trajectory separation for short trajectories
1719  // 2 = max vertex - trajectory separation for long trajectories
1720  // 3 = max position pull for adding TJs to a vertex
1721  // 4 = max allowed vertex position error
1722  // 5 = min MCSMom
1723  // 6 = min Pts/Wire fraction
1724 
1725  if(tj.AlgMod[kKilled]) return false;
1726  if(tj.CTP != vx.CTP) return false;
1727  // already attached?
1728  if(tj.VtxID[0] == vx.ID || tj.VtxID[1] == vx.ID) return false;
1729 
1730  unsigned short maxShortTjLen = tjs.Vertex2DCuts[0];
1731  // square the separation cut to simplify testing in the loop
1732  float maxSepCutShort2 = tjs.Vertex2DCuts[1] * tjs.Vertex2DCuts[1];
1733  float maxSepCutLong2 = tjs.Vertex2DCuts[2] * tjs.Vertex2DCuts[2];
1734 
1735  // assume that end 0 is closest to the vertex
1736  unsigned short end = 0;
1737  float vtxTjSep2 = PosSep2(vx.Pos, tj.Pts[tj.EndPt[0]].Pos);
1738  float sep1 = PosSep2(vx.Pos, tj.Pts[tj.EndPt[1]].Pos);
1739  if(sep1 < vtxTjSep2) {
1740  // End 1 is closer
1741  end = 1;
1742  vtxTjSep2 = sep1;
1743  }
1744  // is there a vertex already assigned to this end?
1745  if(tj.VtxID[end] > 0) return false;
1746 
1747  // is the trajectory short?
1748  bool tjShort = (tj.EndPt[1] - tj.EndPt[0] < maxShortTjLen);
1749  float closestApproach;
1750  // ignore bad separation between the closest tj end and the vertex
1751  if(tjShort) {
1752  if(vtxTjSep2 > maxSepCutShort2) return false;
1753  closestApproach = tjs.Vertex2DCuts[1];
1754  } else {
1755  closestApproach = tjs.Vertex2DCuts[2];
1756  if(vtxTjSep2 > maxSepCutLong2) return false;
1757  }
1758 
1759  // Calculate the pull on the vertex
1760  TrajPoint& tp = tj.Pts[tj.EndPt[end]];
1761  float tpVxPull = TrajPointVertexPull(tjs, tp, vx);
1762  bool signalBetween = SignalBetween(tjs, tp, vx.Pos[0], 0.8, prt);
1763 
1764  // See if the vertex position is close to an end
1765  unsigned short closePt;
1766  TrajClosestApproach(tj, vx.Pos[0], vx.Pos[1], closePt, closestApproach);
1767  // count the number of points between the end of the trajectory and the vertex.
1768  // tj ------------- tj ------------
1769  // vx * >> dpt = 0 vx * >> dpt = 2
1770  short dpt;
1771  if(end == 0) {
1772  dpt = closePt - tj.EndPt[end];
1773  } else {
1774  dpt = tj.EndPt[end] - closePt;
1775  }
1776 
1777  float length = TrajLength(tj);
1778  // don't attach it if the tj length is shorter than the separation distance
1779  if(length > 2 && length < closestApproach) return false;
1780 
1781  float pullCut = tjs.Vertex2DCuts[3];
1782  // Dec 21, 2017 Loosen up the pull cut for short close Tjs. These are likely to
1783  // be poorly reconstructed. It is better to have them associated with the vertex
1784  // than not.
1785  if(tjShort) pullCut = 10;
1786 
1787  if(prt) {
1788  mf::LogVerbatim myprt("TC");
1789  myprt<<"ATTV: 2V"<<vx.ID;
1790  myprt<<" NTraj "<<vx.NTraj;
1791  myprt<<" oldTJs";
1792  for(unsigned short itj = 0; itj < tjs.allTraj.size(); ++itj) {
1793  Trajectory& tj = tjs.allTraj[itj];
1794  if(tj.AlgMod[kKilled]) continue;
1795  if(tj.CTP != vx.CTP) continue;
1796  if(tj.VtxID[0] == vx.ID) myprt<<" "<<tj.ID<<"_0";
1797  if(tj.VtxID[1] == vx.ID) myprt<<" "<<tj.ID<<"_1";
1798  }
1799  myprt<<" +tjID "<<tj.ID<<"_"<<end<<" vtxTjSep "<<sqrt(vtxTjSep2)<<" tpVxPull "<<tpVxPull<<" pullCut "<<pullCut<<" dpt "<<dpt;
1800  }
1801 // if(tpVxPull > tjs.Vertex2DCuts[3]) return false;
1802  if(tpVxPull > pullCut) return false;
1803  if(dpt > 2) return true;
1804 
1805  // remove the fixed position flag if there are more than 2 tjs
1806  bool fixedBit = vx.Stat[kFixed];
1807  if(fixedBit && vx.NTraj < 2) vx.Stat[kFixed] = false;
1808 
1809  // don't allow a short Tj with a large pull to bias the fit
1810  if(tjShort && tpVxPull > tjs.Vertex2DCuts[3]) tj.AlgMod[kNoFitToVx] = true;
1811 
1812  // Passed all the cuts. Attach it to the vertex and try a fit
1813  tj.VtxID[end] = vx.ID;
1814  // flag as a photon Tj so it isn't included in the fit
1815  tj.AlgMod[kPhoton] = !signalBetween;
1816  // make a copy of the vertex and fit it
1817  auto vxTmp = vx;
1818  if(FitVertex(tjs, vxTmp, prt)) {
1819  SetVx2Score(tjs, vxTmp, prt);
1820  if(prt) mf::LogVerbatim("TC")<<" Success";
1821  vx = vxTmp;
1822  return true;
1823  }
1824 
1825  // test this again
1826  tj.AlgMod[kNoFitToVx] = true;
1827  if(prt) mf::LogVerbatim("TC")<<" Poor fit. Keep Tj "<<tj.ID<<" with kNoFitToVx";
1828  return true;
1829 /*
1830  // fit failed so remove the tj -> vx assignment if it is long and
1831  // set noFitToVtx if it is short
1832  if(tjShort) {
1833  tj.AlgMod[kNoFitToVx] = true;
1834  if(prt) mf::LogVerbatim("TC")<<" Poor fit. Keep short Tj "<<tj.ID<<" with kNoFitToVx";
1835  return true;
1836  } else {
1837  tj.VtxID[end] = 0;
1838  // restore the fixed flag
1839  vx.Stat[kFixed] = fixedBit;
1840  if(prt) mf::LogVerbatim("TC")<<" Poor fit. Removed Tj "<<tj.ID;
1841  return false;
1842  }
1843 */
1844  } // AttachTrajToVertex
1845 
1847  float TrajPointVertexPull(TjStuff& tjs, const TrajPoint& tp, const VtxStore& vx)
1848  {
1849  // Calculates the position pull between a trajectory point and a vertex
1850 
1851  // impact parameter between them
1852  double ip = PointTrajDOCA(tjs, vx.Pos[0], vx.Pos[1], tp);
1853  // separation^2
1854  double sep2 = PosSep2(vx.Pos, tp.Pos);
1855 
1856  // Find the projection of the vertex error ellipse in a coordinate system
1857  // along the TP direction
1858  double vxErrW = vx.PosErr[0] * tp.Dir[1];
1859  double vxErrT = vx.PosErr[1] * tp.Dir[0];
1860  double vxErr2 = vxErrW * vxErrW + vxErrT * vxErrT;
1861  // add the TP position error^2
1862  vxErr2 += tp.HitPosErr2;
1863 
1864  // close together so ignore the TP projection error and return
1865  // the pull using the vertex error and TP position error
1866  if(sep2 < 1) return (float)(ip/sqrt(vxErr2));
1867 
1868  double dang = ip / sqrt(sep2);
1869 
1870  // calculate the angle error.
1871  // Start with the vertex error^2
1872  double angErr = vxErr2 / sep2;
1873  // Add the TP angle error^2
1874  angErr += tp.AngErr * tp.AngErr;
1875  if(angErr == 0) return 999;
1876  angErr = sqrt(angErr);
1877  return (float)(dang / angErr);
1878 
1879  } // TrajPointVertexPull
1880 
1882  float VertexVertexPull(TjStuff& tjs, const Vtx3Store& vx1, const Vtx3Store& vx2)
1883  {
1884  // Calculates the position pull between two vertices
1885  double dx = vx1.X - vx2.X;
1886  double dy = vx1.Y - vx2.Y;
1887  double dz = vx1.Z - vx2.Z;
1888  double dxErr2 = (vx1.XErr * vx1.XErr + vx2.XErr * vx2.XErr) / 2;
1889  double dyErr2 = (vx1.YErr * vx1.YErr + vx2.YErr * vx2.YErr) / 2;
1890  double dzErr2 = (vx1.ZErr * vx1.ZErr + vx2.ZErr * vx2.ZErr) / 2;
1891  dx = dx * dx / dxErr2;
1892  dy = dy * dy / dyErr2;
1893  dz = dz * dz / dzErr2;
1894  return (float)(sqrt(dx + dy + dz)/3);
1895  }
1896 
1898  float VertexVertexPull(TjStuff& tjs, const VtxStore& vx1, const VtxStore& vx2)
1899  {
1900  // Calculates the position pull between two vertices
1901  double dw = vx1.Pos[0] - vx2.Pos[0];
1902  double dt = vx1.Pos[1] - vx2.Pos[1];
1903  double dwErr2 = (vx1.PosErr[0] * vx1.PosErr[0] + vx2.PosErr[0] * vx2.PosErr[0]) / 2;
1904  double dtErr2 = (vx1.PosErr[1] * vx1.PosErr[1] + vx2.PosErr[1] * vx2.PosErr[1]) / 2;
1905  dw = dw * dw / dwErr2;
1906  dt = dt * dt / dtErr2;
1907  return (float)sqrt(dw + dt);
1908  }
1909 
1911  bool StoreVertex(TjStuff& tjs, VtxStore& vx)
1912  {
1913  // jacket around the push to ensure that the Tj and vtx CTP is consistent.
1914  // The calling function should score the vertex after the trajectories are attached
1915 
1916  if(vx.ID != int(tjs.vtx.size() + 1)) {
1917  mf::LogVerbatim("TC")<<"StoreVertex: Invalid ID "<<vx.ID<<" It should be "<<tjs.vtx.size() + 1;
1918  return false;
1919  }
1920 
1921  unsigned short nvxtj = 0;
1922  unsigned short nok = 0;
1923  for(auto& tj : tjs.allTraj) {
1924  if(tj.AlgMod[kKilled]) continue;
1925  if(vx.ID == tj.VtxID[0] || vx.ID == tj.VtxID[1]) ++nvxtj;
1926  if(vx.CTP != tj.CTP) continue;
1927  if(vx.ID == tj.VtxID[0] || vx.ID == tj.VtxID[1]) ++nok;
1928  } // tj
1929 
1930  if(nok != nvxtj) {
1931  mf::LogVerbatim("TC")<<"StoreVertex: vertex "<<vx.ID<<" Topo "<<vx.Topo<<" has inconsistent CTP code "<<vx.CTP<<" with one or more Tjs\n";
1932  for(auto& tj : tjs.allTraj) {
1933  if(tj.AlgMod[kKilled]) continue;
1934  if(tj.VtxID[0] == vx.ID) tj.VtxID[0] = 0;
1935  if(tj.VtxID[1] == vx.ID) tj.VtxID[1] = 0;
1936  }
1937  return false;
1938  }
1939  vx.NTraj = nok;
1940  tjs.vtx.push_back(vx);
1941  return true;
1942 
1943  } // StoreVertex
1944 
1946  bool FitVertex(TjStuff& tjs, VtxStore& vx, bool prt)
1947  {
1948  // A poor-mans fitting scheme. If the fitted vertex position error is within the supplied
1949  // value, the position and errors are updated and we return true, otherwise the vertex is
1950  // left unchanged and we return false
1951 
1952  // tjs.Vertex2DCuts fcl input usage
1953  // 0 = maximum length of a short trajectory
1954  // 1 = max vertex - trajectory separation for short trajectories
1955  // 2 = max vertex - trajectory separation for long trajectories
1956  // 3 = max position pull for adding TJs to a vertex
1957  // 4 = max allowed vertex position error
1958  // 5 = min MCSMom
1959  // 6 = min Pts/Wire fraction
1960 
1961  if(vx.Stat[kFixed]) {
1962  if(prt) mf::LogVerbatim("TC")<<" vertex position fixed. No fit allowed";
1963  return true;
1964  }
1965 
1966 
1967  // Create a vector of trajectory points that will be used to fit the vertex position
1968  std::vector<TrajPoint> vxTp;
1969  for(auto& tj : tjs.allTraj) {
1970  if(tj.AlgMod[kKilled]) continue;
1971  if(tj.CTP != vx.CTP) continue;
1972  if(tj.AlgMod[kPhoton]) continue;
1973  if(tj.AlgMod[kNoFitToVx]) continue;
1974  if(tj.VtxID[0] == vx.ID) vxTp.push_back(tj.Pts[tj.EndPt[0]]);
1975  if(tj.VtxID[1] == vx.ID) vxTp.push_back(tj.Pts[tj.EndPt[1]]);
1976  } // tj
1977 
1978  bool success = FitVertex(tjs, vx, vxTp, prt);
1979 
1980  if(!success) return false;
1981  return true;
1982 
1983  } // FitVertex
1984 
1986  bool FitVertex(TjStuff& tjs, VtxStore& vx, std::vector<TrajPoint> vxTp, bool prt)
1987  {
1988  // Variant of FitVertex that fits the passed trajectory points to a vertex position but doesn't
1989  // require using information in TJStuff
1990 
1991  // tjs.Vertex2DCuts fcl input usage
1992  // 0 = maximum length of a short trajectory
1993  // 1 = max vertex - trajectory separation for short trajectories
1994  // 2 = max vertex - trajectory separation for long trajectories
1995  // 3 = max position pull for adding TJs to a vertex
1996  // 4 = max allowed vertex position error
1997  // 5 = min MCSMom
1998  // 6 = min Pts/Wire fraction
1999  // 7 min Score
2000  // 8 Min charge fraction near a merge point (not a vertex)
2001  // 9 max MCSmom asymmetry for a merge
2002 
2003 
2004  vx.NTraj = vxTp.size();
2005 
2006  if(vxTp.size() < 2) return false;
2007 
2008  if(prt) {
2009  PrintHeader("FV");
2010  for(auto& tp : vxTp) PrintTrajPoint("FV", tjs, 0, 1, 1, tp);
2011  }
2012 
2013  // Find trajectory intersections pair-wise tweaking the angle and position(?) within
2014  // +/- 1 sigma
2015  double sum0 = 0, sum02 = 0;
2016  double sum1 = 0, sum12 = 0;
2017  double sumw = 0;
2018  double wgt;
2019  double cnt = 0;
2020  // a temporary TP for tweaking the angle
2021  TrajPoint tmp;
2022  // another point to check for a signal at each intersection
2023  TrajPoint intTp;
2024  intTp.CTP = vxTp[0].CTP;
2025  for(unsigned short itj = 0; itj < vxTp.size() - 1; ++itj) {
2026  for(unsigned short jtj = itj + 1; jtj < vxTp.size(); ++jtj) {
2027  float p0, p1;
2028  TrajIntersection(vxTp[itj], vxTp[jtj], p0, p1);
2029  intTp.Pos[0] = p0; intTp.Pos[1] = p1;
2030  wgt = 1;
2031  // accumulate
2032  sum0 += wgt * p0; sum02 += wgt * p0 * p0;
2033  sum1 += wgt * p1; sum12 += wgt * p1 * p1; sumw += wgt; ++cnt;
2034  // tweak the itj angle +
2035  tmp = vxTp[itj];
2036  tmp.Ang += tmp.AngErr;
2037  tmp.Dir[0] = cos(tmp.Ang); tmp.Dir[1] = sin(tmp.Ang);
2038  TrajIntersection(tmp, vxTp[jtj], p0, p1);
2039  intTp.Pos[0] = p0; intTp.Pos[1] = p1;
2040  // adjust the weight for 4 points at +/1 1 sigma = 0.607 / 4
2041  wgt = 0.152;
2042  // accumulate
2043  sum0 += wgt * p0; sum02 += wgt * p0 * p0;
2044  sum1 += wgt * p1; sum12 += wgt * p1 * p1; sumw += wgt; ++cnt;
2045  // tweak the itj angle -
2046  tmp = vxTp[itj];
2047  tmp.Ang -= 2 * tmp.AngErr;
2048  tmp.Dir[0] = cos(tmp.Ang); tmp.Dir[1] = sin(tmp.Ang);
2049  TrajIntersection(tmp, vxTp[jtj], p0, p1);
2050  intTp.Pos[0] = p0; intTp.Pos[1] = p1;
2051  // accumulate
2052  sum0 += wgt * p0; sum02 += wgt * p0 * p0;
2053  sum1 += wgt * p1; sum12 += wgt * p1 * p1; sumw += wgt; ++cnt;
2054  // Repeat this process with jtj
2055  // tweak the jtj angle +
2056  tmp = vxTp[jtj];
2057  tmp.Ang += tmp.AngErr;
2058  tmp.Dir[0] = cos(tmp.Ang); tmp.Dir[1] = sin(tmp.Ang);
2059  TrajIntersection(vxTp[itj], tmp, p0, p1);
2060  intTp.Pos[0] = p0; intTp.Pos[1] = p1;
2061  // accumulate
2062  sum0 += wgt * p0; sum02 += wgt * p0 * p0;
2063  sum1 += wgt * p1; sum12 += wgt * p1 * p1; sumw += wgt; ++cnt;
2064  // tweak the itj angle -
2065  tmp = vxTp[itj];
2066  tmp.Ang -= 2 * tmp.AngErr;
2067  tmp.Dir[0] = cos(tmp.Ang); tmp.Dir[1] = sin(tmp.Ang);
2068  TrajIntersection(vxTp[itj], tmp, p0, p1);
2069  intTp.Pos[0] = p0; intTp.Pos[1] = p1;
2070  // accumulate
2071  sum0 += wgt * p0; sum02 += wgt * p0 * p0;
2072  sum1 += wgt * p1; sum12 += wgt * p1 * p1; sumw += wgt; ++cnt;
2073  } // jtj
2074  } // itj
2075 
2076  if(sumw == 0) return false;
2077 
2078  double vxP0 = sum0 / sumw;
2079  double vxP1 = sum1 / sumw;
2080  double vxP0rms = sqrt((sum02 - sumw * vxP0 * vxP0) / sumw);
2081  double vxP1rms = sqrt((sum12 - sumw * vxP1 * vxP1) / sumw);
2082  double rootN = sqrt(cnt);
2083  vxP0rms /= rootN;
2084  vxP1rms /= rootN;
2085  // don't let the errors get too small
2086  if(vxP0rms < 0.5) vxP0rms = 0.5;
2087  if(vxP1rms < 0.5) vxP1rms = 0.5;
2088 
2089  if(prt) mf::LogVerbatim("TC")<<"FitVertex 2V"<<vx.ID<<" CTP "<<vx.CTP<<" NTraj "<<vx.NTraj<<" in "<<std::fixed<<std::setprecision(1)<<vx.Pos[0]<<":"<<vx.Pos[1]/tjs.UnitsPerTick<<" out wire "<<vxP0<<" +/- "<<vxP0rms<<" ticks "<<vxP1/tjs.UnitsPerTick<<"+/-"<<vxP1rms/tjs.UnitsPerTick;
2090 
2091  // apply Vertex2DCuts if this isn't a neutral vertex (which is expected to have very large
2092  // errors)
2093  if(vx.Topo != 11) {
2094  float inflate = 1;
2095  if(vx.Stat[kOnDeadWire]) inflate = 1.5;
2096  if(vxP0rms > inflate * tjs.Vertex2DCuts[4] || vxP1rms > inflate * tjs.Vertex2DCuts[4]) {
2097  if(prt) mf::LogVerbatim("TC")<<" fit failed. Max allowed position error "<<inflate * tjs.Vertex2DCuts[4];
2098  return false;
2099  }
2100  } // not a neutral vertex
2101 
2102  vx.Pos[0] = vxP0;
2103  vx.PosErr[0] = vxP0rms;
2104  vx.Pos[1] = vxP1;
2105  vx.PosErr[1] = vxP1rms;
2106 
2107  // Calculate chisq
2108  vx.ChiDOF = 0;
2109  for(unsigned short itj = 0; itj < vxTp.size(); ++itj) {
2110  vx.ChiDOF += TrajPointVertexPull(tjs, vxTp[itj], vx);
2111  } // itj
2112  vx.ChiDOF /= (float)vxTp.size();
2113 
2114  if(prt) {
2115  mf::LogVerbatim myprt("TC");
2116  myprt<<"Pull";
2117  for(unsigned short itj = 0; itj < vxTp.size(); ++itj) {
2118  float pull = TrajPointVertexPull(tjs, vxTp[itj], vx);
2119  myprt<<" "<<PrintPos(tjs, vxTp[itj])<<" - "<<std::fixed<<std::setprecision(2)<<pull;
2120  } // itj
2121  myprt<<" ChiDOF "<<vx.ChiDOF;
2122  }
2123  return true;
2124 
2125  } // FitVertex
2126 
2128  bool ChkVtxAssociations(TjStuff& tjs, const CTP_t& inCTP)
2129  {
2130  // Check the associations
2131 
2132  // check the 2D -> 3D associations
2133  geo::PlaneID planeID = DecodeCTP(inCTP);
2134  unsigned short plane = planeID.Plane;
2135  for(auto& vx2 : tjs.vtx) {
2136  if(vx2.CTP != inCTP) continue;
2137  if(vx2.ID == 0) continue;
2138  if(vx2.Vx3ID == 0) continue;
2139  if(vx2.Vx3ID > int(tjs.vtx3.size())) {
2140  mf::LogVerbatim("TC")<<"ChkVtxAssociations: Invalid vx2.Vx3ID "<<vx2.Vx3ID<<" in 2D vtx "<<vx2.ID;
2141  return false;
2142  }
2143  auto& vx3 = tjs.vtx3[vx2.Vx3ID-1];
2144  if(vx3.ID == 0) {
2145  mf::LogVerbatim("TC")<<"ChkVtxAssociations: 2V"<<vx2.ID<<" thinks it is matched to 3V"<<vx3.ID<<" but vx3 is obsolete";
2146  return false;
2147  }
2148  if(vx3.Vx2ID[plane] != vx2.ID) {
2149  mf::LogVerbatim("TC")<<"ChkVtxAssociations: 2V"<<vx2.ID<<" thinks it is matched to 3V"<<vx3.ID<<" but vx3 says no!";
2150  return false;
2151  }
2152  } // vx2
2153  // check the 3D -> 2D associations
2154  for(auto& vx3 : tjs.vtx3) {
2155  if(vx3.ID == 0) continue;
2156  if(vx3.Vx2ID[plane] == 0) continue;
2157  if(vx3.Vx2ID[plane] > (int)tjs.vtx.size()) {
2158  mf::LogVerbatim("TC")<<"ChkVtxAssociations: Invalid vx3.Vx2ID "<<vx3.Vx2ID[plane]<<" in CTP "<<inCTP;
2159  return false;
2160  }
2161  auto& vx2 = tjs.vtx[vx3.Vx2ID[plane]-1];
2162  if(vx2.Vx3ID != vx3.ID) {
2163  mf::LogVerbatim("TC")<<"ChkVtxAssociations: 3V"<<vx3.ID<<" thinks it is matched to 2V"<<vx2.ID<<" but vx2 says no!";
2164  return false;
2165  }
2166  } // vx3
2167 
2168  // check the Tj -> 2D associations
2169  for(auto& tj : tjs.allTraj) {
2170  if(tj.AlgMod[kKilled]) continue;
2171  for(unsigned short end = 0; end < 2; ++end) {
2172  if(tj.VtxID[end] == 0) continue;
2173  if(tj.VtxID[end] > tjs.vtx.size()) {
2174  mf::LogVerbatim("TC")<<"ChkVtxAssociations: T"<<tj.ID<<" thinks it is matched to 2V"<<tj.VtxID[end]<<" on end "<<end<<" but no vertex exists. Recovering";
2175  tj.VtxID[end] = 0;
2176  return false;
2177  }
2178  unsigned short ivx = tj.VtxID[end] - 1;
2179  auto& vx2 = tjs.vtx[ivx];
2180  if(vx2.ID == 0) {
2181  mf::LogVerbatim("TC")<<"ChkVtxAssociations: T"<<tj.ID<<" thinks it is matched to 2V"<<tj.VtxID[end]<<" on end "<<end<<" but the vertex is killed. Fixing the Tj";
2182  tj.VtxID[end] = 0;
2183  return false;
2184  }
2185  } // end
2186  } // tj
2187 
2188  return true;
2189 
2190  } // ChkVtxAssociations
2191 
2193  void ScoreVertices(TjStuff& tjs, const geo::TPCID& tpcid, bool prt)
2194  {
2195  // reset all 3D vertex, 2D vertex and Tj high-score vertex bits in tpcid
2196 
2197  unsigned int cstat = tpcid.Cryostat;
2198  unsigned int tpc = tpcid.TPC;
2199 
2200  // reset the 2D vertex status bits
2201  for(auto& vx : tjs.vtx) {
2202  if(vx.ID == 0) continue;
2203  geo::PlaneID planeID = DecodeCTP(vx.CTP);
2204  if(planeID.Cryostat != cstat) continue;
2205  if(planeID.TPC != tpc) continue;
2206  vx.Stat[kHiVx3Score] = false;
2207  } // vx
2208  // and the tj bits
2209  for(auto& tj : tjs.allTraj) {
2210  if(tj.AlgMod[kKilled]) continue;
2211  geo::PlaneID planeID = DecodeCTP(tj.CTP);
2212  if(planeID.Cryostat != cstat) continue;
2213  if(planeID.TPC != tpc) continue;
2214  tj.AlgMod[kTjHiVx3Score] = false;
2215  } // tj
2216  // Score the 2D vertices
2217  for(auto& vx : tjs.vtx) {
2218  if(vx.ID == 0) continue;
2219  geo::PlaneID planeID = DecodeCTP(vx.CTP);
2220  if(planeID.Cryostat != cstat) continue;
2221  if(planeID.TPC != tpc) continue;
2222  SetVx2Score(tjs, vx, prt);
2223  } // vx
2224  // Score the 3D vertices
2225  for(auto& vx3 : tjs.vtx3) {
2226  if(vx3.ID == 0) continue;
2227  if(vx3.TPCID != tpcid) continue;
2228  SetVx3Score(tjs, vx3, prt);
2229  } // vx3
2230  } // ScoreVertices
2231 
2233  void KillPoorVertices(TjStuff& tjs, const geo::TPCID& tpcid)
2234  {
2235  // kill 2D vertices that have low score and are not attached to a high-score 3D vertex
2236  if(tjs.vtx.empty()) return;
2237  unsigned int cstat = tpcid.Cryostat;
2238  unsigned int tpc = tpcid.TPC;
2239  for(auto& vx : tjs.vtx) {
2240  if(vx.ID == 0) continue;
2241  geo::PlaneID planeID = DecodeCTP(vx.CTP);
2242  if(planeID.Cryostat != cstat) continue;
2243  if(planeID.TPC != tpc) continue;
2244  if(vx.Score > tjs.Vertex2DCuts[7]) continue;
2245  if(vx.Vx3ID > 0) {
2246  auto& vx3 = tjs.vtx3[vx.Vx3ID - 1];
2247  if(vx3.Primary) continue;
2248  if(tjs.vtx3[vx.Vx3ID - 1].Score >= tjs.Vertex2DCuts[7]) continue;
2249  }
2250  MakeVertexObsolete(tjs, vx, false);
2251  } // vx
2252 
2253  } // KillPoorVertices
2254 
2257  {
2258  // Sets the tj and 2D vertex score bits to true
2259 
2260  if(vx3.ID == 0) return;
2261 
2262  for(unsigned short ipl = 0; ipl < tjs.NumPlanes; ++ipl) {
2263  if(vx3.Vx2ID[ipl] <= 0) continue;
2264  VtxStore& vx2 = tjs.vtx[vx3.Vx2ID[ipl] - 1];
2265  vx2.Stat[kHiVx3Score] = false;
2266  // transfer this to all attached tjs and vertices attached to those tjs
2267  std::vector<int> tjlist = GetVtxTjIDs(tjs, vx2);
2268  std::vector<int> vxlist;
2269  while(true) {
2270  // tag Tjs and make a list of attached vertices whose high-score
2271  // bit needs to be set
2272  vxlist.clear();
2273  for(auto tjid : tjlist) {
2274  auto& tj = tjs.allTraj[tjid - 1];
2275  tj.AlgMod[kTjHiVx3Score] = true;
2276  for(unsigned short end = 0; end < 2; ++end) {
2277  if(tj.VtxID[end] == 0) continue;
2278  auto& vx2 = tjs.vtx[tj.VtxID[end] - 1];
2279  if(vx2.Stat[kHiVx3Score]) continue;
2280  vx2.Stat[kHiVx3Score] = true;
2281  vxlist.push_back(vx2.ID);
2282  } // end
2283  } // tjid
2284 
2285  if(vxlist.empty()) break;
2286  // re-build tjlist using vxlist
2287  std::vector<int> newtjlist;
2288  for(auto vxid : vxlist) {
2289  auto& vx2 = tjs.vtx[vxid - 1];
2290  auto tmp = GetVtxTjIDs(tjs, vx2);
2291  for(auto tjid : tmp) {
2292  if(std::find(tjlist.begin(), tjlist.end(), tjid) == tjlist.end()) newtjlist.push_back(tjid);
2293  } // tjid
2294  } // vxid
2295  if(newtjlist.empty()) break;
2296  tjlist = newtjlist;
2297  } // true
2298  } // ipl
2299 
2300  } // SetHighScoreBits
2301 
2303  void SetVx3Score(TjStuff& tjs, Vtx3Store& vx3, bool prt)
2304  {
2305  // Calculate the 3D vertex score and flag Tjs that are attached to high score vertices as defined
2306  // by Vertex2DCuts
2307 
2308  if(vx3.ID == 0) return;
2309 
2310  vx3.Score = 0;
2311  for(unsigned short ipl = 0; ipl < tjs.NumPlanes; ++ipl) {
2312  if(vx3.Vx2ID[ipl] <= 0) continue;
2313  VtxStore& vx2 = tjs.vtx[vx3.Vx2ID[ipl] - 1];
2314  vx3.Score += vx2.Score;
2315  } // ipl
2316  vx3.Score /= (float)tjs.NumPlanes;
2317  if(vx3.Score > tjs.Vertex2DCuts[7]) SetHighScoreBits(tjs, vx3);
2318 
2319  } // SetVx3Score
2320 
2322  void SetVx2Score(TjStuff& tjs, bool prt)
2323  {
2324  // A version that sets the score of the last added vertex
2325  if(tjs.vtx.empty()) return;
2326  auto& vx2 = tjs.vtx[tjs.vtx.size() - 1];
2327  SetVx2Score(tjs, vx2, prt);
2328  } // SetVx2Score
2329 
2331  void SetVx2Score(TjStuff& tjs, VtxStore& vx2, bool prt)
2332  {
2333  // Calculate the 2D vertex score
2334  if(vx2.ID == 0) return;
2335 
2336  // Don't score vertices from CheckTrajBeginChg, MakeJunkVertices or Neutral vertices. Set to the minimum
2337  if(vx2.Topo == 8 || vx2.Topo == 9 || vx2.Topo == 11) {
2338  vx2.Score = tjs.Vertex2DCuts[7] + 0.1;
2339  auto vtxTjID = GetVtxTjIDs(tjs, vx2);
2340  vx2.TjChgFrac = ChgFracNearPos(tjs, vx2.Pos, vtxTjID);
2341  return;
2342  }
2343 
2344  // Cuts on Tjs attached to vertices
2345  constexpr float maxChgRMS = 0.25;
2346  constexpr float momBin = 50;
2347 
2348  vx2.Score = -1000;
2349  vx2.TjChgFrac = 0;
2350  if(vx2.ID == 0) return;
2351  if(tjs.VertexScoreWeights.size() < 4) return;
2352 
2353  auto vtxTjIDs = GetVtxTjIDs(tjs, vx2);
2354  if(vtxTjIDs.empty()) return;
2355 
2356  // Vertex position error
2357  float vpeScore = -tjs.VertexScoreWeights[0] * (vx2.PosErr[0] + vx2.PosErr[1]);
2358 
2359  unsigned short m3Dcnt = 0;
2360  if(vx2.Vx3ID > 0) {
2361  m3Dcnt = 1;
2362  // Add another if the 3D vertex is complete
2363  unsigned short ivx3 = vx2.Vx3ID - 1;
2364  if(tjs.vtx3[ivx3].Wire < 0) m3Dcnt = 2;
2365  }
2366  float m3DScore = tjs.VertexScoreWeights[1] * m3Dcnt;
2367 
2368  vx2.TjChgFrac = ChgFracNearPos(tjs, vx2.Pos, vtxTjIDs);
2369  float cfScore = tjs.VertexScoreWeights[2] * vx2.TjChgFrac;
2370 
2371  // Define a weight for each Tj
2372  std::vector<int> tjids;
2373  std::vector<float> tjwts;
2374  for(auto tjid : vtxTjIDs) {
2375  Trajectory& tj = tjs.allTraj[tjid - 1];
2376  // Feb 22 Ignore short Tjs and junk tjs
2377  if(tj.AlgMod[kJunkTj]) continue;
2378  unsigned short lenth = tj.EndPt[1] - tj.EndPt[0] + 1;
2379  if(lenth < 3) continue;
2380  float wght = (float)tj.MCSMom / momBin;
2381  if(wght > 10) wght = 10;
2382  // weight by tagged muon
2383  if(tj.PDGCode == 13) wght *= 2;
2384  // weight by charge rms
2385  if(tj.ChgRMS < maxChgRMS) ++wght;
2386  // Shower Tj
2387  if(tj.AlgMod[kShowerTj]) ++wght;
2388  // ShowerLike
2389  if(tj.AlgMod[kShowerLike]) --wght;
2390  tjids.push_back(tjid);
2391  tjwts.push_back(wght);
2392  } // tjid
2393 
2394  if(tjids.empty()) return;
2395 
2396  float tjScore = 0;
2397  float sum = 0;
2398  float cnt = 0;
2399  for(unsigned short it1 = 0; it1 < tjids.size() - 1; ++it1) {
2400  Trajectory& tj1 = tjs.allTraj[tjids[it1] - 1];
2401  float wght1 = tjwts[it1];
2402  // the end that has a vertex
2403  unsigned short end1 = 0;
2404  if(tj1.VtxID[1] == vx2.ID) end1 = 1;
2405  unsigned short endPt1 = tj1.EndPt[end1];
2406  // bump up the weight if there is a Bragg peak at the other end
2407  unsigned short oend1 = 1 - end1;
2408  if(tj1.StopFlag[oend1][kBragg]) ++wght1;
2409  float ang1 = tj1.Pts[endPt1].Ang;
2410  float ang1Err2 = tj1.Pts[endPt1].AngErr * tj1.Pts[endPt1].AngErr;
2411  for(unsigned short it2 = it1 + 1; it2 < tjids.size(); ++it2) {
2412  Trajectory& tj2 = tjs.allTraj[tjids[it2] - 1];
2413  float wght2 = tjwts[it2];
2414  unsigned end2 = 0;
2415  if(tj2.VtxID[1] == vx2.ID) end2 = 1;
2416  // bump up the weight if there is a Bragg peak at the other end
2417  unsigned short oend2 = 1 - end2;
2418  if(tj2.StopFlag[oend2][kBragg]) ++wght2;
2419  unsigned short endPt2 = tj2.EndPt[end2];
2420  float ang2 = tj2.Pts[endPt2].Ang;
2421  float ang2Err2 = tj2.Pts[endPt2].AngErr * tj2.Pts[endPt2].AngErr;
2422  float dang = DeltaAngle(ang1, ang2);
2423  float dangErr = 0.5 * sqrt(ang1Err2 + ang2Err2);
2424  if((dang / dangErr) > 3 && wght1 > 0 && wght2 > 0) {
2425  sum += wght1 + wght2;
2426  ++cnt;
2427  }
2428  } // it2
2429  } // it1
2430  if(cnt > 0) {
2431  sum /= cnt;
2432  tjScore = tjs.VertexScoreWeights[3] * sum;
2433  }
2434  vx2.Score = vpeScore + m3DScore + cfScore + tjScore;
2435  if(prt) {
2436  // last call after vertices have been matched to the truth. Use to optimize VertexScoreWeights using
2437  // an ntuple
2438  mf::LogVerbatim myprt("TC");
2439  myprt<<" SVx2W 2V"<<vx2.ID;
2440  myprt<<" m3Dcnt "<<m3Dcnt;
2441  myprt<<" PosErr "<<std::fixed<<std::setprecision(2)<<(vx2.PosErr[0] + vx2.PosErr[1]);
2442  myprt<<" TjChgFrac "<<std::fixed<<std::setprecision(3)<<vx2.TjChgFrac;
2443  myprt<<" sum "<<std::fixed<<std::setprecision(1)<<sum;
2444  myprt<<" cnt "<<(int)cnt;
2445  myprt<<" Score "<<vx2.Score;
2446  }
2447  } // SetVx2Score
2448 
2450  unsigned short Vx3Topo(TjStuff& tjs, Vtx3Store& vx3)
2451  {
2452  // Returns the most common value of Topo for the 2D vertices that are matched
2453  // to this 3D vertex. This **might** be a useful measure to identify neutrino interaction
2454  // vertices
2455 
2456  if(vx3.ID == 0) return USHRT_MAX;
2457  // Consider Topo values between 0 and 9
2458  std::array<short, 10> cnts;
2459  cnts.fill(0);
2460  for(auto vx2id : vx3.Vx2ID) {
2461  if(vx2id == 0) continue;
2462  auto& vx2 = tjs.vtx[vx2id - 1];
2463  if(vx2.Topo < 0 || vx2.Topo > 9) continue;
2464  ++cnts[vx2.Topo];
2465  } // vx2id
2466  short most = 0;
2467  unsigned short theMost = USHRT_MAX;
2468  for(unsigned short itp = 0; itp < 10; ++itp) {
2469  if(cnts[itp] > most) {
2470  most = cnts[itp];
2471  theMost = itp;
2472  }
2473  } // itp
2474  return theMost;
2475  } // Vx3Topo
2476 
2479  {
2480 
2481  if(!tjs.UseAlg[kComp3DVxIG]) return;
2482  if(tjs.NumPlanes != 3) return;
2483 
2484  bool prt = (debug.Plane >= 0 && debug.Tick == 44444);
2485  if(prt) mf::LogVerbatim("TC")<<"Inside CI3DVIG:";
2486 
2487  for(unsigned short iv3 = 0; iv3 < tjs.vtx3.size(); ++iv3) {
2488  Vtx3Store& vx3 = tjs.vtx3[iv3];
2489  // ignore obsolete vertices
2490  if(vx3.ID == 0) continue;
2491  if(vx3.TPCID != tpcid) continue;
2492  // check for a completed 3D vertex
2493  if(vx3.Wire < 0) continue;
2494  unsigned short mPlane = USHRT_MAX;
2495  for(unsigned short ipl = 0; ipl < tjs.NumPlanes; ++ipl) {
2496  if(vx3.Vx2ID[ipl] > 0) continue;
2497  mPlane = ipl;
2498  break;
2499  } // ipl
2500  if(mPlane == USHRT_MAX) continue;
2501 // CTP_t mCTP = EncodeCTP(vx3.CStat, vx3.TPC, mPlane);
2502  CTP_t mCTP = EncodeCTP(vx3.TPCID.Cryostat, vx3.TPCID.TPC, mPlane);
2503  // require that the missing vertex be in a large block of dead wires
2504  float dwc = DeadWireCount(tjs, vx3.Wire - 4, vx3.Wire + 4, mCTP);
2505  if(dwc < 5) continue;
2506  // X position of the purported missing vertex
2507  // A TP for the missing 2D vertex
2508  VtxStore aVtx;
2509  aVtx.ID = tjs.vtx.size() + 1;
2510  aVtx.Pos[0] = vx3.Wire;
2511  aVtx.Pos[1] = tjs.detprop->ConvertXToTicks(vx3.X, mPlane, vx3.TPCID.TPC, vx3.TPCID.Cryostat) * tjs.UnitsPerTick;
2512  aVtx.CTP = mCTP;
2513  aVtx.Topo = 4;
2514  aVtx.NTraj = 0;
2515  // Give it a bogus pass to indicate it wasn't created while stepping
2516  aVtx.Pass = 9;
2517  if(prt) mf::LogVerbatim("TC")<<"CI3DVIG: Incomplete vertex "<<iv3<<" in plane "<<mPlane<<" wire "<<vx3.Wire<<" Made 2D vertex ";
2518  std::vector<int> tjIDs;
2519  std::vector<unsigned short> tjEnds;
2520  for(unsigned short itj = 0; itj < tjs.allTraj.size(); ++itj) {
2521  if(tjs.allTraj[itj].CTP != mCTP) continue;
2522  if(tjs.allTraj[itj].AlgMod[kKilled]) continue;
2523  for(unsigned short end = 0; end < 2; ++end) {
2524  unsigned short ept = tjs.allTraj[itj].EndPt[end];
2525  TrajPoint& tp = tjs.allTraj[itj].Pts[ept];
2526  unsigned short oept = tjs.allTraj[itj].EndPt[1 - end];
2527  TrajPoint& otp = tjs.allTraj[itj].Pts[oept];
2528  // ensure that this is the end closest to the vertex
2529  if(std::abs(tp.Pos[0] - aVtx.Pos[0]) > std::abs(otp.Pos[0] - aVtx.Pos[0])) continue;
2530  float doca = PointTrajDOCA(tjs, aVtx.Pos[0], aVtx.Pos[1], tp);
2531  if(doca > 2) continue;
2532  float dwc = DeadWireCount(tjs, aVtx.Pos[0], tp.Pos[0], tp.CTP);
2533  float ptSep;
2534  if(aVtx.Pos[0] > tp.Pos[0]) {
2535  ptSep = aVtx.Pos[0] - tp.Pos[0] - dwc;
2536  } else {
2537  ptSep = tp.Pos[0] - aVtx.Pos[0] - dwc;
2538  }
2539  if(prt) mf::LogVerbatim("TC")<<"CI3DVIG: tj ID "<<tjs.allTraj[itj].ID<<" doca "<<doca<<" ptSep "<<ptSep;
2540  if(ptSep < -2 || ptSep > 2) continue;
2541  // don't clobber an existing association
2542  if(tjs.allTraj[itj].VtxID[end] > 0) continue;
2543  tjIDs.push_back(tjs.allTraj[itj].ID);
2544  tjEnds.push_back(end);
2545  } // end
2546  } // itj
2547  if(!tjIDs.empty()) {
2548  // Determine how messy this region is
2549  aVtx.TjChgFrac = ChgFracNearPos(tjs, aVtx.Pos, tjIDs);
2550  if(aVtx.TjChgFrac < 0.7) continue;
2551  aVtx.Vx3ID = vx3.ID;
2552  // Save the 2D vertex
2553  if(!StoreVertex(tjs, aVtx)) continue;
2554  for(unsigned short ii = 0; ii < tjIDs.size(); ++ii) {
2555  unsigned short itj = tjIDs[ii] - 1;
2556  tjs.allTraj[itj].VtxID[tjEnds[ii]] = aVtx.ID;
2557  tjs.allTraj[itj].AlgMod[kComp3DVxIG] = true;
2558  } // ii
2559  SetVx2Score(tjs, prt);
2560  vx3.Vx2ID[mPlane] = aVtx.ID;
2561  vx3.Wire = -1;
2562  if(prt) mf::LogVerbatim("TC")<<"CI3DVIG: new vtx 2V"<<aVtx.ID<<" points to 3V"<<vx3.ID;
2563  }
2564  } // vx3
2565 
2566  } // CompleteIncomplete3DVerticesInGaps
2567 
2570  {
2571  // Look for trajectories in a plane that lack a 2D vertex as listed in
2572  // 2DVtxID that are near the projected wire. This may trigger splitting trajectories,
2573  // assigning them to a new 2D vertex and completing 3D vertices
2574 
2575  if(!tjs.UseAlg[kComp3DVx]) return;
2576  if(tjs.NumPlanes != 3) return;
2577 
2578  bool prt = (debug.Plane >= 0 && debug.Tick == 33333);
2579 
2580  float maxdoca = 3;
2581  if(prt) mf::LogVerbatim("TC")<<"Inside CI3DV with maxdoca set to "<<maxdoca;
2582  unsigned short ivx3 = 0;
2583  for(auto& vx3 : tjs.vtx3) {
2584  // ignore obsolete vertices
2585  if(vx3.ID == 0) continue;
2586  if(vx3.TPCID != tpcid) continue;
2587  // check for a completed 3D vertex
2588  if(vx3.Wire < 0) continue;
2589  unsigned short mPlane = USHRT_MAX;
2590  for(unsigned short plane = 0; plane < tjs.NumPlanes; ++plane) {
2591  if(vx3.Vx2ID[plane] > 0) continue;
2592  mPlane = plane;
2593  } // ipl
2594  if(mPlane == USHRT_MAX) continue;
2595  CTP_t mCTP = EncodeCTP(vx3.TPCID.Cryostat, vx3.TPCID.TPC, mPlane);
2596  // X position of the purported missing vertex
2597  // A TP for the missing 2D vertex
2598  TrajPoint vtp;
2599  vtp.Pos[0] = vx3.Wire;
2600  vtp.Pos[1] = tjs.detprop->ConvertXToTicks(vx3.X, mPlane, vx3.TPCID.TPC, vx3.TPCID.Cryostat) * tjs.UnitsPerTick;
2601  if(prt) mf::LogVerbatim("TC")<<"CI3DV 3V"<<vx3.ID<<" Pos "<<mPlane<<":"<<PrintPos(tjs, vtp.Pos);
2602  std::vector<int> tjIDs;
2603  std::vector<unsigned short> tjPts;
2604  for(auto& tj : tjs.allTraj) {
2605  if(tj.CTP != mCTP) continue;
2606  if(tj.AlgMod[kKilled]) continue;
2607  if(tj.Pts.size() < 6) continue;
2608  if(tj.AlgMod[kComp3DVx]) continue;
2609  float doca = maxdoca;
2610  // find the closest distance between the vertex and the trajectory
2611  unsigned short closePt = 0;
2612  TrajPointTrajDOCA(tjs, vtp, tj, closePt, doca);
2613  if(closePt > tj.EndPt[1]) continue;
2614  // try to improve the location of the vertex by looking for a distinctive feature on the
2615  // trajectory, e.g. high multiplicity hits or larger than normal charge
2616  if(RefineVtxPosition(tjs, tj, closePt, 3, false)) vtp.Pos = tj.Pts[closePt].Pos;
2617  if(prt) mf::LogVerbatim("TC")<<"CI3DV 3V"<<vx3.ID<<" candidate itj ID "<<tj.ID<<" vtx pos "<<PrintPos(tjs, vtp.Pos)<<" doca "<<doca;
2618  tjIDs.push_back(tj.ID);
2619  tjPts.push_back(closePt);
2620  } // itj
2621  if(tjIDs.empty()) continue;
2622  // compare the length of the Tjs used to make the vertex with the length of the
2623  // Tj that we want to split. Don't allow a vertex using very short Tjs to split a long
2624  // Tj in the 3rd plane
2625 // float score;
2626 // auto vxtjs = GetVtxTjIDs(tjs, vx3, score);
2627  auto vxtjs = GetAssns(tjs, "3V", vx3.ID, "T");
2628  unsigned short maxPts = 0;
2629  for(auto tjid : vxtjs) {
2630  auto& tj = tjs.allTraj[tjid - 1];
2631  unsigned short npwc = NumPtsWithCharge(tjs, tj, false);
2632  if(npwc > maxPts) maxPts = npwc;
2633  } // tjid
2634  // skip this operation if any of the Tjs in the split list are > 3 * maxPts
2635  maxPts *= 3;
2636  bool skipit = false;
2637  for(auto tjid : tjIDs) {
2638  auto& tj = tjs.allTraj[tjid - 1];
2639  if(NumPtsWithCharge(tjs, tj, false) > maxPts) skipit = true;
2640  } // tjid
2641  if(prt) mf::LogVerbatim("TC")<<" maxPts "<<maxPts<<" skipit? "<<skipit;
2642  if(skipit) continue;
2643  // 2D vertex
2644  VtxStore aVtx;
2645  unsigned short newVtxIndx = tjs.vtx.size();
2646  aVtx.ID = newVtxIndx + 1;
2647  aVtx.CTP = mCTP;
2648  aVtx.Topo = 3;
2649  aVtx.NTraj = 0;
2650  // Give it a bogus pass to indicate it wasn't created while stepping
2651  aVtx.Pass = 9;
2652  aVtx.Pos = vtp.Pos;
2653  // ensure this isn't in a messy region
2654  aVtx.TjChgFrac = ChgFracNearPos(tjs, aVtx.Pos, tjIDs);
2655  if(prt) mf::LogVerbatim("TC")<<" charge fraction near position "<<aVtx.TjChgFrac;
2656  if(aVtx.TjChgFrac < 0.6) continue;
2657  if(!StoreVertex(tjs, aVtx)) continue;
2658  // make a reference to the new vertex
2659  VtxStore& newVtx = tjs.vtx[tjs.vtx.size()-1];
2660  if(prt) mf::LogVerbatim("TC")<<" Stored 2D vertex "<<newVtx.ID;
2661  // make a temporary copy so we can nudge it a bit if there is only one Tj
2662  std::array<float, 2> vpos = aVtx.Pos;
2663  for(unsigned short ii = 0; ii < tjIDs.size(); ++ii) {
2664  unsigned short itj = tjIDs[ii] - 1;
2665  // Don't use a reference variable since it may get scrambled after SplitTraj
2666 // auto& tj = tjs.allTraj[itj];
2667  unsigned short closePt = tjPts[ii];
2668  // determine which end is the closest
2669  unsigned short end = 1;
2670  // closest to the beginning?
2671  if(fabs(closePt - tjs.allTraj[itj].EndPt[0]) < fabs(closePt - tjs.allTraj[itj].EndPt[1])) end = 0;
2672  short dpt = fabs(closePt - tjs.allTraj[itj].EndPt[end]);
2673  if(dpt < 3) {
2674  // close to an end
2675  if(tjs.allTraj[itj].VtxID[end] > 0) {
2676  if(prt) mf::LogVerbatim("TC")<<" T"<<tjs.allTraj[itj].ID<<" has a vertex "<<tjs.allTraj[itj].VtxID[end]<<" at end "<<end<<". Skip it";
2677  continue;
2678  }
2679  tjs.allTraj[itj].VtxID[end] = tjs.vtx[newVtxIndx].ID;
2680  ++newVtx.NTraj;
2681  if(prt) mf::LogVerbatim("TC")<<" attach Traj T"<<tjs.allTraj[itj].ID<<" at end "<<end;
2682  tjs.allTraj[itj].AlgMod[kComp3DVx] = true;
2683  vpos = tjs.allTraj[itj].Pts[tjs.allTraj[itj].EndPt[end]].Pos;
2684  } else {
2685  // closePt is not near an end, so split the trajectory
2686  if(SplitTraj(tjs, itj, closePt, newVtxIndx, prt)) {
2687  if(prt) mf::LogVerbatim("TC")<<" SplitTraj success 2V"<<tjs.vtx[newVtxIndx].ID<<" at closePt "<<closePt;
2688  // successfully split the Tj
2689  newVtx.NTraj += 2;
2690  } else {
2691  // split failed. Give up
2692  if(prt) mf::LogVerbatim("TC")<<" SplitTraj failed";
2693  newVtx.NTraj = 0;
2694  break;
2695  }
2696  // Update the PDGCode for the chopped trajectory
2697  SetPDGCode(tjs, itj);
2698  // and for the new trajectory
2699  SetPDGCode(tjs, tjs.allTraj.size()-1);
2700  } // closePt is not near an end, so split the trajectory
2701  tjs.allTraj[itj].AlgMod[kComp3DVx] = true;
2702  unsigned short newtj = tjs.allTraj.size() - 1;
2703  tjs.allTraj[newtj].AlgMod[kComp3DVx] = true;
2704  } // ii
2705  if(newVtx.NTraj == 0) {
2706  // A failure occurred. Recover
2707  if(prt) mf::LogVerbatim("TC")<<" Failed. Recover and delete vertex "<<newVtx.ID;
2708  MakeVertexObsolete(tjs, newVtx, true);
2709  } else {
2710  // success
2711  vx3.Vx2ID[mPlane] = newVtx.ID;
2712  newVtx.Vx3ID = vx3.ID;
2713  vx3.Wire = -1;
2714  // set the vertex position to the start of the Tj if there is only one and fix it
2715  if(newVtx.NTraj == 1) {
2716  newVtx.Pos = vpos;
2717  newVtx.Stat[kFixed] = true;
2718  }
2719  AttachAnyTrajToVertex(tjs, newVtx.ID - 1, prt);
2720  SetVx2Score(tjs, prt);
2721  if(prt) {
2722  mf::LogVerbatim myprt("TC");
2723  myprt<<" Success: new 2V"<<newVtx.ID<<" at "<<(int)newVtx.Pos[0]<<":"<<(int)newVtx.Pos[1]/tjs.UnitsPerTick;
2724  myprt<<" points to 3V"<<vx3.ID;
2725  myprt<<" TjIDs:";
2726  for(auto& tjID : tjIDs) myprt<<" T"<<std::to_string(tjID);
2727  } // prt
2728  } // success
2729  ++ivx3;
2730  } // vx3
2731 
2732  } // CompleteIncomplete3DVertices
2733 
2735  bool RefineVtxPosition(TjStuff& tjs, const Trajectory& tj, unsigned short& nearPt, short nPtsToChk, bool prt)
2736  {
2737  // The tj has been slated to be split somewhere near point nearPt. This function will move
2738  // the near point a bit to the most likely point of a vertex
2739 
2740  float maxChg = tj.Pts[nearPt].Chg;
2741  short maxChgPt = nearPt;
2742 
2743  for(short ipt = nearPt - nPtsToChk; ipt < nearPt + nPtsToChk; ++ipt) {
2744  if(ipt < tj.EndPt[0] || ipt > tj.EndPt[1]) continue;
2745  auto& tp = tj.Pts[ipt];
2746  if(tp.Chg > maxChg) {
2747  maxChg = tp.Chg;
2748  maxChgPt = ipt;
2749  }
2750  if(prt) mf::LogVerbatim("TC")<<"RVP: ipt "<<ipt<<" Pos "<<tp.CTP<<":"<<PrintPos(tjs, tp.Pos)<<" chg "<<(int)tp.Chg<<" nhits "<<tp.Hits.size();
2751  } // ipt
2752  if(nearPt == maxChgPt) return false;
2753  nearPt = maxChgPt;
2754  return true;
2755  } //RefineVtxPosition
2756 
2758  void VtxHitsSwap(TjStuff& tjs, const CTP_t inCTP){
2759 
2760  if(!tjs.UseAlg[kVtxHitsSwap]) return;
2761 
2762  geo::PlaneID planeID = DecodeCTP(inCTP);
2763 
2764  bool vtxPrt = ((debug.Plane == (int)planeID.Plane || debug.Plane == 3) && debug.Tick < 0);
2765  if(vtxPrt) mf::LogVerbatim("TC")<<"inside VtxHitsSwap on plane "<<planeID.Plane;
2766  for (unsigned short iv = 0; iv < tjs.vtx.size(); ++iv){
2767  VtxStore& rvx = tjs.vtx[iv];
2768  if(rvx.CTP != inCTP) continue;
2769  // Only consider vertex with two trajectories
2770  if(rvx.NTraj != 2) continue;
2771  if (vtxPrt) mf::LogVerbatim("TC")<<"Vertex "<<iv<<" Pos[0]: "<<rvx.Pos[0]<<" "<<rvx.Pos[1];
2772  std::array<unsigned short, 2> tjlist{{0,0}};
2773  for(unsigned short itj = 0; itj < tjs.allTraj.size(); ++itj) {
2774  if(tjs.allTraj[itj].AlgMod[kKilled]) continue;
2775  if(tjs.allTraj[itj].CTP != rvx.CTP) continue;
2776  Trajectory& tj = tjs.allTraj[itj];
2777  // ensure that the ID is OK so the code below doesn't choke
2778  for(unsigned short end = 0; end < 2; ++end) {
2779  if(tj.VtxID[end] == rvx.ID) {
2780  tjlist[end] = itj;
2781  }
2782  }
2783  }//all trajectories
2784 
2785  //Ignore short trajectories
2786  if (tjs.allTraj[tjlist[0]].EndPt[1]<5||
2787  tjs.allTraj[tjlist[1]].EndPt[1]<5) continue;
2788 
2789  for (unsigned short i = 0; i<2; ++i){
2790 
2791  //First check if first hit should be swapped
2792  Trajectory& tj0 = tjs.allTraj[tjlist[i]];
2793  Trajectory& tj1 = tjs.allTraj[tjlist[1-i]];
2794  unsigned short endPt0 = tjs.allTraj[tjlist[i]].EndPt[i];
2795  unsigned short endPt1 = tjs.allTraj[tjlist[1-i]].EndPt[1-i];
2796  //first TP on first trajectory
2797  float chg0 = TpSumHitChg(tjs, tj0.Pts[endPt0]);
2798  float w0 = tj0.Pts[endPt0].Pos[0];
2799  //if (vtxPrt) mf::LogVerbatim("TC")<<PrintPos(tjs, tj0.Pts[endPt0]);
2800  //second TP on first trajectory
2801  float chg1 = 0;
2802  float w1 = 0;
2803  unsigned short j = endPt0;
2804  while (j!=tj0.EndPt[1-i]){
2805  if (i==0) ++j;
2806  else --j;
2807  if (tj0.Pts[j].Chg){
2808  chg1 = TpSumHitChg(tjs, tj0.Pts[j]);
2809  w1 = tj0.Pts[j].Pos[0];
2810  //if (vtxPrt) mf::LogVerbatim("TC")<<PrintPos(tjs, tj0.Pts[j]);
2811  break;
2812  }
2813  }
2814  //first TP on second trajectory
2815  float chg2 = TpSumHitChg(tjs,tj1.Pts[endPt1]);
2816  float w2 = tj1.Pts[endPt1].Pos[0];
2817  //DOCA between first TP on first TJ and first TP on second TJ
2818  float delta = 1000;
2819  for (size_t k = 0; k<tj0.Pts[endPt0].Hits.size(); ++k){
2820  if (!tj0.Pts[endPt0].UseHit[k]) continue;
2821  float this_delta = PointTrajDOCA(tjs, tj0.Pts[endPt0].Hits[k], tj1.Pts[endPt1]);
2822  if (this_delta<delta) delta = this_delta;
2823  }
2824  // if (vtxPrt) mf::LogVerbatim("TC")<<PrintPos(tjs, tj1.Pts[endPt1]);
2825  //if (vtxPrt) mf::LogVerbatim("TC")<<chg0<<" "<<chg1<<" "<<chg2<<" "<<delta;
2826  if (chg0>0&&std::abs((chg0-chg1)/chg0)-std::abs((chg0-chg2)/chg0)>0.2&&delta<1.5&&std::abs(w2-w1)<1.5){
2827  if (vtxPrt) {
2828  mf::LogVerbatim("TC")<<"chg0 = "<<chg0<<" chg1 = "<<chg1<<" chg2 = "<<chg2<<" delta = "<<delta<<" w0 = "<<w0<<" w1 = "<<w1<<" w2 = "<<w2;
2829  mf::LogVerbatim("TC")<<"VHS Moving TP "<<PrintPos(tjs, tj0.Pts[endPt0])<<" from TJ "<<tj0.ID<<" to TJ "<<tj1.ID;
2830  }
2831  //Add first TP of first trajectory to the second trajectory
2832  TrajPoint tp = tj0.Pts[endPt0];
2833  for (size_t j = 0; j<tp.Hits.size(); ++j){
2834  if (!tp.UseHit[j]) continue;
2835  tjs.fHits[tp.Hits[j]].InTraj = tj1.ID;
2836  }
2837  if (i==0){
2838  //append to the end
2839  tj1.Pts.push_back(tp);
2840  }
2841  else{
2842  //insert at the beginning
2843  tj1.Pts.insert(tj1.Pts.begin(), tp);
2844  }
2845  SetEndPoints(tjs, tj1);
2846 
2847  //Remove first TP from first trajectory
2848  tj0.Pts[endPt0].Chg = 0;
2849  for (size_t j = 0; j<tj0.Pts[endPt0].Hits.size(); ++j){
2850  tj0.Pts[endPt0].UseHit[j] = false;
2851  }
2852  SetEndPoints(tjs, tj0);
2853  tj0.AlgMod[kVtxHitsSwap] = true;
2854  tj1.AlgMod[kVtxHitsSwap] = true;
2855  break;
2856  }
2857 
2858  //Now Check if the beginning of the first trajectory should be moved to the second trajectory.
2859  j = endPt0;
2860  std::vector<unsigned short> tplist;
2861  while (j!=tj0.EndPt[1-i]){
2862  if (tj0.Pts[j].Chg){
2863  float delta = 1000;
2864  for (size_t k = 0; k<tj0.Pts[j].Hits.size(); ++k){
2865  if (!tj0.Pts[j].UseHit[k]) continue;
2866  float this_delta = PointTrajDOCA(tjs, tj0.Pts[j].Hits[k], tj1.Pts[endPt1]);
2867  if (this_delta<delta) delta = this_delta;
2868  //if (vtxPrt) mf::LogVerbatim("TC")<<j<<" "<<k<<" "<<PrintPos(tjs, tj0.Pts[j])<<" "<<PointTrajDOCA(tjs, tj0.Pts[j].Hits[k], tj1.Pts[endPt1]);
2869  }
2870  if (delta < 0.3 && tj0.Pts[j].Delta > 1.0 && (j==endPt0 || !tplist.empty())){
2871  tplist.push_back(j);
2872  }
2873  else break;
2874  }
2875  if (i==0) ++j;
2876  else --j;
2877  }
2878  //if (vtxPrt) mf::LogVerbatim("TC")<<tplist.size();
2879  //Need at least two TPs to make this change
2880  if (tplist.size()>1){
2881  if (vtxPrt) mf::LogVerbatim("TC")<<"VHS Moving "<<tplist.size()<<" TPs from TJ "<<tj0.ID<<" to TJ "<<tj1.ID;
2882  //Append TPs to the second TJ
2883  for (unsigned short j = 0; j<tplist.size(); ++j){
2884  TrajPoint tp = tj0.Pts[tplist[j]];
2885  for (size_t k = 0; k<tp.Hits.size(); ++k){
2886  if (!tp.UseHit[k]) continue;
2887  tjs.fHits[tp.Hits[k]].InTraj = tj1.ID;
2888  }
2889  if (i==0){
2890  //append to the end
2891  tj1.Pts.push_back(tp);
2892  }
2893  else{
2894  //insert at the beginning
2895  tj1.Pts.insert(tj1.Pts.begin(), tp);
2896  }
2897  }
2898  SetEndPoints(tjs, tj1);
2899 
2900  //Remove TPs from first trajectory
2901  for (unsigned short j = 0; j<tplist.size(); ++j){
2902  tj0.Pts[tplist[j]].Chg = 0;
2903  for (size_t k = 0; k<tj0.Pts[tplist[j]].Hits.size(); ++k){
2904  tj0.Pts[tplist[j]].UseHit[k] = false;
2905  }
2906  }
2907  SetEndPoints(tjs, tj0);
2908  tj0.AlgMod[kVtxHitsSwap] = true;
2909  tj1.AlgMod[kVtxHitsSwap] = true;
2910  break;
2911  }
2912  }//loop over two trajectories
2913  }//loop over vertices
2914  }
2915 
2917  bool MakeVertexObsolete(TjStuff& tjs, VtxStore& vx2, bool forceKill)
2918  {
2919  // Makes a 2D vertex obsolete
2920 
2921  // check for a high-score 3D vertex
2922  bool hasHighScoreVx3 = (vx2.Vx3ID > 0);
2923  if(hasHighScoreVx3 && !forceKill && tjs.vtx3[vx2.Vx3ID - 1].Score >= tjs.Vertex2DCuts[7]) return false;
2924 
2925  // Kill it
2926  int vx2id = vx2.ID;
2927  if(vx2.Vx3ID > 0) {
2928  auto& vx3 = tjs.vtx3[vx2.Vx3ID - 1];
2929  for(auto& v3v2id : vx3.Vx2ID) if(v3v2id == vx2.ID) v3v2id = 0;
2930  }
2931  vx2.ID = 0;
2932  for(auto& tj : tjs.allTraj) {
2933  if(tj.AlgMod[kKilled]) continue;
2934  for(unsigned short end = 0; end < 2; ++end) {
2935  if(tj.VtxID[end] != vx2id) continue;
2936  tj.VtxID[end] = 0;
2937  tj.AlgMod[kPhoton] = false;
2938  // clear the kEnvOverlap bits on the TPs
2939  for(unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
2940  if(end == 0) {
2941  unsigned short ipt = tj.EndPt[0] + ii;
2942  auto& tp = tj.Pts[ipt];
2943  if(!tp.Environment[kEnvOverlap]) break;
2944  if(ipt == tj.EndPt[1]) break;
2945  } else {
2946  unsigned short ipt = tj.EndPt[1] - ii;
2947  auto& tp = tj.Pts[ipt];
2948  if(!tp.Environment[kEnvOverlap]) break;
2949  if(ipt == tj.EndPt[0]) break;
2950  }
2951  } // ii
2952  if(tj.AlgMod[kTjHiVx3Score]) {
2953  // see if the vertex at the other end is high-score and if so, preserve the state
2954  unsigned short oend = 1 - end;
2955  if(tj.VtxID[oend] > 0) {
2956  auto& ovx2 = tjs.vtx[tj.VtxID[oend] - 1];
2957  if(!ovx2.Stat[kHiVx3Score]) tj.AlgMod[kTjHiVx3Score] = false;
2958  } // vertex at the other end
2959  } // tj.AlgMod[kTjHiVx3Score]
2960  } // end
2961  } // tj
2962 
2963  if(!hasHighScoreVx3) return true;
2964 
2965  // update the affected 3D vertex
2966  Vtx3Store& vx3 = tjs.vtx3[vx2.Vx3ID - 1];
2967  // make the 3D vertex incomplete
2968  geo::PlaneID planeID = DecodeCTP(vx2.CTP);
2969  unsigned short plane = planeID.Plane;
2970  if(vx3.Vx2ID[plane] != vx2id) return true;
2971  vx3.Vx2ID[plane] = 0;
2972  vx3.Wire = vx2.Pos[0];
2973  // Ensure that there are at least two 2D vertices left
2974  unsigned short n2D = 0;
2975  for(unsigned short plane = 0; plane < tjs.NumPlanes; ++plane) {
2976  if(vx3.Vx2ID[plane] > 0) ++n2D;
2977  } // plane
2978 
2979  if(n2D > 1) {
2980  // 3D vertex is incomplete
2981  // correct the score
2982  SetVx3Score(tjs, vx3, false);
2983  return true;
2984  }
2985 
2986  // 3D vertex is obsolete
2987  // Detach the all remaining 2D vertices from the 3D vertex
2988  for(auto& vx2 : tjs.vtx) {
2989  if(vx2.ID == 0) continue;
2990  if(vx2.Vx3ID == vx3.ID) vx2.Vx3ID = 0;
2991  } // vx2
2992  for(auto& pfp : tjs.pfps) {
2993  for(unsigned short end = 0; end < 2; ++end) if(pfp.Vx3ID[end] == vx3.ID) pfp.Vx3ID[end] = 0;
2994  } // pfp
2995  vx3.ID = 0;
2996  return true;
2997 
2998  } // MakeVertexObsolete
2999 
3002  {
3003  // Deletes a 3D vertex and 2D vertices in all planes
3004  // The 2D and 3D vertices are NOT killed if forceKill is false and the 3D vertex
3005  // has a high score
3006  if(vx3.ID == 0) return true;
3007  if(vx3.ID > int(tjs.vtx3.size())) return false;
3008 
3009  // set the score to 0
3010 // vx3.Score = 0;
3011 
3012  for(auto vx2id : vx3.Vx2ID) {
3013  if(vx2id == 0 || vx2id > (int)tjs.vtx.size()) continue;
3014  auto& vx2 = tjs.vtx[vx2id - 1];
3015  MakeVertexObsolete(tjs, vx2, true);
3016  }
3017  vx3.ID = 0;
3018  return true;
3019  } // MakeVertexObsolete
3020 
3022  std::vector<int> GetVtxTjIDs(const TjStuff& tjs, const VtxStore& vx2)
3023  {
3024  // returns a list of trajectory IDs that are attached to vx2
3025  std::vector<int> tmp;
3026  if(vx2.ID == 0) return tmp;
3027  for(auto& tj : tjs.allTraj) {
3028  if(tj.AlgMod[kKilled]) continue;
3029  if(tj.CTP != vx2.CTP) continue;
3030  for(unsigned short end = 0; end < 2; ++end) {
3031  if(tj.VtxID[end] == vx2.ID) tmp.push_back(tj.ID);
3032  } // end
3033  } // tj
3034  return tmp;
3035  } // GetVtxTjIDs
3036 
3037 
3039  std::vector<int> GetVtxTjIDs(const TjStuff& tjs, const Vtx3Store& vx3, float& score)
3040  {
3041  // returns a list of Tjs in all planes that are attached to vx3
3042  std::vector<int> tmp;
3043  if(vx3.ID == 0) return tmp;
3044  float nvx2 = 0;
3045  score = 0;
3046  for(auto& vx2 : tjs.vtx) {
3047  if(vx2.ID == 0) continue;
3048  if(vx2.Vx3ID != vx3.ID) continue;
3049  auto vtxTjID2 = GetVtxTjIDs(tjs, vx2);
3050  tmp.insert(tmp.end(), vtxTjID2.begin(), vtxTjID2.end());
3051  score += vx2.Score;
3052  ++nvx2;
3053  } // vx2
3054  if(nvx2 < 1) return tmp;
3055  // find the average score
3056  score /= nvx2;
3057  // sort by increasing ID
3058  std::sort(tmp.begin(), tmp.end());
3059  return tmp;
3060  } // GetVtxTjIDs
3061 
3063  std::vector<unsigned short> GetPFPVertices(const TjStuff& tjs, const PFPStruct& pfp)
3064  {
3065  // returns a list of 3D vertices that are attached to Tjs in this pfp. No check is
3066  // made of the actual vertex attachment of the pfp.
3067  std::vector<unsigned short> tmp;
3068  if(pfp.TjIDs.empty()) return tmp;
3069  for(auto tjid : pfp.TjIDs) {
3070  auto& tj = tjs.allTraj[tjid - 1];
3071  for(unsigned short end = 0; end < 2; ++end) {
3072  if(tj.VtxID[end] == 0) continue;
3073  auto& vx2 = tjs.vtx[tj.VtxID[end] - 1];
3074  if(vx2.Vx3ID == 0) continue;
3075  if(std::find(tmp.begin(), tmp.end(), vx2.Vx3ID) != tmp.end()) continue;
3076  tmp.push_back(vx2.Vx3ID);
3077  } // end
3078  } // tjid
3079  return tmp;
3080  } // GetPFPVertices
3081 
3083  void PosInPlane(const TjStuff& tjs, const Vtx3Store& vx3, unsigned short plane, Point2_t& pos)
3084  {
3085  // returns the 2D position of the vertex in the plane
3086  pos[0] = tjs.geom->WireCoordinate(vx3.Y, vx3.Z, plane, vx3.TPCID.TPC, vx3.TPCID.Cryostat);
3087  pos[1] = tjs.detprop->ConvertXToTicks(vx3.X, plane, vx3.TPCID.TPC, vx3.TPCID.Cryostat) * tjs.UnitsPerTick;
3088 
3089  } // PosInPlane
3090 
3091 
3093  unsigned short IsCloseToVertex(TjStuff& tjs, VtxStore& inVx2)
3094  {
3095  // Returns the ID of a 2D vertex having the minimum pull < user-specified cut
3096 
3097  float minPull = tjs.Vertex2DCuts[3];
3098  unsigned short imBest = 0;
3099  for(auto& vx2 : tjs.vtx) {
3100  float pull = VertexVertexPull(tjs, inVx2, vx2);
3101  if(pull < minPull) {
3102  minPull = pull;
3103  imBest = vx2.ID;
3104  }
3105  } // vx2
3106  return imBest;
3107  } // IsCloseToVertex
3108 
3110  unsigned short IsCloseToVertex(TjStuff& tjs, Vtx3Store& vx3)
3111  {
3112  // Returns the ID of a 3D vertex having the minimum pull < user-specified cut
3113 
3114  float minPull = tjs.Vertex3DCuts[1];
3115  unsigned short imBest = 0;
3116  for(auto& oldvx3 : tjs.vtx3) {
3117  if(oldvx3.ID == 0) continue;
3118  if(std::abs(oldvx3.X - vx3.X) > tjs.Vertex3DCuts[0]) continue;
3119  float pull = VertexVertexPull(tjs, vx3, oldvx3);
3120  if(pull < minPull) {
3121  minPull = pull;
3122  imBest = oldvx3.ID;
3123  }
3124  } // oldvx3
3125  return imBest;
3126 
3127  } // IsCloseToVertex
3128 
3129 } // namespace
TPCID()=default
Default constructor: an invalid TPC ID.
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.
bool valDecreasing(SortEntry c1, SortEntry c2)
Definition: TCVertex.cxx:10
Point2_t Pos
Definition: DataStructs.h:147
void SetVx3Score(TjStuff &tjs, Vtx3Store &vx3, bool prt)
Definition: TCVertex.cxx:2303
Point2_t PosErr
Definition: DataStructs.h:85
std::string PrintPos(const TjStuff &tjs, const TrajPoint &tp)
Definition: Utils.cxx:4738
MaybeLogger_< ELseverityLevel::ELsev_info, true > LogVerbatim
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)
Definition: Utils.cxx:2362
bool ChkVtxAssociations(TjStuff &tjs, const CTP_t &inCTP)
Definition: TCVertex.cxx:2128
float TrajPointVertexPull(TjStuff &tjs, const TrajPoint &tp, const VtxStore &vx)
Definition: TCVertex.cxx:1847
std::vector< float > Vertex2DCuts
Max position pull, max Position error rms.
Definition: DataStructs.h:509
struct of temporary 2D vertices (end points)
Definition: DataStructs.h:83
static unsigned int kWire
CTP_t CTP
Cryostat, TPC, Plane code.
Definition: DataStructs.h:145
std::array< double, 3 > Point3_t
Definition: DataStructs.h:35
void Find3DVertices(TjStuff &tjs, const geo::TPCID &tpcid)
Definition: TCVertex.cxx:1215
std::bitset< 64 > AlgMod
Bit set if algorithm AlgBit_t modifed the trajectory.
Definition: DataStructs.h:171
bool RefineVtxPosition(TjStuff &tjs, const Trajectory &tj, unsigned short &nearPt, short nPtsToChk, bool prt)
Definition: TCVertex.cxx:2735
void CompleteIncomplete3DVertices(TjStuff &tjs, const geo::TPCID &tpcid)
Definition: TCVertex.cxx:2569
void PrintAllTraj(std::string someText, const TjStuff &tjs, const DebugStuff &debug, unsigned short itj, unsigned short ipt, bool prtVtx)
Definition: Utils.cxx:4232
bool AnalyzePFP(TjStuff &tjs, PFPStruct &pfp, bool prt)
Definition: PFPUtils.cxx:2339
vertex position fixed manually - no fitting done
Definition: DataStructs.h:102
unsigned short NearestPtWithChg(TjStuff &tjs, Trajectory &tj, unsigned short thePt)
Definition: Utils.cxx:2889
float ChgFracBetween(TjStuff &tjs, Point3_t pos1, Point3_t pos2, geo::TPCID tpcid)
Definition: PFPUtils.cxx:2860
void SplitTrajCrossingVertices(TjStuff &tjs, CTP_t inCTP)
Definition: TCVertex.cxx:1091
Float_t y
Definition: compare.C:6
std::vector< float > MaxPos1
Definition: DataStructs.h:472
float TpSumHitChg(TjStuff &tjs, TrajPoint const &tp)
Definition: Utils.cxx:1678
std::array< std::bitset< 8 >, 2 > StopFlag
Definition: DataStructs.h:190
bool FitVertex(TjStuff &tjs, VtxStore &vx, bool prt)
Definition: TCVertex.cxx:1946
Double_t z
Definition: plot.C:279
Planes which measure X direction.
Definition: geo_types.h:81
The data type to uniquely identify a Plane.
Definition: geo_types.h:250
std::vector< PFPStruct > pfps
Definition: DataStructs.h:505
bool valIncreasing(SortEntry c1, SortEntry c2)
Definition: TCVertex.cxx:11
matched to a high-score 3D vertex
Definition: DataStructs.h:104
std::vector< unsigned int > Hits
Definition: DataStructs.h:162
bool AttachAnyTrajToVertex(TjStuff &tjs, unsigned short ivx, bool prt)
Definition: TCVertex.cxx:1689
unsigned short Pass
Definition: DataStructs.h:87
CryostatID_t Cryostat
Index of cryostat.
Definition: geo_types.h:130
void ChkVxTjs(TjStuff &tjs, const CTP_t &inCTP, bool prt)
Definition: TCVertex.cxx:715
bool TrajClosestApproach(Trajectory const &tj, float x, float y, unsigned short &closePt, float &DOCA)
Definition: Utils.cxx:2244
bool DefinePFP(std::string inFcnLabel, TjStuff &tjs, PFPStruct &pfp, bool prt)
Definition: PFPUtils.cxx:2159
Float_t tmp
Definition: plot.C:37
double DeltaAngle(const Vector3_t v1, const Vector3_t v2)
Definition: PFPUtils.cxx:1607
unsigned short NumPlanes
Definition: DataStructs.h:475
std::array< int, 2 > Vx3ID
Definition: DataStructs.h:246
float DeadWireCount(const TjStuff &tjs, const TrajPoint &tp1, const TrajPoint &tp2)
Definition: Utils.cxx:1756
bool CompatibleMerge(TjStuff &tjs, std::vector< int > &tjIDs, bool prt)
Definition: Utils.cxx:455
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.
std::vector< T > SetIntersection(const std::vector< T > &set1, const std::vector< T > &set2)
Definition: Utils.h:214
unsigned short IsCloseToVertex(TjStuff &tjs, VtxStore &inVx2)
Definition: TCVertex.cxx:3093
unsigned short FarEnd(const TjStuff &tjs, const PFPStruct &pfp, const Point3_t &pos)
Definition: PFPUtils.cxx:2936
bool SignalAtTp(TjStuff &tjs, const TrajPoint &tp)
Definition: Utils.cxx:1639
std::vector< unsigned short > GetPFPVertices(const TjStuff &tjs, const PFPStruct &pfp)
Definition: TCVertex.cxx:3063
virtual double ConvertXToTicks(double X, int p, int t, int c) const =0
bool SplitTraj(TjStuff &tjs, unsigned short itj, float XPos, bool makeVx2, bool prt)
Definition: Utils.cxx:1879
void SetHighScoreBits(TjStuff &tjs, Vtx3Store &vx3)
Definition: TCVertex.cxx:2256
bool AttachTrajToVertex(TjStuff &tjs, Trajectory &tj, VtxStore &vx, bool prt)
Definition: TCVertex.cxx:1712
void FindNeutralVertices(TjStuff &tjs, const geo::TPCID &tpcid)
Definition: TCVertex.cxx:369
struct of temporary 3D vertices
Definition: DataStructs.h:111
bool AttachPFPToVertex(TjStuff &tjs, PFPStruct &pfp, unsigned short end, unsigned short vx3ID, bool prt)
Definition: TCVertex.cxx:1651
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
DebugStuff debug
Definition: DebugStruct.cxx:4
void MakeJunkVertices(TjStuff &tjs, const CTP_t &inCTP)
Definition: TCVertex.cxx:14
CTP_t CTP
Cryostat, TPC, Plane code.
Definition: DataStructs.h:170
void Match3DVtxTjs(TjStuff &tjs, const geo::TPCID &tpcid, bool prt)
Definition: TCVertex.cxx:1506
std::vector< VtxStore > vtx
2D vertices
Definition: DataStructs.h:502
void VtxHitsSwap(TjStuff &tjs, const CTP_t inCTP)
Definition: TCVertex.cxx:2758
std::vector< TrajPoint > Pts
Trajectory points.
Definition: DataStructs.h:169
bool SignalBetween(TjStuff &tjs, const TrajPoint &tp1, const TrajPoint &tp2, const float &MinWireSignalFraction, bool prt)
Definition: Utils.cxx:1495
TrajPoint MakeBareTP(TjStuff &tjs, Point3_t &pos, Vector3_t &dir, CTP_t inCTP)
Definition: Utils.cxx:3435
std::vector< float > NeutralVxCuts
Definition: DataStructs.h:512
void Find2DVertices(TjStuff &tjs, const CTP_t &inCTP)
Definition: TCVertex.cxx:114
int Plane
Select plane.
Definition: DebugStruct.h:29
std::array< Vector3_t, 2 > Dir
Definition: DataStructs.h:242
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
void FindHammerVertices2(TjStuff &tjs, const CTP_t &inCTP)
Definition: TCVertex.cxx:819
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
std::vector< float > MaxPos0
Definition: DataStructs.h:471
double PosSep2(const Point3_t &pos1, const Point3_t &pos2)
Definition: PFPUtils.cxx:1631
unsigned int index
Definition: PFPUtils.cxx:8
Point2_t Pos
Definition: DataStructs.h:84
std::vector< MatchStruct > matchVec
3D matching vector
Definition: DataStructs.h:504
std::string PrintHit(const TCHit &hit)
Definition: Utils.cxx:4732
float UnitsPerTick
scale factor from Tick to WSE equivalent units
Definition: DataStructs.h:468
bool PointDirIntersect(Point3_t p1, Vector3_t p1Dir, Point3_t p2, Vector3_t p2Dir, Point3_t &intersect, float &doca)
Definition: PFPUtils.cxx:2769
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
bool IntersectionPoint(geo::WireID const &wid1, geo::WireID const &wid2, double &y, double &z) const
Returns the intersection point of two wires.
geo::TPCID TPCID
Definition: DataStructs.h:469
void ScoreVertices(TjStuff &tjs, const geo::TPCID &tpcid, bool prt)
Definition: TCVertex.cxx:2193
float PointTrajDOCA(TjStuff const &tjs, unsigned int iht, TrajPoint const &tp)
Definition: Utils.cxx:2139
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
short MCSMom(const TjStuff &tjs, const std::vector< int > &tjIDs)
Definition: Utils.cxx:2837
float ChgFracNearEnd(TjStuff &tjs, PFPStruct &pfp, unsigned short end)
Definition: PFPUtils.cxx:2890
void CompleteIncomplete3DVerticesInGaps(TjStuff &tjs, const geo::TPCID &tpcid)
Definition: TCVertex.cxx:2478
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
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
virtual double ConvertTicksToX(double ticks, int p, int t, int c) const =0
int Tick
Select hit PeakTime for debugging (< 0 for vertex finding)
Definition: DebugStruct.h:32
void TrajIntersection(TrajPoint const &tp1, TrajPoint const &tp2, Point2_t &pos)
Definition: Utils.cxx:2166
unsigned int CTP_t
Definition: DataStructs.h:41
std::vector< float > VertexScoreWeights
Definition: DataStructs.h:511
std::string to_string(Flag_t< Storage > const flag)
Convert a flag into a stream (shows its index).
Definition: BitMask.h:187
bool MergeWithVertex(TjStuff &tjs, VtxStore &vx, unsigned short oVxID, bool prt)
Definition: TCVertex.cxx:544
std::vector< TCHit > fHits
Definition: DataStructs.h:495
in close()
PFPStruct CreatePFP(const TjStuff &tjs, const geo::TPCID &tpcid)
Definition: PFPUtils.cxx:1943
TDirectory * dir
Definition: macro.C:5
unsigned short NTraj
Definition: DataStructs.h:86
geo::PlaneID DecodeCTP(CTP_t CTP)
Definition: DataStructs.cxx:89
void FindHammerVertices(TjStuff &tjs, const CTP_t &inCTP)
Definition: TCVertex.cxx:995
bool Split3DKink(TjStuff &tjs, PFPStruct &pfp, double sep, bool prt)
Definition: PFPUtils.cxx:1782
unsigned short MatchVecIndex
Definition: DataStructs.h:255
double PosSep(const Point3_t &pos1, const Point3_t &pos2)
Definition: PFPUtils.cxx:1625
std::vector< int > TjIDs
Definition: DataStructs.h:237
void KillPoorVertices(TjStuff &tjs, const geo::TPCID &tpcid)
Definition: TCVertex.cxx:2233
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
unsigned short NumPtsWithCharge(const TjStuff &tjs, const Trajectory &tj, bool includeDeadWires)
Definition: Utils.cxx:1738
bool MergeAndStore(TjStuff &tjs, unsigned int itj1, unsigned int itj2, bool doPrt)
Definition: Utils.cxx:3896
std::array< double, 3 > Vector3_t
Definition: DataStructs.h:36
unsigned short TPNearVertex(TjStuff &tjs, const TrajPoint &tp)
Definition: TCVertex.cxx:1637
void PosInPlane(const TjStuff &tjs, const Vtx3Store &vx3, unsigned short plane, Point2_t &pos)
Definition: TCVertex.cxx:3083
std::vector< T > SetDifference(const std::vector< T > &set1, const std::vector< T > &set2)
Definition: Utils.h:236
std::vector< evd::details::RawDigitInfo_t >::const_iterator end(RawDigitCacheDataClass const &cache)
void PrintPFP(std::string someText, const TjStuff &tjs, const PFPStruct &pfp, bool printHeader)
Definition: Utils.cxx:4619
void SetPDGCode(TjStuff &tjs, unsigned short itj)
Definition: Utils.cxx:3697
void MoveTPToWire(TrajPoint &tp, float wire)
Definition: Utils.cxx:2351
bool StoreVertex(TjStuff &tjs, VtxStore &vx)
Definition: TCVertex.cxx:1911
unsigned short Vx3Topo(TjStuff &tjs, Vtx3Store &vx3)
Definition: TCVertex.cxx:2450
std::vector< float > KinkCuts
kink angle, nPts fit, (alternate) kink angle significance
Definition: DataStructs.h:516
TPCID_t TPC
Index of the TPC within its cryostat.
Definition: geo_types.h:203
bool TrajTrajDOCA(const TjStuff &tjs, const Trajectory &tj1, const Trajectory &tj2, unsigned short &ipt1, unsigned short &ipt2, float &minSep)
Definition: Utils.cxx:2046
float VertexVertexPull(TjStuff &tjs, const Vtx3Store &vx1, const Vtx3Store &vx2)
Definition: TCVertex.cxx:1882
void PrintTrajPoint(std::string someText, const TjStuff &tjs, unsigned short ipt, short dir, unsigned short pass, TrajPoint const &tp)
Definition: Utils.cxx:4574
std::vector< float > Vertex3DCuts
2D vtx -> 3D vtx matching cuts
Definition: DataStructs.h:510
void PrintHeader(std::string someText)
Definition: Utils.cxx:4568
float TjChgFrac
Fraction of charge near the vertex that is from hits on the vertex Tjs.
Definition: DataStructs.h:96
bool StorePFP(TjStuff &tjs, PFPStruct &pfp)
Definition: PFPUtils.cxx:2660
void TrajPointTrajDOCA(TjStuff &tjs, TrajPoint const &tp, Trajectory const &tj, unsigned short &closePt, float &minSep)
Definition: Utils.cxx:2026
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
short StepDir
the normal user-defined stepping direction = 1 (US -> DS) or -1 (DS -> US)
Definition: DataStructs.h:531
double HitPosErr2
Definition: DataStructs.h:149
bool HasWire(geo::WireID const &wireid) const
Returns whether we have the specified wire.
const detinfo::DetectorProperties * detprop
Definition: DataStructs.h:527