LArSoft  v07_13_02
Liquid Argon Software toolkit - http://larsoft.org/
PFPUtils.cxx
Go to the documentation of this file.
2 // temp include for study
4 
5 namespace tca {
6 
7  struct SortEntry{
8  unsigned int index;
9  float val;
10  };
11  // TODO: Fix the sorting mess
12  bool valDecreasings (SortEntry c1, SortEntry c2) { return (c1.val > c2.val);}
13  bool valIncreasings (SortEntry c1, SortEntry c2) { return (c1.val < c2.val);}
14 
16  void StitchPFPs()
17  {
18  // Stitch PFParticles in different TPCs. This does serious damage to PFPStruct and should
19  // only be called from TrajCluster module just before making PFParticles to put in the event
20  if(slices.size() < 2) return;
21  if(tcc.geom->NTPC() == 1) return;
22  if(tcc.pfpStitchCuts.size() < 2) return;
23  if(tcc.pfpStitchCuts[0] <= 0) return;
24 
25  bool prt = tcc.dbgStitch;
26 
27  if(prt) {
28  mf::LogVerbatim myprt("TC");
29  std::string fcnLabel = "SP";
30  myprt<<fcnLabel<<" cuts "<<sqrt(tcc.pfpStitchCuts[0])<<" "<<tcc.pfpStitchCuts[1]<<"\n";
31  bool printHeader = true;
32  for(size_t isl = 0; isl < slices.size(); ++isl) {
33  if(debug.Slice >= 0 && int(isl) != debug.Slice) continue;
34  auto& slc = slices[isl];
35  if(slc.pfps.empty()) continue;
36  for(auto& pfp : slc.pfps) PrintP(fcnLabel, myprt, pfp, printHeader);
37  } // slc
38  } // prt
39 
40  // lists of pfp UIDs to stitch
41  std::vector<std::vector<int>> stLists;
42  for(unsigned short sl1 = 0; sl1 < slices.size() - 1; ++sl1) {
43  auto& slc1 = slices[sl1];
44  for(unsigned short sl2 = sl1 + 1; sl2 < slices.size(); ++sl2) {
45  auto& slc2 = slices[sl2];
46  // look for PFParticles in the same recob::Slice
47  if(slc1.ID != slc2.ID) continue;
48  for(auto& p1 : slc1.pfps) {
49  if(p1.ID <= 0) continue;
50  // Can't stitch shower PFPs
51  if(p1.PDGCode == 1111) continue;
52  for(auto& p2 : slc2.pfps) {
53  if(p2.ID <= 0) continue;
54  // Can't stitch shower PFPs
55  if(p2.PDGCode == 1111) continue;
56  float maxSep2 = tcc.pfpStitchCuts[0];
57  float maxCth = tcc.pfpStitchCuts[1];
58  bool gotit = false;
59  for(unsigned short e1 = 0; e1 < 2; ++e1) {
60  auto& pos1 = p1.XYZ[e1];
61  // require the end to be close to a TPC boundary
62  if(InsideFV(slc1, p1, e1)) continue;
63  auto& dir1 = p1.Dir[e1];
64  for(unsigned short e2 = 0; e2 < 2; ++e2) {
65  auto& pos2 = p2.XYZ[e2];
66  // require the end to be close to a TPC boundary
67  if(InsideFV(slc2, p2, e2)) continue;
68  auto& dir2 = p2.Dir[e2];
69  float sep = PosSep2(pos1, pos2);
70  if(sep > maxSep2) continue;
71  float cth = std::abs(DotProd(dir1, dir2));
72  if(cth < maxCth) continue;
73  maxSep2 = sep;
74  maxCth = cth;
75  gotit = true;
76  } // e2
77  } // e1
78  if(!gotit) continue;
79  if(prt) {
80  mf::LogVerbatim myprt("TC");
81  myprt<<"Stitch slice "<<slc1.ID<<" P"<<p1.UID<<" TPC "<<p1.TPCID.TPC;
82  myprt<<" and P"<<p2.UID<<" TPC "<<p2.TPCID.TPC;
83  myprt<<" sep "<<sqrt(maxSep2)<<" maxCth "<<maxCth;
84  }
85  // see if either of these are in a list
86  bool added = false;
87  for(auto& pm : stLists) {
88  bool p1InList = (std::find(pm.begin(), pm.end(), p1.UID) != pm.end());
89  bool p2InList = (std::find(pm.begin(), pm.end(), p2.UID) != pm.end());
90  if(p1InList || p2InList) {
91  if(p1InList) pm.push_back(p2.UID);
92  if(p2InList) pm.push_back(p1.UID);
93  added = true;
94  }
95  } // pm
96  if(added) continue;
97  // start a new list
98  std::vector<int> tmp(2);
99  tmp[0] = p1.UID;
100  tmp[1] = p2.UID;
101  stLists.push_back(tmp);
102  break;
103  } // p2
104  } // p1
105  } // sl2
106  } // sl1
107  if(stLists.empty()) return;
108 
109  for(auto& stl : stLists) {
110  // Find the endpoints of the stitched pfp
111  float minZ = 1E6;
112  std::pair<unsigned short, unsigned short> minZIndx;
113  unsigned short minZEnd = 2;
114  for(auto puid : stl) {
115  auto slcIndex = GetSliceIndex("P", puid);
116  if(slcIndex.first == USHRT_MAX) continue;
117  auto& pfp = slices[slcIndex.first].pfps[slcIndex.second];
118  for(unsigned short end = 0; end < 2; ++end) {
119  if(pfp.XYZ[end][2] < minZ) { minZ = pfp.XYZ[end][2]; minZIndx = slcIndex; minZEnd = end; }
120  } // end
121  } // puid
122  if(minZEnd > 1) continue;
123  // preserve the pfp with the min Z position
124  auto& pfp = slices[minZIndx.first].pfps[minZIndx.second];
125  if(prt) mf::LogVerbatim("TC")<<"SP: P"<<pfp.UID;
126  // reverse it if necessary
127  if(minZEnd != 0) ReversePFP(slices[minZIndx.first], pfp);
128  // add the Tjs in the other slices to it
129  for(auto puid : stl) {
130  if(puid == pfp.UID) continue;
131  auto sIndx = GetSliceIndex("P", puid);
132  if(sIndx.first == USHRT_MAX) continue;
133  auto& opfp = slices[sIndx.first].pfps[sIndx.second];
134  if(prt) mf::LogVerbatim("TC")<<" +P"<<opfp.UID;
135  pfp.TjUIDs.insert(pfp.TjUIDs.end(), opfp.TjUIDs.begin(), opfp.TjUIDs.end());
136  if(prt) mf::LogVerbatim();
137  // Check for parents and daughters
138  if(opfp.ParentUID > 0) {
139  auto pSlcIndx = GetSliceIndex("P", opfp.ParentUID);
140  if(pSlcIndx.first < slices.size()) {
141  auto& parpfp = slices[pSlcIndx.first].pfps[pSlcIndx.second];
142  std::replace(parpfp.DtrUIDs.begin(), parpfp.DtrUIDs.begin(), opfp.UID, pfp.UID);
143  } // valid pSlcIndx
144  } // has a parent
145  for(auto dtruid : opfp.DtrUIDs) {
146  auto dSlcIndx = GetSliceIndex("P", dtruid);
147  if(dSlcIndx.first < slices.size()) {
148  auto& dtrpfp = slices[dSlcIndx.first].pfps[dSlcIndx.second];
149  dtrpfp.ParentUID = pfp.UID;
150  } // valid dSlcIndx
151  } // dtruid
152  // declare it obsolete
153  opfp.ID = 0;
154  } // puid
155  } // stl
156 
157  } // StitchPFPs
158 
160  void UpdateMatchStructs(TCSlice& slc, int oldTj, int newTj)
161  {
162  // Replaces tjid and ipt references in slc.matchVec and slc.pfps from
163  // oldTj to newTj. This function is called when Tjs are split or merged
164  // or if a Tj is reversed (in which case oldTj = newTj).
165  // The method used is to match the trajectory point positions
166  if(oldTj <= 0 || oldTj > (int)slc.tjs.size()) return;
167  if(newTj <= 0 || newTj > (int)slc.tjs.size()) return;
168  if(slc.mallTraj.empty() && slc.pfps.empty()) return;
169 
170  // convert from int to unsigned short
171  int oldtjid = oldTj;
172  int newtjid = newTj;
173  auto& ntj = slc.tjs[newTj - 1];
174  unsigned short npts = ntj.EndPt[1] - ntj.EndPt[0] + 1;
175  // put the X positions of the new Tj into a vector for matching
176  std::vector<float> xpos(ntj.Pts.size());
177  geo::PlaneID planeID = DecodeCTP(ntj.CTP);
178  for(unsigned short npt = ntj.EndPt[0]; npt <= ntj.EndPt[1]; ++npt) {
179  auto& ntp = ntj.Pts[npt];
180  if(ntp.Chg <= 0) continue;
181  xpos[npt] = tcc.detprop->ConvertTicksToX(ntp.Pos[1]/tcc.unitsPerTick, planeID);
182  } // npt
183 
184  if(!slc.mallTraj.empty()) {
185  for(unsigned int ipt = 0; ipt < slc.mallTraj.size(); ++ipt) {
186  auto& tj2pt = slc.mallTraj[ipt];
187  if(tj2pt.id > slc.tjs.size()) continue;
188  if(tj2pt.id != oldtjid) continue;
189  // Found the old Tj. Now find the point
190  for(unsigned short npt = ntj.EndPt[0]; npt <= ntj.EndPt[1]; ++npt) {
191  auto& ntp = ntj.Pts[npt];
192  if(ntp.Chg <= 0) continue;
193  if(std::nearbyint(ntp.Pos[0]) == tj2pt.wire && xpos[npt] > tj2pt.xlo && xpos[npt] < tj2pt.xhi) {
194  tj2pt.id = newtjid;
195  tj2pt.ipt = npt;
196  tj2pt.npts = npts;
197  break;
198  } // points match
199  } // npt
200  } // ipt
201  } // !slc.mallTraj.empty()
202 
203  // Update pfp space points
204  if(!slc.pfps.empty()) {
205  for(auto& pfp : slc.pfps) {
206  for(auto& tp3 : pfp.Tp3s) {
207  // check each of the Tj2Pts associated with this space point
208  for(auto& tj2pt : tp3.Tj2Pts) {
209  if(tj2pt.id > slc.tjs.size()) continue;
210  if(tj2pt.id != oldtjid) continue;
211  // look for the corresponding point (wire) on the new Tj
212  for(unsigned short npt = ntj.EndPt[0]; npt <= ntj.EndPt[1]; ++npt) {
213  auto& ntp = ntj.Pts[npt];
214  if(std::nearbyint(ntp.Pos[0]) == tj2pt.wire && xpos[npt] > tj2pt.xlo && xpos[npt] < tj2pt.xhi) {
215  tj2pt.id = newtjid;
216  tj2pt.ipt = npt;
217  tj2pt.npts = npts;
218  break;
219  }
220  } // npt
221  } // tj2pt
222  } // tp3
223  } // pfp
224  } // pfps exists
225 
226  } // UpdateMatchStructs
227 
229  void UpdateTp3s(TCSlice& slc, PFPStruct& pfp, int oldTj, int newTj)
230  {
231  // Replaces occurrences of oldTj with newTj in the pfp vector of Tp3s
232  if(oldTj <= 0 || oldTj > (int)slc.tjs.size()) return;
233  if(newTj <= 0 || newTj > (int)slc.tjs.size()) return;
234  if(slc.mallTraj.empty() && pfp.Tp3s.empty()) return;
235 
236  // convert from int to unsigned short
237  unsigned short oldtjid = oldTj;
238  unsigned short newtjid = newTj;
239  auto& ntj = slc.tjs[newTj - 1];
240  unsigned short npts = ntj.EndPt[1] - ntj.EndPt[0] + 1;
241  // put the X positions of the new Tj into a vector for matching
242  std::vector<float> xpos(ntj.Pts.size());
243  geo::PlaneID planeID = DecodeCTP(ntj.CTP);
244  for(unsigned short npt = ntj.EndPt[0]; npt <= ntj.EndPt[1]; ++npt) {
245  auto& ntp = ntj.Pts[npt];
246  if(ntp.Chg <= 0) continue;
247  xpos[npt] = tcc.detprop->ConvertTicksToX(ntp.Pos[1]/tcc.unitsPerTick, planeID);
248  } // npt
249 
250  for(auto& tp3 : pfp.Tp3s) {
251  // check each of the Tj2Pts associated with this space point
252  for(auto& tj2pt : tp3.Tj2Pts) {
253  if(tj2pt.id > slc.tjs.size()) continue;
254  if(tj2pt.id != oldtjid) continue;
255  // look for the corresponding point (wire) on the new Tj
256  for(unsigned short npt = ntj.EndPt[0]; npt <= ntj.EndPt[1]; ++npt) {
257  auto& ntp = ntj.Pts[npt];
258  if(std::nearbyint(ntp.Pos[0]) == tj2pt.wire && xpos[npt] > tj2pt.xlo && xpos[npt] < tj2pt.xhi) {
259  tj2pt.id = newtjid;
260  tj2pt.ipt = npt;
261  tj2pt.npts = npts;
262  break;
263  }
264  } // npt
265  } // tj2pt
266  } // tp3
267 
268  } // UpdateTp3s
269 
271  void FillmAllTraj(TCSlice& slc)
272  {
273  // Fills the mallTraj vector with trajectory points in the tpc and sorts
274  // them by increasing X
275  slc.matchVec.clear();
276 
277  int cstat = slc.TPCID.Cryostat;
278  int tpc = slc.TPCID.TPC;
279 
280  // count the number of TPs and clear out any old 3D match flags
281  unsigned int ntp = 0;
282  for(auto& tj : slc.tjs) {
283  if(tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) continue;
284  if(tj.ID <= 0) continue;
285  geo::PlaneID planeID = DecodeCTP(tj.CTP);
286  if((int)planeID.Cryostat != cstat) continue;
287  if((int)planeID.TPC != tpc) continue;
288  ntp += NumPtsWithCharge(slc, tj, false);
289  tj.AlgMod[kMat3D] = false;
290  } // tj
291  if(ntp < 2) return;
292 
293  slc.mallTraj.resize(ntp);
294 
295  // define mallTraj
296  unsigned int icnt = 0;
297  for(auto& tj : slc.tjs) {
298  if(tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) continue;
299  geo::PlaneID planeID = DecodeCTP(tj.CTP);
300  if((int)planeID.Cryostat != cstat) continue;
301  if((int)planeID.TPC != tpc) continue;
302  int plane = planeID.Plane;
303  int tjID = tj.ID;
304  if(tjID <= 0) continue;
305  short score = 1;
306  if(tj.AlgMod[kTjHiVx3Score]) score = 0;
307  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
308  auto& tp = tj.Pts[ipt];
309  if(tp.Chg == 0) continue;
310  if(icnt > slc.mallTraj.size() - 1) break;
311  if(tp.Pos[0] < -0.4) continue;
312  slc.mallTraj[icnt].wire = std::nearbyint(tp.Pos[0]);
313  bool hasWire = tcc.geom->HasWire(geo::WireID(cstat, tpc, plane, slc.mallTraj[icnt].wire));
314  // don't try matching if the wire doesn't exist
315  if(!hasWire) continue;
316  float xpos = tcc.detprop->ConvertTicksToX(tp.Pos[1]/tcc.unitsPerTick, plane, tpc, cstat);
317  float posPlusRMS = tp.Pos[1] + TPHitsRMSTime(slc, tp, kUsedHits);
318  float rms = tcc.detprop->ConvertTicksToX(posPlusRMS/tcc.unitsPerTick, plane, tpc, cstat) - xpos;
319  if(rms < tcc.match3DCuts[0]) rms = tcc.match3DCuts[0];
320  if(icnt == slc.mallTraj.size()) break;
321  slc.mallTraj[icnt].xlo = xpos - rms;
322  slc.mallTraj[icnt].xhi = xpos + rms;
323  slc.mallTraj[icnt].dir = tp.Dir;
324  slc.mallTraj[icnt].ctp = tp.CTP;
325  slc.mallTraj[icnt].id = tjID;
326  slc.mallTraj[icnt].ipt = ipt;
327  slc.mallTraj[icnt].npts = tj.EndPt[1] - tj.EndPt[0] + 1;
328  slc.mallTraj[icnt].score = score;
329  ++icnt;
330  } // tp
331  } // tj
332 
333  if(icnt < slc.mallTraj.size()) slc.mallTraj.resize(icnt);
334 
335  // This is pretty self-explanatory
336  std::vector<SortEntry> sortVec(slc.mallTraj.size());
337  for(unsigned int ipt = 0; ipt < slc.mallTraj.size(); ++ipt) {
338  // populate the sort vector
339  sortVec[ipt].index = ipt;
340  sortVec[ipt].val = slc.mallTraj[ipt].xlo;
341  } // ipt
342  // sort by increasing xlo
343  std::sort(sortVec.begin(), sortVec.end(), valIncreasings);
344  // put slc.mallTraj into sorted order
345  auto tallTraj = slc.mallTraj;
346  for(unsigned int ii = 0; ii < sortVec.size(); ++ii) slc.mallTraj[ii] = tallTraj[sortVec[ii].index];
347 
348  } // FillmAllTraj
349 
351  bool SetStart(TCSlice& slc, PFPStruct& pfp, bool prt)
352  {
353  // Analyzes the space point collection and the Tjs in the pfp to find a new start
354  // position. The Tp3s found in FindCompleteness are ordered by increasing X. The general direction
355  // pfp.Dir[0] and the average position of all points in Tp3s was stored in pfp.XYZ[0]. This function
356  // rotates each tp3 into this coordinate system to determine (along, trans) for each point. The min (max)
357  // value of along defines the start (end) of the trajectory.
358 
359  if(pfp.ID <= 0 || pfp.TjIDs.empty()) return false;
360  if(pfp.Tp3s.size() < 2) return false;
361 
362  // The projection along the general direction relative to the average position was found
363  // in FillCompleteness. Now
364  float minAlong = 1E6;
365  unsigned short minPt = 0;
366  float maxAlong = -1E6;
367  unsigned short maxPt = 0;
368  std::vector<SortEntry> sortVec(pfp.Tp3s.size());
369  for(unsigned short ipt = 0; ipt < pfp.Tp3s.size(); ++ipt) {
370  auto& tp3 = pfp.Tp3s[ipt];
371  sortVec[ipt].index = ipt;
372  sortVec[ipt].val = tp3.AlongTrans[0];
373  // find the min (max)
374  if(tp3.AlongTrans[0] < minAlong) {
375  minAlong = tp3.AlongTrans[0];
376  minPt = ipt;
377  }
378  if(tp3.AlongTrans[0] > maxAlong) {
379  maxAlong = tp3.AlongTrans[0];
380  maxPt = ipt;
381  }
382  } // tp3
383 
384  pfp.XYZ[0] = pfp.Tp3s[minPt].Pos;
385  pfp.XYZ[1] = pfp.Tp3s[maxPt].Pos;
386 
387  if(prt) {
388  mf::LogVerbatim("TC")<<"SNS: P"<<pfp.ID<<" minPt "<<minPt<<" maxPt "<<maxPt<<" dir "<<std::fixed<<std::setprecision(2)<<pfp.Dir[0][0]<<" "<<pfp.Dir[0][1]<<" "<<pfp.Dir[0][2];
389  PrintTp3("minPt", slc, pfp.Tp3s[minPt]);
390  PrintTp3("maxPt", slc, pfp.Tp3s[maxPt]);
391  }
392 
393  std::sort(sortVec.begin(), sortVec.end(), valIncreasings);
394  // put them into order
395  std::vector<TrajPoint3> temp;
396  for(unsigned short ii = 0; ii < sortVec.size(); ++ii) temp.push_back(pfp.Tp3s[sortVec[ii].index]);
397  pfp.Tp3s = temp;
398 // PrintTp3s("SNS", slc, pfp, -1);
399 
400  return true;
401 
402  } // SetStart
403 
405  void FollowTp3s(TCSlice& slc, PFPStruct& pfp, bool prt)
406  {
407  // Step through the set of Tp3s on this pfp to create a trajectory. The start and end points
408  // are assumed to be Tp3s[0] and Tp3s[Tp3s.size()-1] respectively.
409 
410  if(pfp.Tp3s.size() < 2) return;
411 
412  unsigned short startPt = 0;
413  unsigned short endPt = pfp.Tp3s.size() - 1;
414  // divide the trajectory in 5 cm long sections. The average position of the Tp3s in
415  // each section will be found. Tp3s
416  constexpr float sectionLen = 5;
417  float endAlong = pfp.Tp3s[0].AlongTrans[0] + sectionLen;
418  std::vector<Vector3_t> sectionPos;
419  sectionPos.push_back(pfp.Tp3s[0].Pos);
420  std::vector<unsigned short> sectionPt;
421  sectionPt.push_back(0);
422  for(unsigned short section = 0; section < 100; ++section) {
423  // a point to find the average position in this section
424  Point3_t avePos {{0,0,0}};
425  Vector3_t aveDir {{0,0,0}};
426  unsigned short cnt = 0;
427  for(unsigned short ipt = startPt; ipt < endPt; ++ipt) {
428  auto& tp3 = pfp.Tp3s[ipt];
429  // The path length along the direction vector from the start point to the end
430  // point was stashed in dEdxErr
431  // remove outliers
432  if(tp3.AlongTrans[1] > 2) continue;
433  if(tp3.AlongTrans[0] < endAlong) {
434  // still in the same section - sum and continue
435  for(unsigned short xyz = 0; xyz < 3; ++xyz) {
436  avePos[xyz] += tp3.Pos[xyz];
437  aveDir[xyz] += tp3.Dir[xyz];
438  }
439  ++cnt;
440  continue;
441  }
442  // entered the next section. Check for a failure
443  if(cnt == 0) continue;
444  // calculate the average position
445  for(unsigned short xyz = 0; xyz < 3; ++xyz) avePos[xyz] /= cnt;
446  SetMag(aveDir, 1);
447 /*
448  std::cout<<"Section "<<section<<" cnt "<<cnt<<" avePos"<<std::fixed<<std::setprecision(1);
449  std::cout<<" "<<avePos[0]<<" "<<avePos[1]<<" "<<avePos[2];
450  std::cout<<" aveDir "<<std::setprecision(2)<<aveDir[0]<<" "<<aveDir[1]<<" "<<aveDir[2]<<"\n";
451 */
452  sectionPos.push_back(avePos);
453  sectionPt.push_back(ipt);
454  startPt = ipt;
455  endAlong += sectionLen;
456  break;
457  } // ipt
458  } // section
459  sectionPos.push_back(pfp.Tp3s[endPt].Pos);
460  sectionPt.push_back(pfp.Tp3s.size() - 1);
461 /*
462  for(unsigned short ipt = 0; ipt < sectionPos.size(); ++ipt) {
463  std::cout<<ipt<<" sectionPt "<<sectionPt[ipt]<<" sectionPos "<<" "<<sectionPos[ipt][0]<<" "<<sectionPos[ipt][1]<<" "<<sectionPos[ipt][2]<<"\n";
464  } // ipt
465 */
466  // set the general purpose flag bit false (unused) for all Tj Pts. This will be set true
467  // when a Tp is used in a Tp3
468  for(auto tjid : pfp.TjIDs) {
469  auto& tj = slc.tjs[tjid - 1];
470  for(auto& tp : tj.Pts) tp.Environment[kEnvFlag] = false;
471  } // tjid
472  // set the bits true for the first point
473  for(auto tj2pt : pfp.Tp3s[0].Tj2Pts) {
474  slc.tjs[tj2pt.id - 1].Pts[tj2pt.ipt].Environment[kEnvFlag] = true;
475  }
476  // create a vector of new Tp3s that will replace pfp.Tp3s
477  std::vector<TrajPoint3> ntp3;
478  ntp3.push_back(pfp.Tp3s[0]);
479  // 2D position (WSE units) of the TPs at the start of this section. We will require that all 2D TPs are
480  // less than sectionLen (in WSE units) from this point.
481  std::vector<Point2_t> startPos2D(slc.nPlanes);
482  // collect Tp3s in each section
483  unsigned short lastPtAdded = 0;
484  for(unsigned short section = 1; section < sectionPt.size(); ++section) {
485  Point3_t startPos = sectionPos[section - 1];
486  Point3_t endPos = sectionPos[section];
487  auto startDir = PointDirection(startPos, endPos);
488  // define the pfp start direction
489  if(section == 1) pfp.Dir[0] = startDir;
490  // and the end direction
491  pfp.Dir[1] = startDir;
492 // std::cout<<"Section "<<section<<" startDir "<<std::fixed<<std::setprecision(2)<<startDir[0]<<" "<<startDir[1]<<" "<<startDir[2]<<"\n";
493  // define the 2D positions for this point in each plane
494  for(unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
495  geo::PlaneID planeID = geo::PlaneID(pfp.TPCID.Cryostat, pfp.TPCID.TPC, plane);
496  startPos2D[plane][0] = tcc.geom->WireCoordinate(sectionPos[section - 1][1], sectionPos[section - 1][2], planeID);
497  startPos2D[plane][1] = tcc.detprop->ConvertXToTicks(sectionPos[section - 1][0], planeID) * tcc.unitsPerTick;
498  } // plane
499  for(unsigned short ipt = sectionPt[section - 1]; ipt < sectionPt[section]; ++ipt) {
500  auto& tp3 = pfp.Tp3s[ipt];
501  // count the number of Tps in this Tp3 that are already used in the trajectory
502  unsigned short nused = 0;
503  bool big2DSep = false;
504  for(auto tj2pt : pfp.Tp3s[ipt].Tj2Pts) {
505  auto& tp = slc.tjs[tj2pt.id - 1].Pts[tj2pt.ipt];
506  if(tp.Environment[kEnvFlag]) ++nused;
507  unsigned short plane = DecodeCTP(tp.CTP).Plane;
508  float sep2D = PosSep(startPos2D[plane], tp.Pos) * tcc.wirePitch;
509  if(sep2D > sectionLen) big2DSep = true;
510  } // tj2pt
511  if(big2DSep || nused > 1) continue;
512  Point2_t alongTrans;
513  FindAlongTrans(startPos, startDir, tp3.Pos, alongTrans);
514  if(alongTrans[1] > 0.5) continue;
515 /*
516  std::cout<<section<<" ipt "<<ipt<<" trans "<<alongTrans[1]<<" tj_ipt";
517  for(auto tj2pt : tp3.Tj2Pts) std::cout<<" "<<tj2pt.id - 1<<"_"<<tj2pt.ipt;
518  std::cout<<"\n";
519 */
520  tp3.AlongTrans = alongTrans;
521  // don't clobber the original direction
522 // tp3.Dir = dir;
523  ntp3.push_back(tp3);
524  // set the flag
525  for(auto tj2pt : tp3.Tj2Pts) slc.tjs[tj2pt.id - 1].Pts[tj2pt.ipt].Environment[kEnvFlag] = true;
526  lastPtAdded = ipt;
527  } // ipt
528  } // section
529 
530  if(lastPtAdded != endPt) ntp3.push_back(pfp.Tp3s[endPt]);
531 
532  if(prt) {
533  float len = PosSep(ntp3[0].Pos, ntp3[ntp3.size()-1].Pos);
534  mf::LogVerbatim("TC")<<"FollowTp3s: Tp3s size in "<<pfp.Tp3s.size()<<" size out "<<ntp3.size()<<" len "<<std::fixed<<std::setprecision(2)<<len;
535  }
536 
537  // reverse if necessary to be consistent with a vertex
538  if(pfp.Vx3ID[0] > 0) {
539  auto& vx3 = slc.vtx3s[pfp.Vx3ID[0] - 1];
540  Point3_t vpos = {{vx3.X, vx3.Y, vx3.Z}};
541  auto& firstTp3Pos = ntp3[0].Pos;
542  auto& lastTp3Pos = ntp3[ntp3.size() - 1].Pos;
543  if(PosSep2(lastTp3Pos, vpos) < PosSep2(firstTp3Pos, vpos)) std::reverse(ntp3.begin(), ntp3.end());
544  } // pfp.Vx3ID[0] > 0
545 
546  pfp.Tp3s = ntp3;
547  // The directions were set above. Set the start and end positions. Note that the start position
548  // may have been previously determined by a vertex but that is now superseded by the actual start
549  // of the pfp
550  pfp.XYZ[0] = ntp3[0].Pos;
551  pfp.XYZ[1] = ntp3[ntp3.size()-1].Pos;
552 // if(prt) PrintTp3s("FTp3o", slc, pfp, -1);
553 
554  } // FollowTp3s
556  bool FitTp3s(TCSlice& slc, const std::vector<TrajPoint3>& tp3s, Point3_t& pos, Vector3_t& dir, float& rCorr)
557  {
558  return FitTp3s(slc, tp3s, 0, tp3s.size(), pos, dir, rCorr);
559  } // FitTp3s
560 
562  bool FitTp3s(TCSlice& slc, const std::vector<TrajPoint3>& tp3s, unsigned short fromPt, unsigned short toPt, Point3_t& pos, Vector3_t& dir, float& rCorr)
563  {
564  // Fits the Tj2Pts points in Tp3s to a line
565  if(tp3s.size() < 3) return false;
566  if(fromPt >= toPt) return false;
567  if(toPt > tp3s.size()) return false;
568 
569  // temp vectors to ensure that a TP is only used once
570  std::vector<unsigned short> useID;
571  std::vector<unsigned short> useIpt;
572  std::vector<unsigned short> cntInPln(slc.nPlanes);
573  for(unsigned short ipt = fromPt; ipt < toPt; ++ipt) {
574  auto& tp3 = tp3s[ipt];
575  for(auto& tj2pt : tp3.Tj2Pts) {
576  bool isUsed = false;
577  for(unsigned short ii = 0; ii < useID.size(); ++ii) {
578  if(tj2pt.id == useID[ii] && tj2pt.ipt == useIpt[ii]) isUsed = true;
579  } // ii
580  if(isUsed) continue;
581  // add it to the list
582  useID.push_back(tj2pt.id);
583  useIpt.push_back(tj2pt.ipt);
584  auto& tj = slc.tjs[tj2pt.id - 1];
585  ++cntInPln[DecodeCTP(tj.CTP).Plane];
586  } // tj2pt
587  } // ipt
588  // ensure there are at least two points in at least two planes
589  unsigned short enufInPlane = 0;
590  for(unsigned short plane = 0; plane < slc.nPlanes; ++plane) if(cntInPln[plane] > 1) ++enufInPlane;
591  if(enufInPlane < 2) return false;
592 
593  const unsigned int nvars = 4;
594  unsigned int npts = useID.size();
595  TMatrixD A(npts, nvars);
596  // vector holding the Wire number
597  TVectorD w(npts);
598 
599  // X origin
600  double x0 = 0;
601  for(unsigned short ipt = 0; ipt < useID.size(); ++ipt) {
602  auto& tp = slc.tjs[useID[ipt] - 1].Pts[useIpt[ipt]];
603  geo::PlaneID planeID = DecodeCTP(tp.CTP);
604  x0 += tcc.detprop->ConvertTicksToX(tp.Pos[1]/tcc.unitsPerTick, planeID);
605  }
606  x0 /= (double)useID.size();
607 
608  double wght = 1;
609  for(unsigned short ipt = 0; ipt < useID.size(); ++ipt) {
610  auto& tp = slc.tjs[useID[ipt] - 1].Pts[useIpt[ipt]];
611  geo::PlaneID planeID = DecodeCTP(tp.CTP);
612  unsigned int cstat = planeID.Cryostat;
613  unsigned int tpc = planeID.TPC;
614  unsigned int plane = planeID.Plane;
615  // get the wire plane offset
616  double off = tcc.geom->WireCoordinate(0, 0, plane, tpc, cstat);
617  // get the "cosine-like" component
618  double cw = tcc.geom->WireCoordinate(1, 0, plane, tpc, cstat) - off;
619  // the "sine-like" component
620  double sw = tcc.geom->WireCoordinate(0, 1, plane, tpc, cstat) - off;
621  double x = tcc.detprop->ConvertTicksToX(tp.Pos[1]/tcc.unitsPerTick, planeID) - x0;
622  A[ipt][0] = wght * cw;
623  A[ipt][1] = wght * sw;
624  A[ipt][2] = wght * cw * x;
625  A[ipt][3] = wght * sw * x;
626  w[ipt] = wght * (tp.Pos[0] - off);
627  } // ipt
628 
629  TDecompSVD svd(A);
630  bool ok;
631  TVectorD tVec = svd.Solve(w, ok);
632  double norm = sqrt(1 + tVec[2] * tVec[2] + tVec[3] * tVec[3]);
633  dir[0] = 1 / norm;
634  dir[1] = tVec[2] / norm;
635  dir[2] = tVec[3] / norm;
636  pos[0] = x0;
637  pos[1] = tVec[0];
638  pos[2] = tVec[1];
639  rCorr = 1;
640 // std::cout<<"FTP3s: "<<useID.size()<<" cntInPln "<<cntInPln[0]<<" "<<cntInPln[1]<<" "<<cntInPln[2]<<"\n";
641  return true;
642 
643  } // FitTp3s
644 
646  bool FitTp3(TCSlice& slc, TrajPoint3& tp3, const std::vector<Tj2Pt>& tj2pts)
647  {
648  // Fits the vector of Tj2Pts points and puts the results into tp3. This code is adapted
649  // from TrackLineFitAlg: SVD fit adapted from $ROOTSYS/tutorials/matrix/solveLinear.C
650  // Fit equation is w = A(X)v, where w is a vector of hit wires, A is
651  // a matrix to calculate a track projected to a point at X, and v is
652  // a vector (Yo, Zo, dY/dX, dZ/dX).
653  if(tj2pts.size() < 4) return false;
654 
655  const unsigned int nvars = 4;
656  unsigned int npts = tj2pts.size();
657  TMatrixD A(npts, nvars);
658  // vector holding the Wire number
659  TVectorD w(npts);
660 
661  double x0 = 0;
662  for(auto& tj2pt : tj2pts) {
663  auto& tp = slc.tjs[tj2pt.id - 1].Pts[tj2pt.ipt];
664  geo::PlaneID planeID = DecodeCTP(tp.CTP);
665  x0 += tcc.detprop->ConvertTicksToX(tp.Pos[1]/tcc.unitsPerTick, planeID);
666  }
667  x0 /= (double)tj2pts.size();
668 
669  unsigned short ninpl[3] = {0};
670  unsigned short nok = 0;
671  double wght = 1;
672  for(unsigned short ipt = 0; ipt < tj2pts.size(); ++ipt) {
673  auto& tj2pt = tj2pts[ipt];
674  auto& tp = slc.tjs[tj2pt.id - 1].Pts[tj2pt.ipt];
675  geo::PlaneID planeID = DecodeCTP(tp.CTP);
676  unsigned int cstat = planeID.Cryostat;
677  unsigned int tpc = planeID.TPC;
678  unsigned int plane = planeID.Plane;
679  // get the wire plane offset
680  double off = tcc.geom->WireCoordinate(0, 0, plane, tpc, cstat);
681  // get the "cosine-like" component
682  double cw = tcc.geom->WireCoordinate(1, 0, plane, tpc, cstat) - off;
683  // the "sine-like" component
684  double sw = tcc.geom->WireCoordinate(0, 1, plane, tpc, cstat) - off;
685  double x = tcc.detprop->ConvertTicksToX(tp.Pos[1]/tcc.unitsPerTick, planeID) - x0;
686  A[ipt][0] = wght * cw;
687  A[ipt][1] = wght * sw;
688  A[ipt][2] = wght * cw * x;
689  A[ipt][3] = wght * sw * x;
690  w[ipt] = wght * (tp.Pos[0] - off);
691  ++ninpl[plane];
692  // need at least two points in a plane
693  if(ninpl[plane] == 2) ++nok;
694  } // ipt
695 
696  // need at least 2 planes with at least two points
697  if(nok < 2) return false;
698 
699  TDecompSVD svd(A);
700  bool ok;
701  TVectorD tVec = svd.Solve(w, ok);
702 
703  // Calculate Chi/DOF here
704 // tp3.ChiDOF = 1;
705 
706  Vector3_t fitDir;
707  double norm = sqrt(1 + tVec[2] * tVec[2] + tVec[3] * tVec[3]);
708  fitDir[0] = 1 / norm;
709  fitDir[1] = tVec[2] / norm;
710  fitDir[2] = tVec[3] / norm;
711 
712  Point3_t fitPos;
713  fitPos[0] = x0;
714  fitPos[1] = tVec[0];
715  fitPos[2] = tVec[1];
716  // move it to the same Z position as tp3.Pos
717  if(tp3.Pos[2] != 0) {
718  double dz = tp3.Pos[2] - fitPos[2];
719  fitPos[0] += dz * fitDir[0] / fitDir[2];
720  fitPos[1] += dz * fitDir[1] / fitDir[2];
721  fitPos[2] += dz;
722  }
723 
724  if(PosSep2(fitPos, tp3.Pos) > 5) {
725  std::cout<<"Crazy fitPos "<<PosSep(fitPos, tp3.Pos)<<"\n";
726 // tp3.ChiDOF = 10;
727  return false;
728  }
729 
730  tp3.Pos = fitPos;
731  tp3.Dir = fitDir;
732 
733  return true;
734  } // FitTp3
735 
737  void FindCompleteness(TCSlice& slc, PFPStruct& pfp, bool doFit, bool fillTp3s, bool prt)
738  {
739  // Calculate the 3D-matching completeness of the set of Tjs in pfp.TjIDs and store in pfp.EffPur.
740  // The completeness for each Tj is put in pfp.TjCompleteness. The TP-weighted average completeness
741  // is put in pfp.EffPur. This function also fits the matching points to a 3D line and puts the
742  // position and direction in pfp.XYZ[0] and pfp.Dir[0]. The pfp.TP3s vector is optionally filled.
743 
744  if(pfp.TjIDs.size() < 2) return;
745  if(tcc.match3DCuts[0] <= 0) return;
746  // This function uses mallTraj but it isn't necessarily a failure if it doesn't exist
747  if(slc.mallTraj.size() < 6) return;
748 
749  pfp.TjCompleteness.resize(pfp.TjIDs.size());
750  std::fill(pfp.TjCompleteness.begin(), pfp.TjCompleteness.end(), 0);
751  if(fillTp3s) pfp.Tp3s.clear();
752 
753  bool twoPlanes = (slc.nPlanes == 2);
754  bool twoTjs = (pfp.TjIDs.size() == 2);
755  // decide if special handling of small angle pfps is required when filling Tp3s
756  bool smallAngle = false;
757  if(fillTp3s) {
758  smallAngle = (pfp.Dir[0][0] != 0 && std::abs(pfp.Dir[0][0]) < 0.1);
759 // if(pfp.Dir[0][0] == 0 && slc.DebugMode) std::cout<<"P"<<pfp.ID<<" Dir[0] isn't defined\n";
760  }
761  double yzcut = 1.5 * tcc.match3DCuts[0];
762 
763  // initialize the fit sums
764  Point3_t point;
765  Vector3_t dir;
766  if(doFit) Fit3D(0, point, dir, point, dir);
767 
768  // create a vector of bools for each tj for points that are matched in 3D
769  // cast the IDs into an unsigned short for faster comparing
770  std::vector<unsigned short> tjids(pfp.TjIDs.size());
771  // This vector is for matches in 3 planes
772  std::vector<std::vector<bool>> tjptMat3;
773  // This vector is for matches in 2 planes
774  std::vector<std::vector<bool>> tjptMat2;
775  // and the plane index
776  std::vector<unsigned short> tjplane;
777  // Set a maximum size for the TP3s vector
778  unsigned int maxTp3Size = 10000;
779  // Initialize the vectors
780  for(unsigned short itj = 0; itj < pfp.TjIDs.size(); ++itj) {
781  if(pfp.TjIDs[itj] <= 0) {
782  std::cout<<"FindCompleteness: Bad tjid "<<pfp.TjIDs[itj]<<"\n";
783  return;
784  }
785  tjids[itj] = pfp.TjIDs[itj];
786  auto& tj = slc.tjs[pfp.TjIDs[itj] - 1];
787  std::vector<bool> tmp(tj.Pts.size(), false);
788  tjptMat2.push_back(tmp);
789  if(!twoPlanes) tjptMat3.push_back(tmp);
790  tjplane.push_back(DecodeCTP(tj.CTP).Plane);
791  } // tjid
792 
793  for(unsigned int ipt = 0; ipt < slc.mallTraj.size() - 1; ++ipt) {
794  auto& iTjPt = slc.mallTraj[ipt];
795  unsigned short indx = 0;
796  for(indx = 0; indx < tjids.size(); ++indx) if(iTjPt.id == tjids[indx]) break;
797  // require that the Tj ID of this point be in the list
798  if(indx == tjids.size()) continue;
799  auto& itj = slc.tjs[iTjPt.id - 1];
800 // if(itj.AlgMod[kMat3D]) continue;
801  auto& itp = itj.Pts[iTjPt.ipt];
802  unsigned short iplane = DecodeCTP(itp.CTP).Plane;
803  unsigned short tpc = DecodeCTP(itp.CTP).TPC;
804  unsigned short cstat = DecodeCTP(itp.CTP).Cryostat;
805  for(unsigned int jpt = ipt + 1; jpt < slc.mallTraj.size() - 1; ++jpt) {
806  auto& jTjPt = slc.mallTraj[jpt];
807  // ensure that the planes are different
808  if(jTjPt.ctp == iTjPt.ctp) continue;
809  unsigned short jndx = 0;
810  for(jndx = 0; jndx < tjids.size(); ++jndx) if(jTjPt.id == tjids[jndx]) break;
811  // require that the Tj ID of this point be in the list
812  if(jndx == tjids.size()) continue;
813  // check for x range overlap. We know that jTjPt.xlo is > iTjPt.xlo because of the sort
814  if(jTjPt.xlo > iTjPt.xhi) continue;
815  // break out if the x range difference becomes large (5 cm)
816  if(jTjPt.xlo > iTjPt.xhi + 5) break;
817  auto& jtj = slc.tjs[jTjPt.id - 1];
818 // if(jtj.AlgMod[kMat3D]) continue;
819  auto& jtp = jtj.Pts[jTjPt.ipt];
820  TrajPoint3 ijtp3;
821  if(!MakeTp3(slc, itp, jtp, ijtp3, true)) continue;
822  ijtp3.Tj2Pts.resize(2);
823  ijtp3.Tj2Pts[0] = iTjPt;
824  ijtp3.Tj2Pts[1] = jTjPt;
825  // Set the 2-plane match bits
826  tjptMat2[indx][iTjPt.ipt] = true;
827  tjptMat2[jndx][jTjPt.ipt] = true;
828  if(twoPlanes) {
829  if(fillTp3s && pfp.Tp3s.size() < maxTp3Size) {
830  bool saveIt = true;
831  FindAlongTrans(pfp.XYZ[0], pfp.Dir[0], ijtp3.Pos, ijtp3.AlongTrans);
832  // cut on transverse distance
833  if(smallAngle) saveIt = ijtp3.AlongTrans[1] < 1;
834  if(saveIt) pfp.Tp3s.push_back(ijtp3);
835  }
836  if(doFit) Fit3D(1, ijtp3.Pos, ijtp3.Dir, point, dir);
837  continue;
838  }
839  // count it as a triple if this point is in a dead region
840  unsigned short jplane = DecodeCTP(jtp.CTP).Plane;
841  unsigned short kplane = 3 - iplane - jplane;
842  float fwire = tcc.geom->WireCoordinate(ijtp3.Pos[1], ijtp3.Pos[2], kplane, tpc, cstat);
843  if(fwire < -0.4) continue;
844  unsigned int kwire = std::nearbyint(fwire);
845  if(kwire < slc.wireHitRange[kplane].size() && slc.wireHitRange[kplane][kwire].first == -1) {
846  // accumulate the fit sums
847  if(doFit) Fit3D(1, ijtp3.Pos, ijtp3.Dir, point, dir);
848  // fill Tp3s?
849  if(fillTp3s && pfp.Tp3s.size() < maxTp3Size) {
850  bool saveIt = true;
851  FindAlongTrans(pfp.XYZ[0], pfp.Dir[0], ijtp3.Pos, ijtp3.AlongTrans);
852  if(smallAngle) saveIt = ijtp3.AlongTrans[1] < 1;
853  if(saveIt) pfp.Tp3s.push_back(ijtp3);
854  }
855  continue;
856  } // dead wire in kplane
857  for(unsigned int kpt = jpt + 1; kpt < slc.mallTraj.size(); ++kpt) {
858  auto& kTjPt = slc.mallTraj[kpt];
859  // ensure that the planes are different
860  if(kTjPt.ctp == iTjPt.ctp || kTjPt.ctp == jTjPt.ctp) continue;
861  // Look for this tj point in tjids
862  unsigned short kndx = 0;
863  for(kndx = 0; kndx < tjids.size(); ++kndx) if(kTjPt.id == tjids[kndx]) break;
864  // require that the Tj ID of this point be in the list if we aren't filling the Tp3s
865  if(!fillTp3s && kndx == tjids.size()) continue;
866  if(kTjPt.xlo > iTjPt.xhi) continue;
867  // break out if the x range difference becomes large
868  if(kTjPt.xlo > iTjPt.xhi + 5) break;
869  auto& ktj = slc.tjs[kTjPt.id - 1];
870 // if(ktj.AlgMod[kMat3D]) continue;
871  auto& ktp = ktj.Pts[kTjPt.ipt];
872  TrajPoint3 iktp3;
873  if(!MakeTp3(slc, itp, ktp, iktp3, true)) continue;
874  if(std::abs(ijtp3.Pos[1] - iktp3.Pos[1]) > yzcut) continue;
875  if(std::abs(ijtp3.Pos[2] - iktp3.Pos[2]) > yzcut) continue;
876  // make a copy of ijtp3 -> ijktp3
877  auto ijktp3 = ijtp3;
878  // add the Tj2Pt to it
879  ijktp3.Tj2Pts.push_back(kTjPt);
880  // accumulate the fit sums
881  if(doFit) Fit3D(1, iktp3.Pos, iktp3.Dir, point, dir);
882  // fill Tp3s?
883  if(fillTp3s && pfp.Tp3s.size() < maxTp3Size) {
884  // update the charge
885  ijktp3.dEdx = (2 * ijktp3.dEdx + ktp.Chg) / 3;
886  bool saveIt = true;
887  FindAlongTrans(pfp.XYZ[0], pfp.Dir[0], ijktp3.Pos, ijktp3.AlongTrans);
888  if(smallAngle) saveIt = ijktp3.AlongTrans[1] < 1;
889  if(saveIt) pfp.Tp3s.push_back(ijktp3);
890  }
891  // Set the 3-plane match bits
892  if(kndx == tjids.size()) continue;
893  tjptMat3[indx][iTjPt.ipt] = true;
894  tjptMat3[jndx][jTjPt.ipt] = true;
895  tjptMat3[kndx][kTjPt.ipt] = true;
896  } // kpt
897  } // jpt
898  } // ipt
899  // do the fit and put the results into the pfp
900  Fit3D(2, point, dir, pfp.XYZ[0], pfp.Dir[0]);
901  if(prt && doFit) {
902  mf::LogVerbatim myprt("TC");
903  myprt<<"FC: P"<<pfp.ID<<" fit pos "<<std::fixed<<std::setprecision(1)<<pfp.XYZ[0][0]<<" "<<pfp.XYZ[0][1]<<" "<<pfp.XYZ[0][2];
904  myprt<<" fit dir "<<std::setprecision(2)<<pfp.Dir[0][0]<<" "<<pfp.Dir[0][1]<<" "<<pfp.Dir[0][2];
905  myprt<<" Note: fit pos is the average position of all Tp3s - not the start or end.";
906  }
907  // now count the number of tj points were matched
908  // total number of points with charge in all Tjs
909  float tnpwc = 0;
910  // total number that are matched in 3D in 3 planes
911  float tcnt3 = 0;
912  // total number that are matched in 3D in 2 planes
913  float tcnt2 = 0;
914  for(unsigned short itj = 0; itj < tjids.size(); ++itj) {
915  auto& tj = slc.tjs[tjids[itj] - 1];
916  // counts for each tj
917  float npwc = 0;
918  float cnt2 = 0;
919  float cnt3 = 0;
920  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
921  if(tj.Pts[ipt].Chg <= 0) continue;
922  ++npwc;
923  if(tjptMat2[itj][ipt]) ++cnt2;
924  if(!twoPlanes && tjptMat3[itj][ipt]) ++cnt3;
925  } // ipt
926  if(twoTjs) {
927  pfp.TjCompleteness[itj] = cnt2 / npwc;
928  } else {
929  pfp.TjCompleteness[itj] = cnt3 / npwc;
930  }
931  tnpwc += npwc;
932  tcnt3 += cnt3;
933  tcnt2 += cnt2;
934  if(prt) {
935  mf::LogVerbatim myprt("TC");
936  myprt<<"FC: P"<<pfp.ID<<" T"<<tj.ID<<" npwc "<<npwc<<" cnt2 "<<cnt2<<" cnt3 "<<cnt3<<" PDGCode "<<tj.PDGCode;
937  myprt<<" MCSMom "<<tj.MCSMom<<" InShower? "<<tj.SSID;
938  myprt<<" TjCompleteness "<<std::setprecision(2)<<pfp.TjCompleteness[itj];
939  } // prt
940  } // itj
941  if(twoTjs) {
942  pfp.EffPur = tcnt2 / tnpwc;
943  } else {
944  pfp.EffPur = tcnt3 / tnpwc;
945  }
946 
947  } // FindCompleteness
948 
950  void FindMissedTjsInTp3s(TCSlice& slc, PFPStruct& pfp, std::vector<int>& missTjs, std::vector<float>& missFrac)
951  {
952  // compare the Tjs in pfp.TjIDs with the Tjs in Tp3s and return a list of Tjs
953  // in Tp3s that aren't in pfp.TjIDs
954  missTjs.clear();
955  missFrac.clear();
956  if(pfp.TjIDs.empty() || pfp.Tp3s.empty()) return;
957 
958  // determine the projection of the pfp direction vector in each plane.
959  // Don't try to merge if the projection is small
960  std::vector<float> projInPlane(slc.nPlanes);
961  for(unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
962  CTP_t inCTP = EncodeCTP(pfp.TPCID.Cryostat, pfp.TPCID.TPC, plane);
963  auto tp = MakeBareTP(slc, pfp.XYZ[0], pfp.Dir[0], inCTP);
964  projInPlane[plane] = tp.Delta;
965  } // plane
966 
967  std::vector<unsigned short> pfpTjs;
968  std::vector<unsigned short> usMissTjs;
969  std::vector<std::vector<bool>> misTjPtMat;
970  for(auto tjid : pfp.TjIDs) pfpTjs.push_back((unsigned short)tjid);
971  for(auto& tp3 : pfp.Tp3s) {
972  for(auto& tj2pt : tp3.Tj2Pts) {
973  if(std::find(pfpTjs.begin(), pfpTjs.end(), tj2pt.id) != pfpTjs.end()) continue;
974  // Tj isn't in pfp.TjIDs. See if we have it in the missed list
975  unsigned short mtjIndx = 0;
976  for(mtjIndx = 0; mtjIndx < usMissTjs.size(); ++mtjIndx) if(tj2pt.id == usMissTjs[mtjIndx]) break;
977  if(mtjIndx == usMissTjs.size()) {
978  // not in the misTjs list. Ensure that it isn't matched
979  auto& mtj = slc.tjs[tj2pt.id - 1];
980  if(mtj.AlgMod[kKilled] || mtj.AlgMod[kMat3D]) continue;
981  // add it to the list
982  usMissTjs.push_back(tj2pt.id);
983  // create the point match vector
984  std::vector<bool> ptMat(mtj.Pts.size(), false);
985  ptMat[tj2pt.ipt] = true;
986  misTjPtMat.push_back(ptMat);
987  } else {
988  if(tj2pt.ipt < misTjPtMat[mtjIndx].size()) misTjPtMat[mtjIndx][tj2pt.ipt] = true;
989  }
990  } // tj2pt
991  } // tp3
992  for(unsigned short im = 0; im < usMissTjs.size(); ++im) {
993  int mtjid = usMissTjs[im];
994  // calculate the fraction of points that are in Tp3s
995  float cnt = 0;
996  float mat = 0;
997  auto& mtj = slc.tjs[mtjid - 1];
998  // ignore if there is a high-score vertex between the missed tj and those in the pfp list
999  if(SharesHighScoreVx(slc, pfp, mtj)) continue;
1000  for(unsigned short ipt = mtj.EndPt[0]; ipt <= mtj.EndPt[1]; ++ipt) {
1001  auto& mtp = mtj.Pts[ipt];
1002  if(mtp.Chg <= 0) continue;
1003  ++cnt;
1004  if(misTjPtMat[im][ipt]) ++mat;
1005  } // ipt
1006  float frac = mat / cnt;
1007  // ignore if low fraction matched
1008  if(frac < 0.1) continue;
1009  // ignore if this would only extend the tj in this plane by a small amount
1010  float lenInPlane = 0;
1011  for(auto tjid : pfp.TjIDs) {
1012  auto& tj = slc.tjs[tjid - 1];
1013  if(tj.CTP != mtj.CTP) continue;
1014  float len = PosSep(tj.Pts[tj.EndPt[0]].Pos, tj.Pts[tj.EndPt[1]].Pos);
1015  if(len > lenInPlane) lenInPlane = len;
1016  } // tjid
1017  if(cnt < 0.05 * lenInPlane) continue;
1018  // check the direction vector projection in this plane
1019  if(projInPlane[DecodeCTP(mtj.CTP).Plane] < 0.1) continue;
1020  missTjs.push_back(mtjid);
1021  missFrac.push_back(frac);
1022  } // im
1023  } // FindMissedTjsInTp3s
1024 
1026  bool SharesHighScoreVx(TCSlice& slc, const PFPStruct& pfp, const Trajectory& tj)
1027  {
1028  // returns true if tj with tjID shares a high-score 3D vertex with any
1029  // tj in pfp.TjIDs
1030  for(unsigned short end = 0; end < 2; ++end) {
1031  if(tj.VtxID[end] == 0) continue;
1032  auto& vx2 = slc.vtxs[tj.VtxID[end] - 1];
1033  if(!vx2.Stat[kHiVx3Score]) continue;
1034  std::vector<int> vtjlist = GetVtxTjIDs(slc, vx2);
1035  auto shared = SetIntersection(vtjlist, pfp.TjIDs);
1036  if(!shared.empty()) return true;
1037  } // end
1038  return false;
1039  } // SharesHighScoreVx
1040 
1042  void Fit3D(unsigned short mode, Point3_t point, Vector3_t dir, Point3_t& fitPos, Vector3_t& fitDir)
1043  {
1044  // initialize, accumulate and fit the points. The code to fit the direction using the positions
1045  // of the points is commented out and replaced with a simple average of the directions of the points
1046 
1047  // 3D fit sum variables
1048  static double fSum, fSumx, fSumy, fSumz;
1049 // static double fSumx2, fSumy2, fSumz2, fSumxz, fSumyz;
1050  // average the direction vectors
1051  static double fSumDir0, fSumDir1, fSumDir2;
1052 
1053  if(mode == 0) {
1054  fSum = 0; fSumx = 0; fSumy = 0; fSumz = 0;
1055 // fSumx2 = 0; fSumy2 = 0; fSumz2 = 0; fSumxz = 0; fSumyz = 0;
1056  fSumDir0 = 0; fSumDir1 = 0; fSumDir2 = 0;
1057  return;
1058  }
1059  // accumulate
1060  if(mode == 1) {
1061  fSum += 1;
1062  fSumx += point[0];
1063  fSumy += point[1];
1064  fSumz += point[2];
1065 /*
1066  fSumx2 += point[0] * point[0];
1067  fSumy2 += point[1] * point[1];
1068  fSumz2 += point[2] * point[2];
1069  fSumxz += point[0] * point[2];
1070  fSumyz += point[1] * point[2];
1071 */
1072  fSumDir0 += dir[0];
1073  fSumDir1 += dir[1];
1074  fSumDir2 += dir[2];
1075  return;
1076  }
1077 
1078  if(fSum < 2) return;
1079  // just use the average for the position
1080  fitPos[0] = fSumx / fSum;
1081  fitPos[1] = fSumy / fSum;
1082  fitPos[2] = fSumz / fSum;
1083  // and for the direction
1084  fitDir = {{fSumDir0, fSumDir1, fSumDir2}};
1085  SetMag(fitDir, 1);
1086  } // Fit3D
1087 
1089  unsigned short WiresSkippedInCTP(TCSlice& slc, std::vector<int>& tjids, CTP_t inCTP)
1090  {
1091  // counts the number of wires between the end points of all Tjs in the list of tjids
1092  // in inCTP where there is no TP with charge
1093  if(tjids.empty()) return 0;
1094 
1095  // find the min and max Pos[0] positions
1096  float fLoWire = 1E6;
1097  float fHiWire = -1E6;
1098  for(auto tjid : tjids) {
1099  auto& tj = slc.tjs[tjid - 1];
1100  if(tj.CTP != inCTP) continue;
1101  for(unsigned short end = 0; end < 2; ++end) {
1102  float endWire = tj.Pts[tj.EndPt[end]].Pos[0];
1103  if(endWire < -0.4) continue;
1104  if(endWire < fLoWire) fLoWire = endWire;
1105  if(endWire > fHiWire) fHiWire = endWire;
1106  } // end
1107  } // tjid
1108  if(fLoWire >= fHiWire) return 0;
1109  unsigned int loWire = std::nearbyint(fLoWire);
1110  unsigned short nWires = std::nearbyint(fHiWire) - loWire + 1;
1111  std::vector<bool> ptOnWire(nWires, false);
1112 
1113  // count the number of points with charge on all Tjs
1114  float npwc = 0;
1115  for(auto tjid : tjids) {
1116  auto& tj = slc.tjs[tjid - 1];
1117  if(tj.CTP != inCTP) continue;
1118  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
1119  auto& tp = tj.Pts[ipt];
1120  if(tp.Chg <= 0) continue;
1121  if(tp.Pos[0] < -0.4) continue;
1122  ++npwc;
1123  unsigned short indx = std::nearbyint(tp.Pos[0]) - loWire;
1124  if(indx < nWires) ptOnWire[indx] = true;
1125  } // ipt
1126  } // tjid
1127  if(npwc == 0) return 0;
1128  float nskip = 0;
1129  for(unsigned short indx = 0; indx < nWires; ++indx) if(!ptOnWire[indx]) ++nskip;
1130  return (nskip / npwc);
1131 
1132  } // WiresSkippedInCTP
1133 
1135  float LengthInCTP(TCSlice& slc, std::vector<int>& tjids, CTP_t inCTP)
1136  {
1137  // Calculates the maximum length between the end points of Tjs in the list of tjids in inCTP
1138  if(tjids.empty()) return 0;
1139  // put the end point positions into a vector
1140  std::vector<Point2_t> endPos;
1141  for(auto tjid : tjids) {
1142  auto& tj = slc.tjs[tjid - 1];
1143  if(tj.CTP != inCTP) continue;
1144  endPos.push_back(tj.Pts[tj.EndPt[0]].Pos);
1145  endPos.push_back(tj.Pts[tj.EndPt[1]].Pos);
1146  } // tjid
1147  if(endPos.size() < 2) return 0;
1148  float extent = 0;
1149  for(unsigned short pt1 = 0; pt1 < endPos.size() - 1; ++pt1) {
1150  for(unsigned short pt2 = pt1 + 1; pt2 < endPos.size(); ++pt2) {
1151  float sep = PosSep2(endPos[pt1], endPos[pt2]);
1152  if(sep > extent) extent = sep;
1153  } // pt2
1154  } // pt1
1155  return sqrt(extent);
1156  } // LengthInCTP
1157 
1159  bool AddMissedTj(TCSlice& slc, PFPStruct& pfp, unsigned short itj, bool looseCuts, bool prt)
1160  {
1161  // The Tj pfp.TjIDs[itj] has poor completeness. Search matchVec for
1162  // the occurrence of this tj with a large completeness AND the occurrence
1163  // of another tj in pfp.TjIDs.
1164  if(itj > pfp.TjIDs.size() - 1) return false;
1165  if(slc.matchVec.empty()) return false;
1166 
1167  int theTj = pfp.TjIDs[itj];
1168 // bool pfpInShower = (pfp.PDGCode == 11);
1169 
1170  unsigned short oldSize = pfp.TjIDs.size();
1171 
1172  for(unsigned int ims = 0; ims < slc.matchVec.size(); ++ims) {
1173  auto& ms = slc.matchVec[ims];
1174  // look for theTj in the match struct
1175  unsigned short tjIndex = 0;
1176  for(tjIndex = 0; tjIndex < ms.TjIDs.size(); ++tjIndex) if(ms.TjIDs[tjIndex] == theTj) break;
1177  if(tjIndex == ms.TjIDs.size()) continue;
1178  auto shared = SetIntersection(pfp.TjIDs, ms.TjIDs);
1179  if(shared.empty()) continue;
1180  if(looseCuts) {
1181  // Look for shared size at least 2 (theTj and another tj) or size 1 and higher TjCompleteness
1182  bool isWorse = (ms.TjCompleteness[tjIndex] < pfp.TjCompleteness[itj]);
1183  if(shared.size() < 2 && isWorse) continue;
1184  } else {
1185  // Look for shared size at least 2 (theTj and another tj in pfp.TjIDs)
1186  if(shared.size() < 2) continue;
1187  }
1188  // Add the tjs that are not in pfp.TjIDs
1189  for(auto tjid : ms.TjIDs) {
1190  if(std::find(pfp.TjIDs.begin(), pfp.TjIDs.end(), tjid) != pfp.TjIDs.end()) continue;
1191  pfp.TjIDs.push_back(tjid);
1192  // check vertex - tj consistency
1193  if(PFPVxTjOK(slc, pfp, prt)) continue;
1194  pfp.TjCompleteness.push_back(0);
1195  if(prt) mf::LogVerbatim("TC")<<"AMT: P"<<pfp.ID<<" T"<<theTj<<" Add T"<<tjid;
1196  } // mtjid
1197  } // ims
1198  if(pfp.TjIDs.size() > oldSize) return true;
1199  return false;
1200  } // AddMissedTj
1201 
1203  bool MergePFPTjs(TCSlice& slc, PFPStruct& pfp, bool prt)
1204  {
1205  // Checks the list of Tjs in pfp.TjIDs and merges those that are in
1206  // the same plane. This function uses the ordering of Tps which should
1207  // have been sorted
1208  if(pfp.TjIDs.empty()) return false;
1209  if(pfp.Tp3s.empty()) return false;
1210 
1211  geo::TPCGeo const& TPC = tcc.geom->TPC(pfp.TPCID);
1212  unsigned short nplanes = TPC.Nplanes();
1213 
1214  // see if anything needs to be done
1215  std::vector<unsigned short> cntInPln(nplanes);
1216  bool itsOK = true;
1217  for(auto tjid : pfp.TjIDs) {
1218  auto& tj = slc.tjs[tjid - 1];
1219  unsigned short plane = DecodeCTP(tj.CTP).Plane;
1220  ++cntInPln[plane];
1221  if(cntInPln[plane] > 1) itsOK = false;
1222  }
1223  if(itsOK) return true;
1224 
1225  // vector of tj IDs that will replace pfp.TjIDs
1226  std::vector<int> newTjIDs;
1227 
1228  if(prt) {
1229  mf::LogVerbatim myprt("TC");
1230  myprt<<"MergePFPTjs: P"<<pfp.ID<<" in";
1231  for(auto tjid : pfp.TjIDs) myprt<<" T"<<tjid;
1232  }
1233 
1234  for(unsigned short plane = 0; plane < nplanes; ++plane) {
1235  CTP_t inCTP = EncodeCTP(pfp.TPCID.Cryostat, pfp.TPCID.TPC, plane);
1236  // save the TjIDs as unsigned short to match with Tj2Pts
1237  std::vector<unsigned short> tjids;
1238  for(auto tjid : pfp.TjIDs) if(slc.tjs[tjid - 1].CTP == inCTP) tjids.push_back((unsigned short)tjid);
1239  // Only one tj in this plane. No need to merge
1240  if(tjids.size() == 1) {
1241  newTjIDs.push_back((int)tjids[0]);
1242  continue;
1243  }
1244  // no tjs in this plane
1245  if(tjids.size() == 0) continue;
1246  // find the first ID and ipt of Tjs in this plane as they are
1247  // encountered while iterating through Tp3s. This scheme assumes that the Tp3s have
1248  // been sorted by distance from the start and the Tjs are broken end-to-end. This
1249  // information will be used to determine if Tjs need to be reversed before inserting
1250  // the points in to the merged trajectory
1251  // Tj ID first ipt
1252  std::vector<std::array<unsigned short, 2>> firstPts;
1253  for(unsigned short itp3 = 0; itp3 < pfp.Tp3s.size(); ++itp3) {
1254  auto& tp3 = pfp.Tp3s[itp3];
1255  for(auto& tj2pt : tp3.Tj2Pts) {
1256  unsigned short tjIndx = 0;
1257  for(tjIndx = 0; tjIndx < tjids.size(); ++tjIndx) if(tj2pt.id == tjids[tjIndx]) break;
1258  if(tjIndx == tjids.size()) continue;
1259  // look for this tj in firstPts
1260  unsigned short firstPtsIndx = 0;
1261  for(firstPtsIndx = 0; firstPtsIndx < firstPts.size(); ++firstPtsIndx) if(tj2pt.id == firstPts[firstPtsIndx][0]) break;
1262  if(firstPtsIndx == firstPts.size()) {
1263  // not found so add it
1264  std::array<unsigned short, 2> firstPt {{tj2pt.id, tj2pt.ipt}};
1265  firstPts.push_back(firstPt);
1266  }
1267  } // tj2pt
1268  } // itp3
1269  if(firstPts.empty()) continue;
1270  // create a new merged trajectory
1271  Trajectory mtj;
1272  // give it a bogus ID
1273  mtj.ID = -6666;
1274  mtj.CTP = inCTP;
1275  mtj.StepDir = 1;
1276  bool first = true;
1277  for(auto firstPt : firstPts) {
1278  // make a copy so we can reverse it and drop it if the merge fails
1279  auto tj = slc.tjs[firstPt[0] - 1];
1280  unsigned short midPt = 0.5 * (tj.EndPt[0] + tj.EndPt[1]);
1281  if(firstPt[1] > midPt) ReverseTraj(slc, tj);
1282  // Transfer vertices to mtj
1283  if(first) {
1284  first = false;
1285  mtj.VtxID[0] = tj.VtxID[0];
1286  }
1287  mtj.VtxID[1] = tj.VtxID[1];
1288  // insert the points at the end
1289  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
1290  auto& tp = tj.Pts[ipt];
1291  if(tp.Chg <= 0) continue;
1292  mtj.Pts.push_back(tp);
1293  }
1294  } // firstPt
1295  mtj.AlgMod[kMat3DMerge] = true;
1296  SetEndPoints(mtj);
1297  mtj.MCSMom = MCSMom(slc, mtj);
1298  SetPDGCode(slc, mtj, true);
1299  if(prt) {
1300  mf::LogVerbatim myprt("TC");
1301  myprt<<" P"<<pfp.ID<<" try to merge";
1302  for(auto tjid : tjids) {
1303  auto& tj = slc.tjs[tjid - 1];
1304  myprt<<" T"<<tjid<<" MCSMom "<<tj.MCSMom;
1305  }
1306  myprt<<" -> T"<<slc.tjs.size() + 1;
1307  myprt<<" MCSMom "<<mtj.MCSMom;
1308  }
1309  // kill the broken tjs and update the pfp TP3s
1310  for(auto tjid : tjids) {
1311  auto& tj = slc.tjs[tjid - 1];
1312  if(tj.SSID > 0) mtj.SSID = tj.SSID;
1313  MakeTrajectoryObsolete(slc, tjid - 1);
1314  }
1315  // save the new one
1316  if(!StoreTraj(slc, mtj)) {
1317  std::cout<<"MergePFPTjs: StoreTraj failed P"<<pfp.ID<<"\n";
1318  return false;
1319  }
1320  int newTjID = slc.tjs.size();
1321  newTjIDs.push_back(newTjID);
1322  // prepare to clobber vertices
1323  std::vector<unsigned short> vxlist;
1324  for(auto tjid : tjids) {
1325  // update the stored match struct and Tp3s
1326  UpdateMatchStructs(slc, tjid, newTjID);
1327  // Update the Tp3s of this pfp
1328  UpdateTp3s(slc, pfp, tjid, newTjID);
1329  auto& tj = slc.tjs[tjid - 1];
1330  for(unsigned short end = 0; end < 2; ++end) {
1331  if(tj.VtxID[end] == 0) continue;
1332  if(std::find(vxlist.begin(), vxlist.end(), tj.VtxID[end]) != vxlist.end()) {
1333  auto& vx2 = slc.vtxs[tj.VtxID[end] - 1];
1334 // std::cout<<"P"<<pfp.ID<<" Clobber 2V"<<vx2.ID<<"\n";
1335  MakeVertexObsolete("MPTJ", slc, vx2, true);
1336  } else {
1337  vxlist.push_back(tj.VtxID[end]);
1338  }
1339  } // end
1340  } // tjid
1341  } // plane
1342 
1343  pfp.TjIDs = newTjIDs;
1344 
1345  if(prt) {
1346  mf::LogVerbatim myprt("TC");
1347  myprt<<"MergePFPTjs: P"<<pfp.ID<<" out";
1348  for(auto tjid : pfp.TjIDs) myprt<<" T"<<tjid;
1349  PrintPFP("MPTJ", slc, pfp, true);
1350  }
1351  return true;
1352  } // MergePFPTjs
1353 
1355  void FindXMatches(TCSlice& slc, unsigned short numPlanes, short maxScore, std::vector<MatchStruct>& matVec, bool prt)
1356  {
1357  // This function matches trajectory points in slc.mallTraj using the X position. These points should
1358  // have already been sorted by increasing X by the function that created mallTraj.
1359 
1360  if(slc.mallTraj.empty()) return;
1361  if(tcc.match3DCuts[0] <= 0) return;
1362  if(numPlanes < 2) return;
1363 
1364  int cstat = DecodeCTP(slc.mallTraj[0].ctp).Cryostat;
1365  int tpc = DecodeCTP(slc.mallTraj[0].ctp).TPC;
1366  constexpr float twopi = 2 * M_PI;
1367  constexpr float piOver2 = M_PI / 2;
1368 
1369  // create a temp vector to check for duplicates
1370  auto inMatVec = matVec;
1371  std::vector<MatchStruct> temp;
1372 
1373  // the minimum number of points for matching
1374  unsigned short minPts = 2;
1375  // override this with the user minimum for 2-plane matches
1376  if(numPlanes == 2) minPts = tcc.match3DCuts[2];
1377 
1378  // max number of match combos left
1379  unsigned int nAvailable = 0;
1380  if(matVec.size() < tcc.match3DCuts[4]) nAvailable = tcc.match3DCuts[4] - matVec.size();
1381  if(nAvailable == 0 || nAvailable > tcc.match3DCuts[4]) return;
1382 
1383  // these cuts presume that the resolution in X is better than it is in Y and Z
1384  float xcut = tcc.match3DCuts[0];
1385  double yzcut = 1.5 * xcut;
1386  for(unsigned int ipt = 0; ipt < slc.mallTraj.size() - 1; ++ipt) {
1387  auto& iTjPt = slc.mallTraj[ipt];
1388  // length cut
1389  if(iTjPt.npts < minPts) continue;
1390  // look for matches using Tjs that have the correct score
1391  if(iTjPt.score < 0 || iTjPt.score > maxScore) continue;
1392  auto& itp = slc.tjs[iTjPt.id - 1].Pts[iTjPt.ipt];
1393  unsigned short iplane = DecodeCTP(itp.CTP).Plane;
1394  for(unsigned int jpt = ipt + 1; jpt < slc.mallTraj.size() - 1; ++jpt) {
1395  auto& jTjPt = slc.mallTraj[jpt];
1396  // ensure that the planes are different
1397  if(jTjPt.ctp == iTjPt.ctp) continue;
1398  // length cut
1399  if(jTjPt.npts < minPts) continue;
1400  // score cut
1401  if(jTjPt.score < 0 || jTjPt.score > maxScore) continue;
1402  // check for x range overlap. We know that jTjPt.xlo is >= iTjPt.xlo because of the sort
1403  if(jTjPt.xlo > iTjPt.xhi) continue;
1404  // break out if the x range difference becomes large (5 cm)
1405  if(jTjPt.xlo > iTjPt.xhi + 5) break;
1406  auto& jtp = slc.tjs[jTjPt.id - 1].Pts[jTjPt.ipt];
1407  unsigned short jplane = DecodeCTP(jtp.CTP).Plane;
1408  TrajPoint3 tp3;
1409  if(!MakeTp3(slc, itp, jtp, tp3, false)) continue;
1410  // count weight is one for a two-plane match
1411  float cntWght = 1;
1412  if(numPlanes == 3) {
1413  // numPlanes == 3
1414  for(unsigned int kpt = jpt + 1; kpt < slc.mallTraj.size(); ++kpt) {
1415  auto& kTjPt = slc.mallTraj[kpt];
1416  // ensure that the planes are different
1417  if(kTjPt.ctp == iTjPt.ctp || kTjPt.ctp == jTjPt.ctp) continue;
1418  if(kTjPt.score < 0 || kTjPt.score > maxScore) continue;
1419  if(kTjPt.xlo > iTjPt.xhi) continue;
1420  // break out if the x range difference becomes large
1421  if(kTjPt.xlo > iTjPt.xhi + 5) break;
1422  auto& ktp = slc.tjs[kTjPt.id - 1].Pts[kTjPt.ipt];
1423  unsigned short kplane = DecodeCTP(ktp.CTP).Plane;
1424  TrajPoint3 iktp3;
1425  if(!MakeTp3(slc, itp, ktp, iktp3, false)) continue;
1426  if(std::abs(tp3.Pos[1] - iktp3.Pos[1]) > yzcut) continue;
1427  if(std::abs(tp3.Pos[2] - iktp3.Pos[2]) > yzcut) continue;
1428  float dang = 0;
1429  if(tcc.match3DCuts[1] > 0) {
1430  dang = std::abs(DeltaAngle(tp3.Dir, iktp3.Dir));
1431  while(dang > M_PI) dang -= twopi;
1432  if(dang > piOver2) dang = M_PI - dang;
1433  float mcsmom = slc.tjs[iTjPt.id - 1].MCSMom + slc.tjs[jTjPt.id - 1].MCSMom + slc.tjs[kTjPt.id - 1].MCSMom;
1434  mcsmom /= 3;
1435  if(mcsmom > 150 && dang > tcc.match3DCuts[1]) continue;
1436  }
1437  // we have a match.
1438  // Just fill temp. See if the Tj IDs are in the match list.
1439  // first check the input matVec
1440  bool gotit = false;
1441  for(auto& ms : inMatVec) {
1442  if(ms.TjIDs.size() != 3) continue;
1443  if(iTjPt.id == ms.TjIDs[iplane] && jTjPt.id == ms.TjIDs[jplane] && kTjPt.id == ms.TjIDs[kplane]) {
1444  gotit = true;
1445  break;
1446  }
1447  } // ms
1448  if(gotit) continue;
1449  // Triple match count = 2 de-weighted by delta angle
1450  cntWght = 2 - dang;
1451  if(cntWght <= 0) continue;
1452  // next check the temp vector
1453  unsigned short indx = 0;
1454  for(indx = 0; indx < temp.size(); ++indx) {
1455  auto& ms = temp[indx];
1456  if(iTjPt.id != ms.TjIDs[iplane]) continue;
1457  if(jTjPt.id != ms.TjIDs[jplane]) continue;
1458  if(kTjPt.id != ms.TjIDs[kplane]) continue;
1459  ms.Count += cntWght;
1460  break;
1461  } // indx
1462  if(indx == temp.size()) {
1463  // not found in the match vector so add it
1464  MatchStruct ms;
1465  ms.TjIDs.resize(3);
1466  // Note that we can put the Tj IDs in plane-order since there are 3 of them
1467  // This is not the case when there are 2 planes
1468  ms.TjIDs[iplane] = iTjPt.id;
1469  ms.TjIDs[jplane] = jTjPt.id;
1470  ms.TjIDs[kplane] = kTjPt.id;
1471  ms.Count = cntWght;
1472  temp.push_back(ms);
1473  // give up if there are too many
1474  if(temp.size() > nAvailable) break;
1475  } // not found in the list
1476  } // kpt
1477  // numPlanes == 3
1478  } else {
1479  // 2-plane TPC or 2-plane match in a 3-plane TPC
1480  if(slc.nPlanes == 3) {
1481  // See if there is a signal at this point.
1482  unsigned short kplane = 3 - iplane - jplane;
1483  float fkwire = tcc.geom->WireCoordinate(tp3.Pos[1], tp3.Pos[2], kplane, tpc, cstat);
1484  if(fkwire < 0 || fkwire > tcc.maxPos0[kplane]) continue;
1485  TrajPoint tpk;
1486  tpk.CTP = EncodeCTP(cstat, tpc, kplane);
1487  tpk.Pos[0] = fkwire;
1488  float xp = 0.5 * (iTjPt.xlo + iTjPt.xhi);
1489  tpk.Pos[1] = tcc.detprop->ConvertXToTicks(xp, kplane, tpc, cstat) * tcc.unitsPerTick;
1490  // Note that SignalAtTp assumes that a signal exists if the wire is dead
1491  if(!SignalAtTp(slc, tpk)) continue;
1492  }
1493  // Just fill temp. See if the Tj IDs are in the match list
1494  bool gotit = false;
1495  for(auto& ms : inMatVec) {
1496  if(std::find(ms.TjIDs.begin(), ms.TjIDs.end(), iTjPt.id) != ms.TjIDs.end() &&
1497  std::find(ms.TjIDs.begin(), ms.TjIDs.end(), jTjPt.id) != ms.TjIDs.end()) {
1498  gotit = true;
1499  break;
1500  }
1501  } // ms
1502  if(gotit) continue;
1503  unsigned short indx = 0;
1504  for(indx = 0; indx < temp.size(); ++indx) {
1505  auto& ms = temp[indx];
1506  if(std::find(ms.TjIDs.begin(), ms.TjIDs.end(), iTjPt.id) != ms.TjIDs.end() &&
1507  std::find(ms.TjIDs.begin(), ms.TjIDs.end(), jTjPt.id) != ms.TjIDs.end()) break;
1508  } // indx
1509  if(indx == temp.size()) {
1510  MatchStruct ms;
1511  ms.TjIDs.resize(2);
1512  // Here we put the Tj IDs in no particular order
1513  ms.TjIDs[0] = iTjPt.id;
1514  ms.TjIDs[1] = jTjPt.id;
1515  ms.Count = 1;
1516  temp.push_back(ms);
1517  } // not found in the list
1518  else {
1519  ++temp[indx].Count;
1520  }
1521  } // 2-plane TPC
1522  // give up if there are too many
1523  if(temp.size() > nAvailable) break;
1524  } // jpt
1525  // give up if there are too many
1526  if(temp.size() > nAvailable) break;
1527  } // ipt
1528 
1529  // temp
1530 
1531  if(temp.empty()) return;
1532 
1533  if(temp.size() == 1) {
1534  matVec.push_back(temp[0]);
1535  } else {
1536  // multiple entries - need to sort by decreasing match count
1537  std::vector<SortEntry> sortVec(temp.size());
1538  for(unsigned int indx = 0; indx < sortVec.size(); ++indx) {
1539  sortVec[indx].index = indx;
1540  sortVec[indx].val = temp[indx].Count;
1541  } // indx
1542  std::sort(sortVec.begin(), sortVec.end(), valDecreasings);
1543  // Re-order temp
1544  auto tmpVec = temp;
1545  for(unsigned int ii = 0; ii < sortVec.size(); ++ii) temp[ii] = tmpVec[sortVec[ii].index];
1546  // insert it after the triple matches
1547  matVec.insert(matVec.end(), temp.begin(), temp.end());
1548  } // temp size > 1
1549 
1550  if(prt) mf::LogVerbatim("TC")<<"FindXMatches: Found "<<temp.size()<<" matches";
1551 
1552  } // FindXMatches
1553 
1555  bool MakeTp3(TCSlice& slc, const TrajPoint& itp, const TrajPoint& jtp, TrajPoint3& tp3, bool findDirection)
1556  {
1557  // Make a 3D trajectory point using two 2D trajectory points
1558  tp3.Dir = {{999.0, 999.0, 999.0}};
1559  tp3.Pos = {{999.0, 999.0, 999.0}};
1560  geo::PlaneID iPlnID = DecodeCTP(itp.CTP);
1561  geo::PlaneID jPlnID = DecodeCTP(jtp.CTP);
1562  double upt = tcc.unitsPerTick;
1563  double ix = tcc.detprop->ConvertTicksToX(itp.Pos[1] / upt, iPlnID);
1564  double jx = tcc.detprop->ConvertTicksToX(jtp.Pos[1] / upt, jPlnID);
1565 
1566  // don't continue if the points are wildly far apart in X
1567  if(std::abs(ix - jx) > 20) return false;
1568  tp3.Pos[0] = 0.5 * (ix + jx);
1569  // determine the wire orientation and offsets using WireCoordinate
1570  // wire = yp * OrthY + zp * OrthZ - Wire0 = cs * yp + sn * zp - wire0
1571  // wire offset
1572  double iw0 = tcc.geom->WireCoordinate(0, 0, iPlnID);
1573  // cosine-like component
1574  double ics = tcc.geom->WireCoordinate(1, 0, iPlnID) - iw0;
1575  // sine-like component
1576  double isn = tcc.geom->WireCoordinate(0, 1, iPlnID) - iw0;
1577  double jw0 = tcc.geom->WireCoordinate(0, 0, jPlnID);
1578  double jcs = tcc.geom->WireCoordinate(1, 0, jPlnID) - jw0;
1579  double jsn = tcc.geom->WireCoordinate(0, 1, jPlnID) - jw0;
1580  double den = isn * jcs - ics * jsn;
1581  if(den == 0) return false;
1582  double iPos0 = itp.Pos[0];
1583  double jPos0 = jtp.Pos[0];
1584  // Find the Z position of the intersection
1585  tp3.Pos[2] = (jcs * (iPos0 - iw0) - ics * (jPos0 - jw0)) / den;
1586  // and the Y position
1587  bool useI = std::abs(ics) > std::abs(jcs);
1588  if(useI) {
1589  tp3.Pos[1] = (iPos0 - iw0 - isn * tp3.Pos[2]) / ics;
1590  } else {
1591  tp3.Pos[1] = (jPos0 - jw0 - jsn * tp3.Pos[2]) / jcs;
1592  }
1593 
1594  // Now find the direction. Protect against large angles first
1595  if(jtp.Dir[1] == 0) {
1596  // Going either in the +X direction or -X direction
1597  if(jtp.Dir[0] > 0) { tp3.Dir[0] = 1; } else { tp3.Dir[0] = -1; }
1598  tp3.Dir[1] = 0;
1599  tp3.Dir[2] = 0;
1600  return true;
1601  } // jtp.Dir[1] == 0
1602 
1603  // stuff the average TP charge into dEdx
1604  tp3.dEdx = 0.5 * (itp.Chg + jtp.Chg);
1605 
1606  if(!findDirection) return true;
1607 
1608  // make a copy of itp and shift it by many wires to avoid precision problems
1609  double itp2_0 = itp.Pos[0] + 100;
1610  double itp2_1 = itp.Pos[1];
1611  if(std::abs(itp.Dir[0]) > 0.01) itp2_1 += 100 * itp.Dir[1] / itp.Dir[0];
1612  // Create a second Point3 for the shifted point
1613  Point3_t pos2;
1614  // Find the X position corresponding to the shifted point
1615  pos2[0] = tcc.detprop->ConvertTicksToX(itp2_1 / upt, iPlnID);
1616  // Convert X to Ticks in the j plane and then to WSE units
1617  double jtp2Pos1 = tcc.detprop->ConvertXToTicks(pos2[0], jPlnID) * upt;
1618  // Find the wire position (Pos0) in the j plane that this corresponds to
1619  double jtp2Pos0 = (jtp2Pos1 - jtp.Pos[1]) * (jtp.Dir[0] / jtp.Dir[1]) + jtp.Pos[0];
1620  // Find the Y,Z position using itp2 and jtp2Pos0
1621  pos2[2] = (jcs * (itp2_0 - iw0) - ics * (jtp2Pos0 - jw0)) / den;
1622  if(useI) {
1623  pos2[1] = (itp2_0 - iw0 - isn * pos2[2]) / ics;
1624  } else {
1625  pos2[1] = (jtp2Pos0 - jw0 - jsn * pos2[2]) / jcs;
1626  }
1627  double sep = PosSep(tp3.Pos, pos2);
1628  if(sep == 0) return false;
1629  for(unsigned short ixyz = 0; ixyz < 3; ++ixyz) tp3.Dir[ixyz] = (pos2[ixyz] - tp3.Pos[ixyz]) /sep;
1630 
1631  return true;
1632 
1633  } // MakeTP3
1634 
1636  double DeltaAngle(const Vector3_t v1, const Vector3_t v2)
1637  {
1638  if(v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2]) return 0;
1639  return acos(DotProd(v1, v2));
1640  }
1641 
1644  {
1645  // Finds the direction vector between the two points from p1 to p2
1646  Vector3_t dir;
1647  for(unsigned short xyz = 0; xyz < 3; ++xyz) dir[xyz] = p2[xyz] - p1[xyz];
1648  if(dir[0] == 0 && dir[1] == 0 && dir[2] == 0) return dir;
1649  if(!SetMag(dir, 1)) { dir[0] = 0; dir[1] = 0; dir[3] = 0; }
1650  return dir;
1651  } // PointDirection
1652 
1654  double PosSep(const Point3_t& pos1, const Point3_t& pos2)
1655  {
1656  return sqrt(PosSep2(pos1, pos2));
1657  } // PosSep
1658 
1660  double PosSep2(const Point3_t& pos1, const Point3_t& pos2)
1661  {
1662  // returns the separation distance^2 between two positions in 3D
1663  double d0 = pos1[0] - pos2[0];
1664  double d1 = pos1[1] - pos2[1];
1665  double d2 = pos1[2] - pos2[2];
1666  return d0*d0 + d1*d1 + d2*d2;
1667  } // PosSep2
1668 
1670  bool SetMag(Vector3_t& v1, double mag)
1671  {
1672  double den = v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2];
1673  if(den == 0) return false;
1674  den = sqrt(den);
1675 
1676  v1[0] *= mag / den;
1677  v1[1] *= mag / den;
1678  v1[2] *= mag / den;
1679  return true;
1680  } // SetMag
1681 
1683  void FilldEdx(TCSlice& slc, PFPStruct& pfp)
1684  {
1685  // Fills the dEdX vector in the match struct. This function should be called after the
1686  // matched trajectory points are ordered so that dE/dx is calculated at the start of the PFParticle
1687  if(pfp.ID == 0) return;
1688  // error check
1689  bool notgood = false;
1690  for(unsigned short end = 0; end < 2; ++end) {
1691  if(pfp.dEdx[end].size() != slc.nPlanes) notgood = true;
1692  if(pfp.dEdxErr[end].size() != slc.nPlanes) notgood = true;
1693  }
1694  if(notgood) {
1695  // if(prt) mf::LogVerbatim("TC")<<"FilldEdx found inconsistent sizes for dEdx\n";
1696  return;
1697  }
1698 
1699  double t0 = 0;
1700 
1701  // don't attempt to find dE/dx at the end of a shower
1702  unsigned short numEnds = 2;
1703  if(pfp.PDGCode == 1111) numEnds = 1;
1704 
1705  unsigned short maxlen = 0;
1706  for(auto tjID : pfp.TjIDs) {
1707 
1708  Trajectory& tj = slc.tjs[tjID - 1];
1709  geo::PlaneID planeID = DecodeCTP(tj.CTP);
1710  double angleToVert = tcc.geom->Plane(planeID).ThetaZ() - 0.5 * ::util::pi<>();
1711  for(unsigned short end = 0; end < numEnds; ++end) {
1712  pfp.dEdx[end][planeID.Plane] = 0;
1713  tj.dEdx[end] = 0;
1714  double cosgamma = std::abs(std::sin(angleToVert) * pfp.Dir[end][1] + std::cos(angleToVert) * pfp.Dir[end][2]);
1715  if(cosgamma == 0) continue;
1716  double dx = tcc.geom->WirePitch(planeID) / cosgamma;
1717  if(dx == 0) continue;
1718  double dQ = tj.Pts[tj.EndPt[end]].AveChg;
1719  if(dQ == 0) continue;
1720  // convert to dQ/dx
1721  dQ /= dx;
1722  double time = tj.Pts[tj.EndPt[end]].Pos[1] / tcc.unitsPerTick;
1723  float dedx = tcc.caloAlg->dEdx_AREA(dQ, time, planeID.Plane, t0);
1724  if(dedx > 999) dedx = -1;
1725  pfp.dEdx[end][planeID.Plane] = dedx;
1726  tj.dEdx[end] = dedx;
1727  // ChgRMS is the fractional error
1728  pfp.dEdxErr[end][planeID.Plane] = dedx * tj.ChgRMS;
1729 
1730  } // end
1731  // Grab the best plane iusing the start f 1 < dE/dx < 50 MeV/cm
1732  if(pfp.dEdx[0][planeID.Plane] > 1 && pfp.dEdx[0][planeID.Plane] < 50) {
1733  if(tj.Pts.size() > maxlen) {
1734  maxlen = tj.Pts.size();
1735  pfp.BestPlane = planeID.Plane;
1736  }
1737  } // valid dE/dx
1738 
1739  } // tj
1740  } // FilldEdX
1741 
1743  void FilldEdx(TCSlice& slc, TrajPoint3& tp3)
1744  {
1745  // fills the dEdx variables in tp3 (MeV/cm)
1746  tp3.dEdx = 0;
1747  if(tp3.Tj2Pts.empty()) return;
1748  double t0 = 0;
1749  float sum = 0;
1750  float sum2 = 0;
1751  float cnt = 0;
1752  for(auto& tj2pt : tp3.Tj2Pts) {
1753  auto& tp = slc.tjs[tj2pt.id - 1].Pts[tj2pt.ipt];
1754  if(tp.Chg <= 0) continue;
1755  geo::PlaneID planeID = DecodeCTP(tp.CTP);
1756  double angleToVert = tcc.geom->Plane(planeID).ThetaZ() - 0.5 * ::util::pi<>();
1757  double cosgamma = std::abs(std::sin(angleToVert) * tp3.Dir[1] + std::cos(angleToVert) * tp3.Dir[2]);
1758  if(cosgamma == 0) continue;
1759  double dx = tcc.geom->WirePitch(planeID) / cosgamma;
1760  // sum the hit charge
1761  double chg = 0;
1762  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1763  if(!tp.UseHit[ii]) continue;
1764  unsigned int iht = tp.Hits[ii];
1765  chg += (*evt.allHits)[slc.slHits[iht].allHitsIndex].Integral();
1766  }
1767  double dQ = chg / dx;
1768  double time = tp.Pos[1] / tcc.unitsPerTick;
1769  float dedx = tcc.caloAlg->dEdx_AREA(dQ, time, planeID.Plane, t0);
1770  if(dedx > 200) continue;
1771  sum += dedx;
1772  sum2 += dedx * dedx;
1773  ++cnt;
1774  } // tj2pt
1775  if(cnt == 0) return;
1776  tp3.dEdx = sum / cnt;
1777 /*
1778  if(cnt > 1) {
1779  float arg = sum2 - cnt * tp3.dEdx * tp3.dEdx;
1780  if(arg > 0) {
1781  tp3.dEdxErr = sqrt(arg / (cnt - 1));
1782  // convert to error on the mean
1783  tp3.dEdxErr /= sqrt(cnt);
1784  }
1785  } // cnt > 1
1786 */
1787  } // FilldEdx
1788 
1790  float PFPDOCA(const PFPStruct& pfp1, const PFPStruct& pfp2, unsigned short& close1, unsigned short& close2)
1791  {
1792  // returns the Distance of Closest Approach between two PFParticles.
1793  close1 = USHRT_MAX;
1794  close2 = USHRT_MAX;
1795  float minSep2 = 1E8;
1796  for(unsigned short ipt1 = 0; ipt1 < pfp1.Tp3s.size(); ++ipt1) {
1797  auto& tp1 = pfp1.Tp3s[ipt1];
1798  for(unsigned short ipt2 = 0; ipt2 < pfp2.Tp3s.size(); ++ipt2) {
1799  auto& tp2 = pfp2.Tp3s[ipt2];
1800  float sep2 = PosSep2(tp1.Pos, tp2.Pos);
1801  if(sep2 > minSep2) continue;
1802  minSep2 = sep2;
1803  close1 = ipt1;
1804  close2 = ipt2;
1805  } // tp2
1806  } // tp1
1807  return sqrt(minSep2);
1808  } // PFPDOCA
1809 
1811  bool Split3DKink(TCSlice& slc, PFPStruct& pfp, double sep, bool prt)
1812  {
1813  // Finds kinks in the PFParticle, splits slc, creates 2D vertices and forces a rebuild if any are found
1814  if(pfp.Tp3s.empty()) return false;
1815  if(!tcc.useAlg[kSplit3DKink]) return false;
1816 
1817  auto kinkPts = FindKinks(slc, pfp, sep, prt);
1818  if(kinkPts.empty()) return false;
1819  if(prt) mf::LogVerbatim("TC")<<"Split3DKink found a kink at Tp3s point "<<kinkPts[0];
1820 
1821  // Only split the biggest angle kink
1822  double big = 0;
1823  unsigned short kpt = 0;
1824  for(auto ipt : kinkPts) {
1825  double dang = KinkAngle(slc, pfp.Tp3s, ipt, sep);
1826  if(dang > big) {
1827  big = dang;
1828  kpt = ipt;
1829  }
1830  } // ipt
1831  if(kpt < 1 || kpt > pfp.Tp3s.size() - 1) return false;
1832  // determine which tjs need to be split
1833  std::vector<unsigned short> tjids;
1834  std::vector<unsigned short> vx2ids;
1835  // inspect a few Tp3s near the kink point to get a list of Tjs
1836  for(unsigned short ipt = kpt; ipt < kpt + 2; ++ipt) {
1837  auto& tp3 = pfp.Tp3s[ipt];
1838  for(auto& tp2 : tp3.Tj2Pts) {
1839  // see if this Tj id is in the list
1840  if(std::find(tjids.begin(), tjids.end(), tp2.id) != tjids.end()) continue;
1841  // ensure that it is pfp.TjIDs
1842  if(std::find(pfp.TjIDs.begin(), pfp.TjIDs.end(), tp2.id) == pfp.TjIDs.end()) continue;
1843  tjids.push_back(tp2.id);
1844  auto& tj = slc.tjs[tp2.id - 1];
1845  auto& tp = tj.Pts[tp2.ipt];
1846  unsigned short closeEnd = USHRT_MAX;
1847  if(tp2.ipt < tj.EndPt[0] + 2) closeEnd = 0;
1848  if(tp2.ipt > tj.EndPt[1] - 2) closeEnd = 1;
1849  if(closeEnd < 2) {
1850  // No split is needed and there should be a vertex at this end of the Tj that
1851  // should be associated with a 3D vertex that we will construct
1852  if(tj.VtxID[closeEnd] == 0) {
1853 // std::cout<<Split3DKink: TODO Tj "<<tj.ID<<" has no vertex attached on end "<<closeEnd<<". Write some code.\n";
1854  return false;
1855  }
1856  vx2ids.push_back(tj.VtxID[closeEnd]);
1857  if(prt) mf::LogVerbatim("TC")<<" tj "<<tj.ID<<" use existing 2V"<<tj.VtxID[closeEnd];
1858  } else {
1859  // make a 2D vertex at this point
1860  VtxStore vx2;
1861  vx2.ID = slc.vtxs.size() + 1;
1862  vx2.CTP = tj.CTP;
1863  vx2.Topo = 10;
1864  vx2.Pos = tp.Pos;
1865  if(!StoreVertex(slc, vx2)) return false;
1866  if(!SplitTraj(slc, tp2.id - 1, tp2.ipt, slc.vtxs.size() - 1, prt)) return false;
1867  vx2ids.push_back(vx2.ID);
1868  AttachAnyTrajToVertex(slc, slc.vtxs.size() - 1, prt);
1869  if(prt) mf::LogVerbatim("TC")<<" tj "<<tj.ID<<" new 2V"<<vx2.ID;
1870  }
1871  } // tp2
1872  } // ipt
1873 
1874  if(vx2ids.size() != slc.nPlanes) {
1875 // std::cout<<"Split3DKink: TODO pfp "<<pfp.ID<<" only has "<<vx2ids.size()<<" 2D vertices. \n";
1876  return false;
1877  }
1878  Vtx3Store vx3;
1879  vx3.TPCID = pfp.TPCID;
1880  vx3.Vx2ID.resize(slc.nPlanes);
1881  vx3.ID = slc.vtx3s.size() + 1;
1882  vx3.X = pfp.Tp3s[kpt].Pos[0];
1883  vx3.Y = pfp.Tp3s[kpt].Pos[1];
1884  vx3.Z = pfp.Tp3s[kpt].Pos[2];
1885  for(auto vx2id : vx2ids) {
1886  if(vx2id == 0) continue;
1887  auto& vx2 = slc.vtxs[vx2id - 1];
1888  unsigned short plane = DecodeCTP(vx2.CTP).Plane;
1889  vx3.Vx2ID[plane] = vx2id;
1890  vx2.Vx3ID = vx3.ID;
1891  } // vx2id
1892  ++evt.globalS3ID;
1893  vx3.UID = evt.globalS3ID;
1894  slc.vtx3s.push_back(vx3);
1895  // mark this as needing an update
1896  pfp.NeedsUpdate = true;
1897  return true;
1898  } // Split3DKink
1899 
1901  std::vector<unsigned short> FindKinks(TCSlice& slc, PFPStruct& pfp, double sep, bool prt)
1902  {
1903  // returns a vector of indices in pfp.Tp3s where kinks exist. The kink angle is calculated using
1904  // Tp3s separated by +/- sep (cm)
1905  std::vector<unsigned short> kinkPts;
1906  // look for a kink angle greater than angCut
1907  double angCut = 0.3;
1908  double kang = 0;
1909  unsigned short kStart = USHRT_MAX;
1910  // foundKink is set true after a kink is found to skip past some number of points after the kink
1911  bool foundKink = false;
1912  double kinkSep2 = 2 * sep * sep;
1913  for(unsigned short ipt = 1; ipt < pfp.Tp3s.size(); ++ipt) {
1914  // skip ahead after a kink?
1915  if(foundKink) {
1916  // location of the previously found kink
1917  unsigned short kpt = kinkPts[kinkPts.size() - 1];
1918  if(foundKink && PosSep2(pfp.Tp3s[ipt].Pos, pfp.Tp3s[kpt].Pos) < kinkSep2) continue;
1919  }
1920  foundKink = false;
1921  double dang = KinkAngle(slc, pfp.Tp3s, ipt, sep);
1922  if(dang < angCut && kStart == USHRT_MAX) continue;
1923  // found a kink larger than the cut. See if this is the onset of a kink
1924  if(kStart == USHRT_MAX) {
1925  // onset of a kink
1926  kStart = ipt;
1927  } else {
1928  // a kink was found. Keep scanning until delta angle (dang) is less than the maximum (kang)
1929  if(dang < kang) {
1930  unsigned short klen = ipt - kStart;
1931  if(prt) mf::LogVerbatim("TC")<<" findKinks: kink angle "<<kang<<" at point "<<ipt<<" klen "<<klen;
1932  kinkPts.push_back(ipt - 1);
1933  foundKink = true;
1934  kStart = USHRT_MAX;
1935  } // dang < kang
1936  } // kink found
1937  kang = dang;
1938  } // ipt
1939  return kinkPts;
1940  } // findKinks
1941 
1943  double KinkAngle(TCSlice& slc, const std::vector<TrajPoint3>& tp3s, unsigned short atPt, double sep)
1944  {
1945  // calculate a kink angle at the TjPt
1946  if(tp3s.empty()) return -1;
1947  if(atPt < 1 || atPt > tp3s.size() - 2) return -1;
1948  double sep2 = sep * sep;
1949  unsigned short pt1 = USHRT_MAX;
1950  for(unsigned short ii = 1; ii < tp3s.size(); ++ii) {
1951  unsigned short ipt = atPt - ii;
1952  if(PosSep2(tp3s[atPt].Pos, tp3s[ipt].Pos) > sep2) {
1953  pt1 = ipt;
1954  break;
1955  }
1956  if(ipt == 0) break;
1957  } // ii
1958  if(pt1 == USHRT_MAX) return -1;
1959  unsigned short pt2 = USHRT_MAX;
1960  for(unsigned short ii = 1; ii < tp3s.size(); ++ii) {
1961  unsigned short ipt = atPt + ii;
1962  if(ipt == tp3s.size()) break;
1963  if(PosSep2(tp3s[atPt].Pos, tp3s[ipt].Pos) > sep2) {
1964  pt2 = ipt;
1965  break;
1966  }
1967  } // ii
1968  if(pt2 == USHRT_MAX) return -1;
1969  return DeltaAngle(tp3s[pt1].Dir, tp3s[pt2].Dir);
1970  } // KinkAngle
1971 
1974  {
1975  // The calling function should define the size of pfp.TjIDs
1976  PFPStruct pfp;
1977  pfp.ID = slc.pfps.size() + 1;
1978  pfp.ParentUID = 0;
1979  pfp.TPCID = slc.TPCID;
1980  // initialize arrays for both ends
1981  if(slc.nPlanes < 4) {
1982  pfp.dEdx[0].resize(slc.nPlanes, 0);
1983  pfp.dEdx[1].resize(slc.nPlanes, 0);
1984  pfp.dEdxErr[0].resize(slc.nPlanes, 0);
1985  pfp.dEdxErr[1].resize(slc.nPlanes, 0);
1986  }
1987  for(unsigned short end = 0; end < 2; ++end) {
1988  // BUG the double brace syntax is required to work around clang bug 21629
1989  // (https://bugs.llvm.org/show_bug.cgi?id=21629)
1990  pfp.Dir[end] = {{0.0, 0.0, 0.0}};
1991  pfp.DirErr[end] = {{0.0, 0.0, 0.0}};
1992  pfp.XYZ[end] = {{0.0, 0.0, 0.0}};
1993  }
1994  return pfp;
1995  } // CreatePFP
1996 
1999  {
2000  // Match Tjs in 3D and create PFParticles
2001 
2002  if(tcc.match3DCuts[0] <= 0) return;
2003 
2004  bool prt = (tcc.dbgPFP && tcc.dbgSlc);
2005 
2006  if(prt) mf::LogVerbatim("TC")<<" inside FindPFParticles";
2007  // clear matchVec
2008  slc.matchVec.clear();
2009  // clear the kEnvFlag bits on all Tjs. The bit will be set true when a TP is
2010  // used in a PFParticle Tp3
2011  for(auto& tj : slc.tjs) {
2012  for(auto& tp : tj.Pts) tp.Environment[kEnvFlag] = false;
2013  } // tj
2014 
2015  // Match these points in 3D and put the results in slc.matchVec
2016  std::vector<MatchStruct> matVec;
2017  // first look for 3-plane matches in a 3-plane TPC
2018  if(slc.nPlanes == 3) {
2019  // Match Tjs with high quality vertices first and the leftovers next
2020  for(short maxScore = 0; maxScore < 2; ++maxScore) FindXMatches(slc, 3, maxScore, matVec, prt);
2021  } // 3-plane TPC
2022  // Make 2-plane matches if we haven't hit the user-defined size limit
2023  if(matVec.size() < tcc.match3DCuts[4]) {
2024  // 2-plane TPC or 2-plane matches in a 3-plane TPC
2025  if(slc.nPlanes == 2) {
2026  for(short maxScore = 0; maxScore < 2; ++maxScore) FindXMatches(slc, 2, maxScore, matVec, prt);
2027  } else {
2028  // Make one attempt at 2-plane matches in a 3-plane TPC, setting maxScore large
2029  FindXMatches(slc, 2, 3, matVec, prt);
2030  }
2031  } // can add more combinations
2032 
2033  if(matVec.empty()) return;
2034 
2035  // sort by decreasing number of matched points
2036  if(matVec.size() > 1) {
2037  PFPStruct pfp = CreatePFP(slc);
2038  std::vector<int> dum1;
2039  std::vector<float> dum2;
2040  std::vector<SortEntry> sortVec(matVec.size());
2041  for(unsigned int ii = 0; ii < matVec.size(); ++ii) {
2042  sortVec[ii].index = ii;
2043  auto& ms = matVec[ii];
2044  sortVec[ii].val = ms.Count;
2045  // de-weight two-plane matches
2046  if(ms.TjIDs.size() == 2) sortVec[ii].val /= 2;
2047  } // ii
2048  std::sort(sortVec.begin(), sortVec.end(), valDecreasings);
2049  std::vector<MatchStruct> tmpVec;
2050  tmpVec.reserve(matVec.size());
2051  for(unsigned int ii = 0; ii < matVec.size(); ++ii) {
2052  tmpVec.push_back(matVec[sortVec[ii].index]);
2053  } // ii
2054  matVec = tmpVec;
2055  } // sort matVec
2056 
2057  // put the maybe OK matches into tjs
2058  PFPStruct pfp = CreatePFP(slc);
2059  for(auto& ms : matVec) {
2060  if(ms.Count < 2) continue;
2061  // check for duplicates
2062  bool skipit = false;
2063  for(auto& oms : slc.matchVec) {
2064  if(ms.TjIDs == oms.TjIDs) {
2065  skipit = true;
2066  break;
2067  }
2068  } // oms
2069  if(skipit) continue;
2070  // Find completeness, do the fit, don't fill Tp3s, don't print
2071  pfp.TjIDs = ms.TjIDs;
2072  FindCompleteness(slc, pfp, true, false, false);
2073  // save the info in matchStruct
2074  ms.TjCompleteness = pfp.TjCompleteness;
2075  ms.Pos = pfp.XYZ[0];
2076  ms.Dir = pfp.Dir[0];
2077  slc.matchVec.push_back(ms);
2078  }
2079  if(slc.matchVec.empty()) return;
2080 
2081  if(prt) {
2082  mf::LogVerbatim myprt("TC");
2083  myprt<<"FPFP: slc.matchVec\n";
2084  unsigned short cnt = 0;
2085  PFPStruct pfp = CreatePFP(slc);
2086  std::vector<int> dum1;
2087  std::vector<float> dum2;
2088  for(unsigned int ii = 0; ii < slc.matchVec.size(); ++ii) {
2089  auto& ms = slc.matchVec[ii];
2090  if(ms.Count == 0) continue;
2091  myprt<<std::setw(4)<<ii<<" Count "<<std::setw(5)<<(int)ms.Count;
2092  myprt<<" Tj ID-UID:";
2093  for(auto& tjid : ms.TjIDs) {
2094  myprt<<" t"<<tjid<<"-T"<<slc.tjs[tjid-1].UID;
2095  }
2096  myprt<<" Comp ";
2097  for(unsigned short itj = 0; itj < ms.TjCompleteness.size(); ++itj) {
2098  myprt<<std::setprecision(2)<<std::setw(6)<<ms.TjCompleteness[itj];
2099  }
2100  myprt<<" Pos ("<<std::setprecision(0)<<std::fixed;
2101  myprt<<ms.Pos[0]<<", "<<ms.Pos[1]<<", "<<ms.Pos[2];
2102  myprt<<") Dir "<<std::setprecision(2)<<std::setw(6)<<ms.Dir[0]<<std::setw(6)<<ms.Dir[1]<<std::setw(6)<<ms.Dir[2];
2103  myprt<<" projInPlane";
2104  for(unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
2105  CTP_t inCTP = EncodeCTP(pfp.TPCID.Cryostat, pfp.TPCID.TPC, plane);
2106  auto tp = MakeBareTP(slc, ms.Pos, ms.Dir, inCTP);
2107  myprt<<" "<<std::setprecision(2)<<tp.Delta;
2108  } // plane
2109  myprt<<" maxTjLen "<<(int)MaxTjLen(slc, ms.TjIDs);
2110  myprt<<" MCSMom "<<MCSMom(slc, ms.TjIDs);
2111  myprt<<" PDGCodeVote "<<PDGCodeVote(slc, ms.TjIDs, false);
2112  myprt<<"\n";
2113  ++cnt;
2114  if(cnt == 500 || ms.Count < 2) {
2115  myprt<<"...stopped printing after 500 entries or Count < 2";
2116  break;
2117  }
2118  } // ii
2119  } // prt
2120 
2121  // create the list of associations to matches that will be converted to PFParticles
2122  // Start with large count tj matches that have a consistent PDGCode and no vertex attachments
2123  // and high completeness
2124  if(slc.matchVec.size() > 1 && slc.matchVec[0].Count > 2 * slc.matchVec[1].Count) {
2125  auto& ms = slc.matchVec[0];
2126  int pdgCode = PDGCodeVote(slc, ms.TjIDs, prt);
2127  bool hasVx = false;
2128  for(auto tid : ms.TjIDs) {
2129  auto& tj = slc.tjs[tid - 1];
2130  if(tj.VtxID[0] > 0 || tj.VtxID[1] > 0) hasVx = true;
2131  } // tid
2132  if(pdgCode != 0 && !hasVx) {
2133  float minCompleteness = 1;
2134  for(unsigned short itj = 0; itj < ms.TjCompleteness.size(); ++itj) {
2135  if(ms.TjCompleteness[itj] < minCompleteness) minCompleteness = ms.TjCompleteness[itj];
2136  } // itj
2137  if(minCompleteness > 0.5) {
2138  PFPStruct pfp = CreatePFP(slc);
2139  pfp.TjIDs = ms.TjIDs;
2140  // note that the ms position is the average position of all 3D matched Tp3s at this point.
2141  // It is not the start position. This will be determined in DefinePFP.
2142  pfp.XYZ[0] = ms.Pos;
2143  pfp.Dir[0] = ms.Dir;
2144  pfp.MatchVecIndex = 0;
2145  // Set the PDGCode so DefinePFP can ignore incompatible matches
2146  pfp.PDGCode = pdgCode;
2147  if(DefinePFP("FPFP", slc, pfp, prt) && AnalyzePFP(slc, pfp, prt) && StorePFP(slc, pfp)) {
2148  ms.Count = 0;
2149  // clobber MatchStructs that use the Tjs in this pfp
2150  for(auto& allms : slc.matchVec) {
2151  auto shared = SetIntersection(allms.TjIDs, pfp.TjIDs);
2152  if(!shared.empty()) allms.Count = 0;
2153  } // allms
2154  } // define/analyze/store PFP
2155  else {
2156  if(prt) mf::LogVerbatim("TC")<<" Define/Analyze/Store PFP failed";
2157  } // define/analyze/store PFP
2158  } // minCompleteness > 0.5
2159  } // pdgCode != 0
2160  } // large count on the first matchVec
2161 
2162  // Next consider Tjs attached to 3D vertices. This is only done when reconstructing neutrino events
2163  if(!tcc.modes[kTestBeam]) {
2164  Match3DVtxTjs(slc, prt);
2165  }
2166 
2167  // define the PFParticleList
2168  for(unsigned int indx = 0; indx < slc.matchVec.size(); ++indx) {
2169  auto& ms = slc.matchVec[indx];
2170  // ignore dead matches
2171  if(ms.Count == 0) continue;
2172  // skip this match if any of the trajectories is already matched or merged and killed
2173  bool skipit = false;
2174  // check for muons and delta rays or InShower Tjs
2175  bool has13 = false;
2176  bool has11 = false;
2177  for(unsigned short itj = 0; itj < ms.TjIDs.size(); ++itj) {
2178  if(ms.TjIDs[itj] <= 0 || ms.TjIDs[itj] > (int)slc.tjs.size()) {
2179  std::cout<<"FindPFParticles: bogus ms TjID "<<ms.TjIDs[itj]<<"\n";
2180  return;
2181  }
2182  auto& tj = slc.tjs[ms.TjIDs[itj] - 1];
2183  if(tj.AlgMod[kMat3D] || tj.AlgMod[kKilled]) skipit = true;
2184  // skip low TjCompleteness
2185  if(ms.TjCompleteness.empty()) {
2186  std::cout<<"FindPFParticles: ms.TjCompleteness is empty\n";
2187  return;
2188  }
2189  if(ms.TjCompleteness[itj] < 0.1) skipit = true;
2190  if(tj.PDGCode == 13) has13 = true;
2191  if(tj.PDGCode == 11) has11 = true;
2192  } // tjID
2193  if(skipit) continue;
2194  if(has13 && has11) continue;
2195  int pdgCode = PDGCodeVote(slc, ms.TjIDs, prt);
2196  PFPStruct pfp = CreatePFP(slc);
2197  pfp.TjIDs = ms.TjIDs;
2198  // note that the ms position is the average position of all 3D matched Tp3s at this point.
2199  // It is not the start position. This will be determined in DefinePFP.
2200  pfp.XYZ[0] = ms.Pos;
2201  pfp.Dir[0] = ms.Dir;
2202  pfp.MatchVecIndex = indx;
2203  // Set the PDGCode so DefinePFP can ignore incompatible matches
2204  pfp.PDGCode = pdgCode;
2205  // set the PDGCode to ensure that delta rays aren't merged with muons. PDGCodeVote
2206  // returns 0 if the vote is mixed
2207  if(has13) pfp.PDGCode = 13;
2208  if(has11) pfp.PDGCode = 11;
2209  if(!DefinePFP("FPFP", slc, pfp, prt)) {
2210  if(prt) mf::LogVerbatim("TC")<<" DefinePFP failed";
2211  continue;
2212  }
2213  double sep = 1;
2214  Split3DKink(slc, pfp, sep, prt);
2215  if(!AnalyzePFP(slc, pfp, prt)) continue;
2216  if(!StorePFP(slc, pfp)) {
2217  if(prt) mf::LogVerbatim("TC")<<" StorePFP failed P"<<pfp.ID;
2218  continue;
2219  }
2220  ms.Count = 0;
2221  // clobber MatchStructs that use the Tjs in this pfp
2222  for(auto& allms : slc.matchVec) {
2223  auto shared = SetIntersection(allms.TjIDs, pfp.TjIDs);
2224  if(!shared.empty()) allms.Count = 0;
2225  } // allms
2226  } // indx
2227 
2228 // MatchMissedTjs(slc);
2229 
2230  } // FindPFParticles
2231 
2233  bool DefinePFP(std::string inFcnLabel, TCSlice& slc, PFPStruct& pfp, bool prt)
2234  {
2235  // This function is called after the 3D matched TjIDs have been specified and optionally
2236  // a start or end vertex ID. It defines the PFParticle but doesn't store it
2237 
2238  if(pfp.PDGCode == 1111) return false;
2239  if(pfp.TjIDs.size() < 2) return false;
2240 
2241  std::string fcnLabel = inFcnLabel + ".DPFP";
2242 
2243  // require at least one tj in at least two planes
2244  std::vector<unsigned short> nInPln(slc.nPlanes);
2245  for(auto tjid : pfp.TjIDs) {
2246  auto& tj = slc.tjs[tjid - 1];
2247  ++nInPln[DecodeCTP(tj.CTP).Plane];
2248  if(tj.AlgMod[kMat3D] || tj.AlgMod[kKilled]) {
2249  std::cout<<fcnLabel<<" P"<<pfp.ID<<" uses T"<<tj.ID<<" but kMat3D is set true\n";
2250  return false;
2251  }
2252  } // tjid
2253  unsigned short npl = 0;
2254  for(unsigned short plane = 0; plane < slc.nPlanes; ++plane) if(nInPln[plane] > 0) ++npl;
2255  if(npl < 2) return false;
2256 
2257  for(unsigned short end = 0; end < 2; ++end) {
2258  if(pfp.Vx3ID[end] < 0 || pfp.Vx3ID[end] > (int)slc.vtxs.size()) {
2259  std::cout<<fcnLabel<<" P"<<pfp.ID<<" end "<<end<<" is invalid\n";
2260  return false;
2261  }
2262  } // end
2263 
2264  if(pfp.Vx3ID[0] == 0 && pfp.Vx3ID[1] > 0) {
2265  std::cout<<fcnLabel<<" P"<<pfp.ID<<" end 1 has a vertex but end 0 doesn't. No endpoints defined\n";
2266  return false;
2267  }
2268 
2269  // check for vertex consistency. There should only be one tj in each plane
2270  // that is attached to a vertex. Remove the shorter tj from the list
2271  // if that is not the case
2272  if(pfp.Vx3ID[0] > 0) PFPVxTjOK(slc, pfp, prt);
2273 
2274  bool pfpTrackLike = (MaxTjLen(slc, pfp.TjIDs) > tcc.match3DCuts[5] && MCSMom(slc, pfp.TjIDs) > tcc.match3DCuts[3]);
2275  // don't look for broken Tjs for primary electron PFPs
2276  if(pfp.PDGCode == 111) pfpTrackLike = false;
2277 
2278  if(prt) {
2279  mf::LogVerbatim myprt("TC");
2280  myprt<<fcnLabel<<" pfp P"<<pfp.ID;
2281  myprt<<" Vx3ID 3V"<<pfp.Vx3ID[0]<<" 3V"<<pfp.Vx3ID[1];
2282  myprt<<" Tjs";
2283  for(auto id : pfp.TjIDs) myprt<<" T"<<id;
2284  myprt<<" matchVec index "<<pfp.MatchVecIndex;
2285  myprt<<" max Tj len "<<MaxTjLen(slc, pfp.TjIDs);
2286  myprt<<" MCSMom "<<MCSMom(slc, pfp.TjIDs);
2287  myprt<<" pfpTrackLike? "<<pfpTrackLike;
2288  } // prt
2289 
2290  if(tcc.useAlg[kMat3DMerge] && pfpTrackLike && pfp.MatchVecIndex < slc.matchVec.size()) {
2291  // The index of slc.matchVec has been specified for this pfp so we can look for evidence of
2292  // broken Tjs starting at the beginning
2293  for(unsigned short ims = 0; ims < pfp.MatchVecIndex + 10; ++ims) {
2294  if(ims >= slc.matchVec.size()) break;
2295  auto& ms = slc.matchVec[ims];
2296  if(ms.Count == 0) continue;
2297  std::vector<int> shared = SetIntersection(ms.TjIDs, pfp.TjIDs);
2298  if(shared.size() < 2) continue;
2299  // check the max length Tj and cut on MCSMom
2300  if(MaxTjLen(slc, ms.TjIDs) < tcc.match3DCuts[5]) continue;
2301  if(MCSMom(slc, ms.TjIDs) < tcc.match3DCuts[3]) continue;
2302  for(auto tjid : ms.TjIDs) {
2303  if(std::find(shared.begin(), shared.end(), tjid) != shared.end()) continue;
2304  auto& tj = slc.tjs[tjid - 1];
2305  if(tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) continue;
2306  if(tj.AlgMod[kMat3D]) continue;
2307  // check for PDGCode compatibility - muons and delta rays
2308  if(pfp.PDGCode == 13 && tj.PDGCode == 11) continue;
2309  if(pfp.PDGCode == 11 && tj.PDGCode == 13) continue;
2310  if(SharesHighScoreVx(slc, pfp, tj)) continue;
2311  if(tj.MCSMom < tcc.match3DCuts[3]) continue;
2312  float len = PosSep(tj.Pts[tj.EndPt[0]].Pos, tj.Pts[tj.EndPt[1]].Pos);
2313  if(len < tcc.match3DCuts[5]) continue;
2314  // check for a compatible merge
2315  bool skipit = false;
2316  for(auto tjid : pfp.TjIDs) {
2317  auto& ptj = slc.tjs[tjid - 1];
2318  if(ptj.CTP != tj.CTP) continue;
2319  if(!CompatibleMerge(slc, tj, ptj, prt)) {
2320  skipit = true;
2321  break;
2322  }
2323  } // tjid
2324  if(skipit) continue;
2325  if(prt) mf::LogVerbatim("TC")<<" add T"<<tjid<<" MCSMom "<<tj.MCSMom<<" length "<<len;
2326  pfp.TjIDs.push_back(tjid);
2327  PFPVxTjOK(slc, pfp, prt);
2328  } // tjid
2329  } // ims
2330  } // matchVec index defined
2331 
2332 /* BB April 25. CheckAndMerge needs work. Shut it off for now.
2333  // check the completeness of matching points in this set of Tjs and possibly
2334  // merge Tjs
2335  if(tcc.useAlg[kMat3DMerge] && pfpTrackLike) {
2336  if(!CheckAndMerge(slc, pfp, prt)) return false;
2337  } else {
2338  // not track-like. Find completeness and fill the TP3s
2339  FindCompleteness(slc, pfp, true, true, prt);
2340  }
2341 */
2342  // Find completeness and fill the TP3s
2343  FindCompleteness(slc, pfp, true, true, prt);
2344  if(pfp.Tp3s.empty()) return false;
2345 
2346  // Set the starting position in 3D if it isn't already defined by a 3D vertex
2347  SetStart(slc, pfp, prt);
2348  FollowTp3s(slc, pfp, prt);
2349  // Check the list of Tjs and merge those that are in the same plane
2350  MergePFPTjs(slc, pfp, prt);
2351 
2352  if(prt) {
2353  mf::LogVerbatim myprt("TC");
2354  myprt<<fcnLabel<<" pfp P"<<pfp.ID;
2355  myprt<<" Vx3ID 3V"<<pfp.Vx3ID[0]<<" 3V"<<pfp.Vx3ID[1];
2356  myprt<<" Tjs";
2357  for(auto id : pfp.TjIDs) myprt<<" T"<<id;
2358  myprt<<" Tp3s size "<<pfp.Tp3s.size();
2359  }
2360 
2361  pfp.NeedsUpdate = false;
2362  return true;
2363  } // DefinePFP
2364 
2366  bool PFPVxTjOK(TCSlice& slc, PFPStruct& pfp, bool prt)
2367  {
2368  // Checks the PFP Vx3 -> Vx2 -> Tj assignment to see if there is more
2369  // than one tj in a plane in the pfp.TjIDs list that is attached to the same 2D vertex.
2370  // This problem is fixed by removing the shorter tj from the TjIDs list. This function
2371  // return true if nothing was done to TjIDs
2372  if(pfp.ID == 0) return true;
2373  if(pfp.TjIDs.empty()) return true;
2374  if(pfp.Vx3ID[0] <= 0 || pfp.Vx3ID[0] > (int)slc.vtx3s.size()) return true;
2375 
2376  auto& vx3 = slc.vtx3s[pfp.Vx3ID[0] - 1];
2377  std::vector<int> killMe;
2378  for(auto vx2id : vx3.Vx2ID) {
2379  if(vx2id == 0) continue;
2380  auto& vx2 = slc.vtxs[vx2id - 1];
2381  auto tjlist = GetVtxTjIDs(slc, vx2);
2382  auto setInt = SetIntersection(pfp.TjIDs, tjlist);
2383 /*
2384  std::cout<<"PVTC: P"<<pfp.ID<<" Tjs";
2385  for(auto tid : pfp.TjIDs) std::cout<<" T"<<tid;
2386  std::cout<<" set Intersection";
2387  for(auto tid : setInt) std::cout<<" T"<<tid;
2388  std::cout<<"\n";
2389 */
2390  if(setInt.size() < 2) continue;
2391  // find the longest one
2392  int imLong = 0;
2393  unsigned short lenth = 0;
2394  for(auto tid : setInt) {
2395  auto& tj = slc.tjs[tid - 1];
2396  unsigned short npts = tj.EndPt[1] - tj.EndPt[0] + 1;
2397  if(npts < lenth) continue;
2398  lenth = npts;
2399  imLong = tj.ID;
2400  } // tid
2401  if(imLong == 0) continue;
2402  // add the others to the killMe list
2403  for(auto tid : setInt) if(tid != imLong) killMe.push_back(tid);
2404  } // vx2id
2405  if(killMe.empty()) return true;
2406  if(prt) {
2407  mf::LogVerbatim myprt("TC");
2408  myprt<<"PVTC: P"<<pfp.ID<<" removing short tjs attached to a vertex:";
2409  for(auto tid : killMe) myprt<<" T"<<tid;
2410  }
2411  // re-create the TjIDs vector
2412  std::vector<int> tmp;
2413  for(auto tid : pfp.TjIDs) {
2414  if(std::find(killMe.begin(), killMe.end(), tid) == killMe.end()) tmp.push_back(tid);
2415  } // tid
2416  pfp.TjIDs = tmp;
2417  return false;
2418  } // PFPVxTjOK
2419 
2421  bool AnalyzePFP(TCSlice& slc, PFPStruct& pfp, bool prt)
2422  {
2423  // Analyzes the PFP for oddities and tries to fix them
2424  if(pfp.ID == 0) return false;
2425  if(pfp.TjIDs.empty()) return false;
2426  if(pfp.Tp3s.empty()) return false;
2427 
2428  // don't bother analyzing this pfp has been altered
2429  if(pfp.NeedsUpdate) {
2430  if(prt) mf::LogVerbatim("TC")<<"AnalyzePFP: P"<<pfp.ID<<" needs to be updated. Skip analysis ";
2431  return true;
2432  }
2433 
2434  // check the tj completeness
2435  float minCompleteness = 0.95;
2436  for(auto tjc : pfp.TjCompleteness) if(tjc < minCompleteness) minCompleteness = tjc;
2437  if(prt) mf::LogVerbatim("TC")<<"inside AnalyzePFP P"<<pfp.ID<<" minCompleteness "<<minCompleteness;
2438  if(minCompleteness == 0.95) return true;
2439  // don't analyze electrons
2440  if(pfp.PDGCode == 111) return true;
2441 
2442  // compare the Tjs in Tp3s with those in TjIDs
2443  std::vector<int> tjIDs;
2444  std::vector<unsigned short> tjCnt;
2445  for(auto& tp3 : pfp.Tp3s) {
2446  for(auto& tp2 : tp3.Tj2Pts) {
2447  // convert to int for std::find
2448  int itjID = tp2.id;
2449  unsigned short indx = 0;
2450  for(indx = 0; indx < tjIDs.size(); ++indx) if(tjIDs[indx] == tp2.id) break;
2451  if(indx == tjIDs.size()) {
2452  tjIDs.push_back(itjID);
2453  tjCnt.push_back(1);
2454  } else {
2455  ++tjCnt[indx];
2456  }
2457  } // tp2
2458  } // tp3
2459 
2460  // look for differences
2461  for(unsigned short ii = 0; ii < tjIDs.size(); ++ii) {
2462  if(std::find(pfp.TjIDs.begin(), pfp.TjIDs.end(), tjIDs[ii]) != pfp.TjIDs.end()) continue;
2463  auto& missTj = slc.tjs[tjIDs[ii] - 1];
2464  if(missTj.AlgMod[kMat3D]) continue;
2465  unsigned short npwc = NumPtsWithCharge(slc, missTj, false);
2466  if(prt) mf::LogVerbatim("TC")<<" missed T"<<missTj.ID<<" npwc "<<npwc<<" tjCnt "<<tjCnt[ii];
2467  if(tjCnt[ii] < 0.5 * npwc) continue;
2468  // June 4, 2018. 3D merging is turned off so require the missed tj to be in
2469  // a missing plane
2470  bool skipit = false;
2471  for(auto tid : pfp.TjIDs) {
2472  auto& tj = slc.tjs[tid - 1];
2473  if(tj.CTP == missTj.CTP) skipit = true;
2474  } // tid
2475  if(skipit) continue;
2476  // add the missed Tj to the pfp and flag it as needing an update
2477  pfp.TjIDs.push_back(missTj.ID);
2478  if(PFPVxTjOK(slc, pfp, prt)) pfp.NeedsUpdate = true;
2479  } // ii
2480 
2481  if(pfp.NeedsUpdate) DefinePFP("APFP", slc, pfp, prt);
2482 
2483  if(prt) {
2484  mf::LogVerbatim myprt("TC");
2485  myprt<<"APFP: Tjs in pfp\n";
2486  for(auto tjid : pfp.TjIDs) {
2487  auto& tj = slc.tjs[tjid - 1];
2488  myprt<<"T"<<tj.ID<<" npwc "<<NumPtsWithCharge(slc, tj, false);
2489  unsigned short indx = 0;
2490  for(indx = 0; indx < tjIDs.size(); ++indx) if(tjIDs[indx] == tjid) break;
2491  if(indx == tjIDs.size()) {
2492  myprt<<" not found in P"<<pfp.ID<<"\n";
2493  continue;
2494  }
2495  myprt<<" nTp3 "<<tjCnt[indx]<<"\n";
2496  } // tjid
2497  } // prt
2498  return true;
2499  } // AnalyzePFP
2500 
2503  {
2504  // Ensure that all PFParticles have a start vertex. It is possible for
2505  // PFParticles to be attached to a 3D vertex that is later killed.
2506  if(!slc.isValid) return;
2507 
2508  for(auto& pfp : slc.pfps) {
2509  if(pfp.ID == 0) continue;
2510  if(pfp.Vx3ID[0] > 0) continue;
2511  Vtx3Store vx3;
2512  vx3.TPCID = pfp.TPCID;
2513  vx3.Vx2ID.resize(slc.nPlanes);
2514  // Flag it as a PFP vertex that isn't required to have matched 2D vertices
2515  vx3.Wire = -2;
2516  vx3.X = pfp.XYZ[0][0];
2517  vx3.Y = pfp.XYZ[0][1];
2518  vx3.Z = pfp.XYZ[0][2];
2519  vx3.ID = slc.vtx3s.size() + 1;
2520  vx3.Primary = false;
2521  ++evt.globalS3ID;
2522  vx3.UID = evt.globalS3ID;
2523  slc.vtx3s.push_back(vx3);
2524 // std::cout<<"PFPVertexCheck: P"<<pfp.ID<<" create 3V"<<vx3.ID<<"\n";
2525  pfp.Vx3ID[0] = vx3.ID;
2526  } // pfp
2527  } // PFPVertexCheck
2528 
2530  void DefinePFPParents(TCSlice& slc, bool prt)
2531  {
2532  /*
2533  This function reconciles vertices, PFParticles and slc, then
2534  defines the parent (j) - daughter (i) relationship and PDGCode. Here is a
2535  description of the conventions:
2536 
2537  V1 is the highest score 3D vertex in this tpcid so a neutrino PFParticle P1 is defined.
2538  V4 is a high-score vertex that has lower score than V1. It is declared to be a
2539  primary vertex because its score is higher than V5 and it is not associated with the
2540  neutrino interaction
2541  V6 was created to adhere to the convention that all PFParticles, in this case P9,
2542  be associated with a start vertex. There is no score for V6. P9 is it's own parent
2543  but is not a primary PFParticle.
2544 
2545  P1 - V1 - P2 - V2 - P4 - V3 - P5 V4 - P6 V6 - P9
2546  \ \
2547  P3 P7 - V5 - P8
2548 
2549  The PrimaryID in this table is the ID of the PFParticle that is attached to the
2550  primary vertex, which may or may not be a neutrino interaction vertex.
2551  The PrimaryID is returned by the PrimaryID function
2552  PFP parentID DtrIDs PrimaryID
2553  -----------------------------------
2554  P1 P1 P2, P3 P1
2555  P2 P1 P4 P2
2556  P3 P1 none P3
2557  P4 P2 P5 P2
2558  P5 P4 none P2
2559 
2560  P6 P6 none P6
2561  P7 P7 P8 P7
2562 
2563  P9 P9 none 0
2564 
2565  */
2566  if(slc.pfps.empty()) return;
2567  if(tcc.modes[kTestBeam]) return;
2568 
2569  int neutrinoPFPID = 0;
2570  for(auto& pfp : slc.pfps) {
2571  if(pfp.ID == 0) continue;
2572  if(!tcc.modes[kTestBeam] && neutrinoPFPID == 0 && (pfp.PDGCode == 12 || pfp.PDGCode == 14)) {
2573  neutrinoPFPID = pfp.ID;
2574  break;
2575  }
2576  } // pfp
2577 
2578  // define the end vertex if the Tjs have end vertices
2579  constexpr unsigned short end1 = 1;
2580  for(auto& pfp : slc.pfps) {
2581  if(pfp.ID == 0) continue;
2582  // already done?
2583  if(pfp.Vx3ID[end1] > 0) continue;
2584  // ignore shower-like pfps
2585  if(IsShowerLike(slc, pfp.TjIDs)) continue;
2586  // count 2D -> 3D matched vertices
2587  unsigned short cnt3 = 0;
2588  unsigned short vx3id = 0;
2589  // list of unmatched 2D vertices that should be merged
2590  std::vector<int> vx2ids;
2591  for(auto tjid : pfp.TjIDs) {
2592  auto& tj = slc.tjs[tjid - 1];
2593  if(tj.VtxID[end1] == 0) continue;
2594  auto& vx2 = slc.vtxs[tj.VtxID[end1] - 1];
2595  if(vx2.Vx3ID == 0) {
2596  if(vx2.Topo == 1 && vx2.NTraj == 2) vx2ids.push_back(vx2.ID);
2597  continue;
2598  }
2599  if(vx3id == 0) vx3id = vx2.Vx3ID;
2600  if(vx2.Vx3ID == vx3id) ++cnt3;
2601  } // tjid
2602  if(cnt3 > 1) {
2603  // ensure it isn't attached at the other end
2604  if(pfp.Vx3ID[1 - end1] == vx3id) continue;
2605  pfp.Vx3ID[end1] = vx3id;
2606  if(cnt3 != slc.nPlanes && tcc.modes[kDebug]) {
2607  std::cout<<"DPFPR: Missed an end vertex for PFP "<<pfp.ID<<" Write some code\n";
2608  }
2609  } // cnt3 > 1
2610  } // pfp
2611 
2612  // Assign a PDGCode to each PFParticle and look for a parent
2613  for(auto& pfp : slc.pfps) {
2614  if(pfp.ID == 0) continue;
2615  // skip a neutrino PFParticle
2616  if(pfp.PDGCode == 12 || pfp.PDGCode == 14 || pfp.PDGCode == 22) continue;
2617  pfp.PDGCode = PDGCodeVote(slc, pfp.TjIDs, prt);
2618  // Define a PFP parent if there are two or more Tjs that are daughters of
2619  // Tjs that are used by the same PFParticle
2620  int pfpParentID = INT_MAX;
2621  unsigned short nParent = 0;
2622  for(auto tjid : pfp.TjIDs) {
2623  auto& tj = slc.tjs[tjid - 1];
2624  if(tj.ParentID <= 0) continue;
2625  auto parPFP = GetAssns(slc, "T", tj.ParentID, "P");
2626  if(parPFP.empty()) continue;
2627  if(pfpParentID == INT_MAX) pfpParentID = parPFP[0];
2628  if(parPFP[0] == pfpParentID) ++nParent;
2629  } // ii
2630  if(nParent > 1) {
2631  auto& ppfp = slc.pfps[pfpParentID - 1];
2632  // set the parent UID
2633  pfp.ParentUID = ppfp.UID;
2634  // add to the parent daughters list
2635  ppfp.DtrUIDs.push_back(pfp.UID);
2636  } // nParent > 1
2637  } // ipfp
2638 
2639 /* TODO: This needs work
2640  if(tcc.modes[kTestBeam]) {
2641  DefinePFPParentsTestBeam(slc, prt);
2642  return;
2643  }
2644 */
2645  // associate primary PFParticles with a neutrino PFParticle
2646  if(neutrinoPFPID > 0) {
2647  auto& neutrinoPFP = slc.pfps[neutrinoPFPID - 1];
2648  int vx3id = neutrinoPFP.Vx3ID[1];
2649  for(auto& pfp : slc.pfps) {
2650  if(pfp.ID == 0 || pfp.ID == neutrinoPFPID) continue;
2651  if(pfp.Vx3ID[0] != vx3id) continue;
2652  pfp.ParentUID = (size_t)neutrinoPFPID;
2653  pfp.Primary = true;
2654  neutrinoPFP.DtrUIDs.push_back(pfp.ID);
2655  if(pfp.PDGCode == 111) neutrinoPFP.PDGCode = 12;
2656  } // pfp
2657  } // neutrino PFP exists
2658  } // DefinePFPParents
2659 
2661  void DefinePFPParentsTestBeam(TCSlice& slc, bool prt)
2662  {
2663  // analog of the one above that was written for neutrino interactions. This differs in that
2664  // the Tj parent - daughter relationship isn't known yet. If one exists, it is ignored...
2665  // The assumption here is that all PFParticles that enter (end0) from upstream Z are parents and
2666  // any PFParticles attached to them at end1 are daughters.
2667 
2668  // create a list (stack) of parent ID <-> daughter IDs. The idea is similar to that
2669  // used in DefineTjParents. A parent-daughter association is made for each entry. After
2670  // it is made, 1) that entry is removed from the stack, 2) the daughter is checked to see
2671  // if it a parent of a grand-daughter and if so that pair is added to the stack.
2672  std::vector<std::pair<unsigned short, unsigned short>> pardtr;
2673 
2674  // Fill the stack with parents that enter the TPC and have daughters attached to
2675  // 3D vertices at the other end
2676  double fidZCut = slc.zLo + 2;
2677  for(auto& parPFP : slc.pfps) {
2678  if(parPFP.ID == 0) continue;
2679  parPFP.Primary = false;
2680  if(parPFP.XYZ[0][2] > fidZCut) continue;
2681  parPFP.Primary = true;
2682  // we found a pfp that entered the TPC. Call it the parent and look for a daughter
2683  if(prt) mf::LogVerbatim("TC")<<"DPFPTestBeam: parent "<<parPFP.ID<<" end1 vtx "<<parPFP.Vx3ID[1];
2684  if(parPFP.Vx3ID[1] == 0) continue;
2685  // There must be other Tjs attached to this vertex which are the daughters. Find them
2686  // and add them to the pardtr stack
2687  float score = 0;
2688  auto& vx3 = slc.vtx3s[parPFP.Vx3ID[1] - 1];
2689  // ensure that it is valid
2690  if(vx3.ID == 0) continue;
2691  // get a list of Tjs attached to this vertex. This will include the Tjs in the parent.
2692  auto vx3TjList = GetVtxTjIDs(slc, vx3, score);
2693  if(vx3TjList.empty()) continue;
2694  // filter out the parent Tjs
2695  auto dtrTjlist = SetDifference(vx3TjList, parPFP.TjIDs);
2696  if(prt) {
2697  mf::LogVerbatim myprt("TC");
2698  myprt<<" Dtrs:";
2699  for(auto dtjID : dtrTjlist) myprt<<" "<<dtjID<<"_"<<GetPFPIndex(slc, dtjID);
2700  }
2701  // Add to the stack
2702  for(auto dtjID : dtrTjlist) {
2703  unsigned short pfpIndex = GetPFPIndex(slc, dtjID);
2704  if(pfpIndex > slc.pfps.size() - 1) continue;
2705  unsigned short dtrID = pfpIndex + 1;
2706  // See if this is a duplicate
2707  bool duplicate = false;
2708  for(auto& pd : pardtr) if(parPFP.ID == pd.first && dtrID == pd.second) duplicate = true;
2709  if(!duplicate) pardtr.push_back(std::make_pair(parPFP.ID, dtrID));
2710  } // dtjID
2711  } // parPFP
2712 
2713  // iterate through the parent - daughter stack, removing the last pair when a
2714  // ParentID is updated and adding pairs for new daughters
2715  for(unsigned short nit = 0; nit < 100; ++nit) {
2716  if(pardtr.empty()) break;
2717  auto lastPair = pardtr[pardtr.size() - 1];
2718  auto& dtr = slc.pfps[lastPair.second - 1];
2719  auto& par = slc.pfps[lastPair.first - 1];
2720  dtr.ParentUID = par.UID;
2721  par.DtrUIDs.push_back(dtr.UID);
2722  // remove the last pair
2723  pardtr.pop_back();
2724  // Now see if the daughter is a parent. First check for a vertex at the other end.
2725  // To do that we need to know which end has the vertex between the parent and daughter
2726  unsigned short dtrEnd = USHRT_MAX;
2727  for(unsigned short ep = 0; ep < 2; ++ep) {
2728  if(par.Vx3ID[ep] == 0) continue;
2729  for(unsigned short ed = 0; ed < 2; ++ed) if(dtr.Vx3ID[ed] == par.Vx3ID[ep]) dtrEnd = ed;
2730  } // ep
2731  if(dtrEnd > 1) continue;
2732  // look at the other end of the daughter
2733  dtrEnd = 1 - dtrEnd;
2734  // check for a vertex
2735  if(dtr.Vx3ID[dtrEnd] == 0) continue;
2736  // get the list of Tjs attached to it
2737  auto& vx3 = slc.vtx3s[dtr.Vx3ID[dtrEnd] - 1];
2738  float score = 0;
2739  auto vx3TjList = GetVtxTjIDs(slc, vx3, score);
2740  if(vx3TjList.empty()) continue;
2741  // filter out the new parent
2742  auto dtrTjlist = SetDifference(vx3TjList, dtr.TjIDs);
2743  // put these onto the stack
2744  for(auto tjid : dtrTjlist) pardtr.push_back(std::make_pair(dtr.ID, tjid));
2745  } // nit
2746  } // DefinePFPParentsTestBeam
2747 
2749  bool StorePFP(TCSlice& slc, PFPStruct& pfp)
2750  {
2751  // stores the PFParticle in TJStuff
2752  if(pfp.ID < int(slc.pfps.size())) return false;
2753  bool neutrinoPFP = pfp.PDGCode == 12 || pfp.PDGCode == 14;
2754  if(!neutrinoPFP) {
2755  if(pfp.TjIDs.empty()) return false;
2756  if(pfp.PDGCode != 1111 && pfp.Tp3s.size() < 2) return false;
2757  }
2758  // check the ID and correct it if it is wrong
2759  if(pfp.ID != (int)slc.pfps.size() + 1) pfp.ID = slc.pfps.size() + 1;
2760  ++evt.globalPFPID;
2761  pfp.UID = evt.globalPFPID;
2762  // check the Tjs and set the 3D match flag
2763  for(auto tjid : pfp.TjIDs) {
2764  auto& tj = slc.tjs[tjid - 1];
2765  if(tj.AlgMod[kMat3D]) return false;
2766  tj.AlgMod[kMat3D] = true;
2767  } // tjid
2768 
2769  if(!pfp.Tp3s.empty()) {
2770  // ensure that the Tj points are in increasing order and reverse them if they aren't. This
2771  // presumes that the space points have been ordered from pfp start to pfp end
2772  std::vector<int> tjids;
2773  // list of tj points to check for increasing (or decreasing) order
2774  std::vector<short> firstIpt;
2775  std::vector<short> lastIpt;
2776  for(auto& Tp3 : pfp.Tp3s) {
2777  for(auto& tj2pt : Tp3.Tj2Pts) {
2778  int tjid = tj2pt.id;
2779  // check for the first occurrence
2780  unsigned short ii = 0;
2781  for(ii = 0; ii < tjids.size(); ++ii) if(tjid == tjids[ii]) break;
2782  if(ii < tjids.size()) {
2783  // exists in the list. Keep track of the last point
2784  lastIpt[ii] = tj2pt.ipt;
2785  continue;
2786  }
2787  tjids.push_back(tjid);
2788  firstIpt.push_back((short)tj2pt.ipt);
2789  lastIpt.push_back((short)tj2pt.ipt);
2790  } // tjpt
2791  } // spt
2792  // reverse Tjs if necessary so that end0 is at the start of the pfp
2793  for(unsigned short ii = 0; ii < tjids.size(); ++ii) {
2794  // ignore Tjs that aren't associated with this pfp
2795  if(std::find(pfp.TjIDs.begin(), pfp.TjIDs.end(), tjids[ii]) == pfp.TjIDs.end()) continue;
2796  auto& tj = slc.tjs[tjids[ii] - 1];
2797  if(lastIpt[ii] < firstIpt[ii]) ReverseTraj(slc, tj);
2798  } // ii
2799  } // Tp3s exist
2800 
2801  if(pfp.BestPlane < 0) FilldEdx(slc, pfp);
2802 
2803  if(pfp.NeedsUpdate) std::cout<<"StorePFP: stored P"<<pfp.ID<<" but NeedsUpdate is true...\n";
2804 
2805  slc.pfps.push_back(pfp);
2806  return true;
2807  } // StorePFP
2808 
2810  bool InsideFV(TCSlice& slc, PFPStruct& pfp, unsigned short end)
2811  {
2812  // returns true if the end of the pfp is inside the fiducial volume of the TPC
2813  if(pfp.ID <= 0) return false;
2814  if(end > 1) return false;
2815 
2816  float abit = 5;
2817  auto& pos1 = pfp.XYZ[end];
2818  return (pos1[0] > slc.xLo + abit && pos1[0] < slc.xHi - abit &&
2819  pos1[1] > slc.yLo + abit && pos1[1] < slc.yHi - abit &&
2820  pos1[2] > slc.zLo + abit && pos1[2] < slc.zHi - abit);
2821 
2822  } // InsideFV
2823 
2825  bool InsideTPC(const Point3_t& pos, geo::TPCID& inTPCID)
2826  {
2827  // determine which TPC this point is in. This function returns false
2828  // if the point is not inside any TPC
2829  float abit = 5;
2830  for (const geo::TPCID& tpcid: tcc.geom->IterateTPCIDs()) {
2831  const geo::TPCGeo& TPC = tcc.geom->TPC(tpcid);
2832  double local[3] = {0.,0.,0.};
2833  double world[3] = {0.,0.,0.};
2834  TPC.LocalToWorld(local,world);
2835  // reduce the active area of the TPC by a bit to be consistent with FillWireHitRange
2836  if(pos[0] < world[0]-tcc.geom->DetHalfWidth(tpcid) + abit) continue;
2837  if(pos[0] > world[0]+tcc.geom->DetHalfWidth(tpcid) - abit) continue;
2838  if(pos[1] < world[1]-tcc.geom->DetHalfHeight(tpcid) + abit) continue;
2839  if(pos[1] > world[1]+tcc.geom->DetHalfHeight(tpcid) - abit) continue;
2840  if(pos[2] < world[2]-tcc.geom->DetLength(tpcid)/2 + abit) continue;
2841  if(pos[2] > world[2]+tcc.geom->DetLength(tpcid)/2 - abit) continue;
2842  inTPCID = tpcid;
2843  return true;
2844  } // tpcid
2845  return false;
2846  } // InsideTPC
2847 
2849  void FindAlongTrans(Point3_t pos1, Vector3_t dir1, Point3_t pos2, Point2_t& alongTrans)
2850  {
2851  // Calculate the distance along and transvers to the direction vector from pos1 to pos2
2852  alongTrans[0] = 0;
2853  alongTrans[1] = 0;
2854  if(pos1[0] == pos2[0] && pos1[1] == pos2[1] && pos1[2] == pos2[2]) return;
2855  auto ptDir = PointDirection(pos1, pos2);
2856  SetMag(dir1, 1.0);
2857  double costh = DotProd(dir1, ptDir);
2858  if(costh > 1) return;
2859  double sep = PosSep(pos1, pos2);
2860  if(sep < 1E-6) return;
2861  alongTrans[0] = costh * sep;
2862  double sinth = sqrt(1 - costh * costh);
2863  alongTrans[1] = sinth * sep;
2864  } // FindAlongTrans
2865 
2867  bool PointDirIntersect(Point3_t p1, Vector3_t p1Dir, Point3_t p2, Vector3_t p2Dir, Point3_t& intersect, float& doca)
2868  {
2869  // Point - vector version
2870  Point3_t p1End, p2End;
2871  for(unsigned short xyz = 0; xyz < 3; ++xyz) {
2872  p1End[xyz] = p1[xyz] + 10 * p1Dir[xyz];
2873  p2End[xyz] = p2[xyz] + 10 * p2Dir[xyz];
2874  }
2875  return LineLineIntersect(p1, p1End, p2, p2End, intersect, doca);
2876  } // PointDirIntersect
2877 
2879  bool LineLineIntersect(Point3_t p1, Point3_t p2, Point3_t p3, Point3_t p4, Point3_t& intersect, float& doca)
2880  {
2881  /*
2882  Calculate the line segment PaPb that is the shortest route between
2883  two lines P1P2 and P3P4. Calculate also the values of mua and mub where
2884  Pa = P1 + mua (P2 - P1)
2885  Pb = P3 + mub (P4 - P3)
2886  Return FALSE if no solution exists.
2887  http://paulbourke.net/geometry/pointlineplane/
2888  */
2889 
2890  Point3_t p13, p43, p21;
2891  double d1343,d4321,d1321,d4343,d2121;
2892  double numer,denom;
2893  constexpr double EPS = std::numeric_limits<double>::min();
2894 
2895  p13[0] = p1[0] - p3[0];
2896  p13[1] = p1[1] - p3[1];
2897  p13[2] = p1[2] - p3[2];
2898  p43[0] = p4[0] - p3[0];
2899  p43[1] = p4[1] - p3[1];
2900  p43[2] = p4[2] - p3[2];
2901  if (std::abs(p43[0]) < EPS && std::abs(p43[1]) < EPS && std::abs(p43[2]) < EPS) return(false);
2902  p21[0] = p2[0] - p1[0];
2903  p21[1] = p2[1] - p1[1];
2904  p21[2] = p2[2] - p1[2];
2905  if (std::abs(p21[0]) < EPS && std::abs(p21[1]) < EPS && std::abs(p21[2]) < EPS) return(false);
2906 
2907  d1343 = p13[0] * p43[0] + p13[1] * p43[1] + p13[2] * p43[2];
2908  d4321 = p43[0] * p21[0] + p43[1] * p21[1] + p43[2] * p21[2];
2909  d1321 = p13[0] * p21[0] + p13[1] * p21[1] + p13[2] * p21[2];
2910  d4343 = p43[0] * p43[0] + p43[1] * p43[1] + p43[2] * p43[2];
2911  d2121 = p21[0] * p21[0] + p21[1] * p21[1] + p21[2] * p21[2];
2912 
2913  denom = d2121 * d4343 - d4321 * d4321;
2914  if (std::abs(denom) < EPS) return(false);
2915  numer = d1343 * d4321 - d1321 * d4343;
2916 
2917  double mua = numer / denom;
2918  double mub = (d1343 + d4321 * mua) / d4343;
2919 
2920  intersect[0] = p1[0] + mua * p21[0];
2921  intersect[1] = p1[1] + mua * p21[1];
2922  intersect[2] = p1[2] + mua * p21[2];
2923  Point3_t pb;
2924  pb[0] = p3[0] + mub * p43[0];
2925  pb[1] = p3[1] + mub * p43[1];
2926  pb[2] = p3[2] + mub * p43[2];
2927  doca = PosSep(intersect, pb);
2928  // average the closest points
2929  for(unsigned short xyz = 0; xyz < 3; ++xyz) intersect[xyz] += pb[xyz];
2930  for(unsigned short xyz = 0; xyz < 3; ++xyz) intersect[xyz] /= 2;
2931  return true;
2932  } // LineLineIntersect
2933 
2935  void ReversePFP(TCSlice& slc, PFPStruct& pfp)
2936  {
2937  std::swap(pfp.XYZ[0], pfp.XYZ[1]);
2938  std::swap(pfp.Dir[0], pfp.Dir[1]);
2939  for(unsigned short xyz = 0; xyz < 3; ++xyz) {
2940  pfp.Dir[0][xyz] *= -1;
2941  pfp.Dir[1][xyz] *= -1;
2942  }
2943  std::swap(pfp.DirErr[0], pfp.DirErr[1]);
2944  std::swap(pfp.dEdx[0], pfp.dEdx[1]);
2945  std::swap(pfp.dEdxErr[0], pfp.dEdxErr[1]);
2946  std::swap(pfp.Vx3ID[0], pfp.Vx3ID[1]);
2947  std::reverse(pfp.Tp3s.begin(), pfp.Tp3s.end());
2948  for(auto& tp3 : pfp.Tp3s) {
2949  for(unsigned short xyz = 0; xyz < 3; ++xyz) {
2950  tp3.Dir[xyz] *= -1;
2951  tp3.Dir[xyz] *= -1;
2952  } // xyz
2953  } // tp3
2954  } // ReversePFP
2955 
2957  float ChgFracBetween(TCSlice& slc, Point3_t pos1, Point3_t pos2)
2958  {
2959  // Step between pos1 and pos2 and find the fraction of the points that have nearby hits
2960  // in each plane. This function returns -1 if something is fishy, but this doesn't mean
2961  // that there is no charge. Note that there is no check for charge precisely at the pos1 and pos2
2962  // positions
2963  float sep = PosSep(pos1, pos2);
2964  if(sep == 0) return -1;
2965  unsigned short nstep = sep / tcc.wirePitch;
2966  auto dir = PointDirection(pos1, pos2);
2967  float sum = 0;
2968  float cnt = 0;
2969  TrajPoint tp;
2970  for(unsigned short step = 0; step < nstep; ++step) {
2971  for(unsigned short xyz = 0; xyz < 3; ++xyz) pos1[xyz] += tcc.wirePitch * dir[xyz];
2972  for(unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
2973  tp.CTP = EncodeCTP(slc.TPCID.Cryostat, slc.TPCID.TPC, plane);
2974  tp.Pos[0] = tcc.geom->WireCoordinate(pos1[1], pos1[2], plane, slc.TPCID.TPC, slc.TPCID.Cryostat);
2975  tp.Pos[1] = tcc.detprop->ConvertXToTicks(pos1[0], plane, slc.TPCID.TPC, slc.TPCID.Cryostat) * tcc.unitsPerTick;
2976  ++cnt;
2977  if(SignalAtTp(slc, tp)) ++sum;
2978  } // plane
2979  } // step
2980  if(cnt == 0) return -1;
2981  return sum / cnt;
2982 
2983  } // ChgFracBetween
2984 
2986  float ChgFracNearEnd(TCSlice& slc, PFPStruct& pfp, unsigned short end)
2987  {
2988  // returns the charge fraction near the end of the pfp. Note that this function
2989  // assumes that there is only one Tj in a plane.
2990  if(pfp.ID == 0) return 0;
2991  if(pfp.TjIDs.empty()) return 0;
2992  if(end < 0 || end > 1) return 0;
2993  if(pfp.TPCID != slc.TPCID) return 0;
2994 
2995  float sum = 0;
2996  float cnt = 0;
2997  // keep track of the lowest value and maybe reject it
2998  float lo = 1;
2999  float hi = 0;
3000  for(unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
3001  CTP_t inCTP = EncodeCTP(pfp.TPCID.Cryostat, pfp.TPCID.TPC, plane);
3002  std::vector<int> tjids(1);
3003  for(auto tjid : pfp.TjIDs) {
3004  auto& tj = slc.tjs[tjid - 1];
3005  if(tj.CTP != inCTP) continue;
3006  tjids[0] = tjid;
3007  Point2_t pos;
3008  geo::PlaneID planeID = geo::PlaneID(pfp.TPCID.Cryostat, pfp.TPCID.TPC, plane);
3009  pos[0] = tcc.geom->WireCoordinate(pfp.XYZ[end][1], pfp.XYZ[end][2], planeID);
3010  if(pos[0] < -0.4) continue;
3011  // check for dead wires
3012  unsigned int wire = std::nearbyint(pos[0]);
3013  if(wire > slc.nWires[plane]) continue;
3014  if(slc.wireHitRange[plane][wire].first == -1) continue;
3015  pos[1] = tcc.detprop->ConvertXToTicks(pfp.XYZ[end][0], planeID) * tcc.unitsPerTick;
3016  float cf = ChgFracNearPos(slc, pos, tjids);
3017  if(cf < lo) lo = cf;
3018  if(cf > hi) hi = cf;
3019  sum += cf;
3020  ++cnt;
3021  } // tjid
3022  } // plane
3023  if(cnt == 0) return 0;
3024  if(cnt > 1 && lo < 0.3 && hi > 0.8) {
3025  sum -= lo;
3026  --cnt;
3027  }
3028  return sum / cnt;
3029  } // ChgFracNearEnd
3030 
3032  unsigned short FarEnd(TCSlice& slc, const PFPStruct& pfp, const Point3_t& pos)
3033  {
3034  // Returns the end (0 or 1) of the pfp that is furthest away from the position pos
3035  if(pfp.ID == 0) return 0;
3036  if(PosSep2(pfp.XYZ[1], pos) > PosSep2(pfp.XYZ[0], pos)) return 1;
3037  return 0;
3038  } // FarEnd
3039 
3041  void PrintTp3(std::string someText, TCSlice& slc, const TrajPoint3& tp3)
3042  {
3043  mf::LogVerbatim myprt("TC");
3044  myprt<<someText<<" Pos"<<std::fixed<<std::setprecision(1);
3045  myprt<<std::setw(6)<<tp3.Pos[0]<<std::setw(6)<<tp3.Pos[1]<<std::setw(6)<<tp3.Pos[2];
3046  myprt<<" Dir"<<std::fixed<<std::setprecision(3);
3047  myprt<<std::setw(7)<<tp3.Dir[0]<<std::setw(7)<<tp3.Dir[1]<<std::setw(7)<<tp3.Dir[2];
3048  myprt<<" tj_ipt";
3049  for(auto tj2pt : tp3.Tj2Pts) {
3050  auto& tj = slc.tjs[tj2pt.id - 1];
3051  auto& tp = tj.Pts[tj2pt.ipt];
3052  myprt<<" "<<tj.ID<<"_"<<PrintPos(slc, tp);
3053  } // tj2pt
3054  } // PrintTp3
3055 
3057  void PrintTp3s(std::string someText, TCSlice& slc, const PFPStruct& pfp, short printPts)
3058  {
3059  if(pfp.Tp3s.empty()) return;
3060  mf::LogVerbatim myprt("TC");
3061  if(printPts < 0) {
3062  // print the head if we are print all points
3063  myprt<<someText<<" pfp P"<<pfp.ID<<"\n";
3064  myprt<<someText<<" ipt ________Pos________ Path ________Dir_______ along trans dang Kink Tj_ipt \n";
3065  }
3066  // print the start
3067  myprt<<someText<<" ";
3068  myprt<<std::fixed<<std::setprecision(1);
3069  myprt<<std::setw(7)<<pfp.XYZ[0][0]<<std::setw(7)<<pfp.XYZ[0][1]<<std::setw(7)<<pfp.XYZ[0][2];
3070  myprt<<" ";
3071  myprt<<std::fixed<<std::setprecision(2);
3072  myprt<<std::setw(7)<<pfp.Dir[0][0]<<std::setw(7)<<pfp.Dir[0][1]<<std::setw(7)<<pfp.Dir[0][2];
3073  myprt<<" <--- pfp.XYZ[0] \n";
3074 
3075  unsigned short fromPt = 0;
3076  unsigned short toPt = pfp.Tp3s.size() - 1;
3077  if(printPts >= 0) fromPt = toPt;
3078  Vector3_t prevDir = pfp.Dir[0];
3079  for(unsigned short ipt = fromPt; ipt <= toPt; ++ipt) {
3080  auto tp3 = pfp.Tp3s[ipt];
3081  myprt<<someText<<std::setw(4)<<ipt;
3082  myprt<<std::fixed<<std::setprecision(1);
3083  myprt<<std::setw(7)<<tp3.Pos[0]<<std::setw(7)<<tp3.Pos[1]<<std::setw(7)<<tp3.Pos[2];
3084  myprt<<std::setprecision(1)<<std::setw(5)<<PosSep(tp3.Pos, pfp.XYZ[0]);
3085  myprt<<std::setprecision(2)<<std::setw(7)<<tp3.Dir[0]<<std::setw(7)<<tp3.Dir[1]<<std::setw(7)<<tp3.Dir[2];
3086  myprt<<std::setprecision(1)<<std::setw(6)<<tp3.AlongTrans[0];
3087  myprt<<std::setprecision(1)<<std::setw(6)<<tp3.AlongTrans[1];
3088  myprt<<std::setprecision(2)<<std::setw(7)<<DeltaAngle(prevDir, tp3.Dir);
3089  prevDir = tp3.Dir;
3090  // Calculate the kink angle at point ipt, using the two points that are
3091  // +/- 1 cm on either side of that point
3092  double sep = 1;
3093  myprt<<std::setprecision(2)<<std::setw(7)<<KinkAngle(slc, pfp.Tp3s, ipt, sep);
3094  for(auto tj2pt : tp3.Tj2Pts) {
3095  auto& tj = slc.tjs[tj2pt.id - 1];
3096  auto& tp = tj.Pts[tj2pt.ipt];
3097  myprt<<" "<<tj.ID<<"_"<<tj2pt.ipt<<"_"<<PrintPos(slc, tp);
3098  } // tj2pt
3099  myprt<<"\n";
3100  } // ipt
3101  // print the end
3102  myprt<<someText<<" ";
3103  myprt<<std::fixed<<std::setprecision(1);
3104  myprt<<std::setw(7)<<pfp.XYZ[1][0]<<std::setw(7)<<pfp.XYZ[1][1]<<std::setw(7)<<pfp.XYZ[1][2];
3105  myprt<<" ";
3106  myprt<<std::fixed<<std::setprecision(2);
3107  myprt<<std::setw(7)<<pfp.Dir[1][0]<<std::setw(7)<<pfp.Dir[1][1]<<std::setw(7)<<pfp.Dir[1][2];
3108  myprt<<" <--- pfp.XYZ[1]. Length "<<PosSep(pfp.XYZ[0], pfp.XYZ[1])<<"\n";
3109  } // PrintTp3s
3110 
3111 } // namespace
Expect tracks entering from the front face. Don&#39;t create neutrino PFParticles.
Definition: DataStructs.h:448
TPCID()=default
Default constructor: an invalid TPC ID.
Float_t x
Definition: compare.C:6
calo::CalorimetryAlg * caloAlg
Definition: DataStructs.h:495
Point2_t AlongTrans
Definition: DataStructs.h:192
geo::Length_t WireCoordinate(double YPos, double ZPos, geo::PlaneID const &planeid) const
Returns the index of the nearest wire to the specified position.
code to link reconstructed objects back to the MC truth information
void Fit3D(unsigned short mode, Point3_t point, Vector3_t dir, Point3_t &fitPos, Vector3_t &fitDir)
Definition: PFPUtils.cxx:1042
Vector2_t Dir
Definition: DataStructs.h:122
Point2_t Pos
Definition: DataStructs.h:121
MaybeLogger_< ELseverityLevel::ELsev_info, true > LogVerbatim
std::vector< Trajectory > tjs
vector of all trajectories in each plane
Definition: DataStructs.h:570
std::vector< Vtx3Store > vtx3s
3D vertices
Definition: DataStructs.h:577
Point2_t dEdx
dE/dx for 3D matched trajectories
Definition: DataStructs.h:154
bool dbgStitch
debug PFParticle stitching
Definition: DataStructs.h:520
int PDGCodeVote(TCSlice &slc, std::vector< int > &tjIDs, bool prt)
Definition: Utils.cxx:372
void SetEndPoints(Trajectory &tj)
Definition: Utils.cxx:2864
std::array< std::vector< float >, 2 > dEdxErr
Definition: DataStructs.h:216
bool MakeVertexObsolete(std::string fcnLabel, TCSlice &slc, VtxStore &vx2, bool forceKill)
Definition: TCVertex.cxx:3120
PlaneGeo const & Plane(unsigned int const p, unsigned int const tpc=0, unsigned int const cstat=0) const
Returns the specified wire.
geo::Length_t DetHalfWidth(geo::TPCID const &tpcid) const
Returns the half width of the active volume of the specified TPC.
void PrintTp3(std::string someText, TCSlice &slc, const TrajPoint3 &tp3)
Definition: PFPUtils.cxx:3041
struct of temporary 2D vertices (end points)
Definition: DataStructs.h:54
std::vector< int > GetAssns(TCSlice &slc, std::string type1Name, int id, std::string type2Name)
Definition: Utils.cxx:4279
void FindCompleteness(TCSlice &slc, PFPStruct &pfp, bool doFit, bool fillTp3s, bool prt)
Definition: PFPUtils.cxx:737
CTP_t CTP
Cryostat, TPC, Plane code.
Definition: DataStructs.h:119
Float_t E
Definition: plot.C:23
std::vector< float > maxPos0
Definition: DataStructs.h:489
bool valIncreasings(SortEntry c1, SortEntry c2)
Definition: PFPUtils.cxx:13
bool InsideTPC(const Point3_t &pos, geo::TPCID &inTPCID)
Definition: PFPUtils.cxx:2825
bool FitTp3(TCSlice &slc, TrajPoint3 &tp3, const std::vector< Tj2Pt > &tj2pts)
Definition: PFPUtils.cxx:646
std::array< double, 3 > Point3_t
Definition: DataStructs.h:35
double dEdx_AREA(art::Ptr< recob::Hit > hit, double pitch, double T0=0) const
short MCSMom(TCSlice &slc, const std::vector< int > &tjIDs)
Definition: Utils.cxx:2914
TCConfig tcc
Definition: DataStructs.cxx:6
unsigned int Nplanes() const
Number of planes in this tpc.
Definition: TPCGeo.h:145
void PrintPFP(std::string someText, TCSlice &slc, const PFPStruct &pfp, bool printHeader)
Definition: Utils.cxx:5587
Float_t den
Definition: plot.C:37
std::vector< int > TjIDs
Definition: DataStructs.h:198
void FindAlongTrans(Point3_t pos1, Vector3_t dir1, Point3_t pos2, Point2_t &alongTrans)
Definition: PFPUtils.cxx:2849
float PFPDOCA(const PFPStruct &pfp1, const PFPStruct &pfp2, unsigned short &close1, unsigned short &close2)
Definition: PFPUtils.cxx:1790
unsigned short WiresSkippedInCTP(TCSlice &slc, std::vector< int > &tjids, CTP_t inCTP)
Definition: PFPUtils.cxx:1089
bool MakeTp3(TCSlice &slc, const TrajPoint &itp, const TrajPoint &jtp, TrajPoint3 &tp3, bool findDirection)
Definition: PFPUtils.cxx:1555
std::vector< float > TjCompleteness
Definition: DataStructs.h:209
std::vector< int > Vx2ID
Definition: DataStructs.h:94
The data type to uniquely identify a Plane.
Definition: geo_types.h:250
Geometry information for a single TPC.
Definition: TPCGeo.h:37
matched to a high-score 3D vertex
Definition: DataStructs.h:76
bool StoreTraj(TCSlice &slc, Trajectory &tj)
Definition: Utils.cxx:1175
bool LineLineIntersect(Point3_t p1, Point3_t p2, Point3_t p3, Point3_t p4, Point3_t &intersect, float &doca)
Definition: PFPUtils.cxx:2879
void FindXMatches(TCSlice &slc, unsigned short numPlanes, short maxScore, std::vector< MatchStruct > &matVec, bool prt)
Definition: PFPUtils.cxx:1355
bool MergePFPTjs(TCSlice &slc, PFPStruct &pfp, bool prt)
Definition: PFPUtils.cxx:1203
CryostatID_t Cryostat
Index of cryostat.
Definition: geo_types.h:130
bool AttachAnyTrajToVertex(TCSlice &slc, unsigned short ivx, bool prt)
Definition: TCVertex.cxx:1868
void PrintP(std::string someText, mf::LogVerbatim &myprt, PFPStruct &pfp, bool &printHeader)
Definition: Utils.cxx:4876
Float_t tmp
Definition: plot.C:37
std::vector< Tj2Pt > Tj2Pts
Definition: DataStructs.h:190
float LengthInCTP(TCSlice &slc, std::vector< int > &tjids, CTP_t inCTP)
Definition: PFPUtils.cxx:1135
double DeltaAngle(const Vector3_t v1, const Vector3_t v2)
Definition: PFPUtils.cxx:1636
a general purpose flag bit used in 3D matching
Definition: DataStructs.h:442
std::vector< unsigned short > FindKinks(TCSlice &slc, PFPStruct &pfp, double sep, bool prt)
Definition: PFPUtils.cxx:1901
bool dbgSlc
debug only in the user-defined slice? default is all slices
Definition: DataStructs.h:507
std::array< int, 2 > Vx3ID
Definition: DataStructs.h:217
bool IsShowerLike(TCSlice &slc, const std::vector< int > TjIDs)
Definition: TCShower.cxx:2002
geo::Length_t WirePitch(geo::PlaneID const &planeid) const
Returns the distance between two consecutive wires.
bool StoreVertex(TCSlice &slc, VtxStore &vx)
Definition: TCVertex.cxx:2093
std::vector< TrajPoint3 > Tp3s
Definition: DataStructs.h:210
IteratorBox< TPC_id_iterator,&GeometryCore::begin_TPC_id,&GeometryCore::end_TPC_id > IterateTPCIDs() const
Enables ranged-for loops on all TPC IDs of the detector.
bool Split3DKink(TCSlice &slc, PFPStruct &pfp, double sep, bool prt)
Definition: PFPUtils.cxx:1811
std::vector< T > SetIntersection(const std::vector< T > &set1, const std::vector< T > &set2)
Definition: Utils.h:231
virtual double ConvertXToTicks(double X, int p, int t, int c) const =0
void PrintTp3s(std::string someText, TCSlice &slc, const PFPStruct &pfp, short printPts)
Definition: PFPUtils.cxx:3057
void UpdateTp3s(TCSlice &slc, PFPStruct &pfp, int oldTj, int newTj)
Definition: PFPUtils.cxx:229
double ThetaZ() const
Angle of the wires from positive z axis; .
Definition: PlaneGeo.cxx:728
TrajPoint MakeBareTP(TCSlice &slc, Point3_t &pos, Vector3_t &dir, CTP_t inCTP)
Definition: Utils.cxx:3523
std::vector< std::vector< std::pair< int, int > > > wireHitRange
Definition: DataStructs.h:575
float MaxTjLen(TCSlice &slc, std::vector< int > &tjIDs)
Definition: Utils.cxx:2322
struct of temporary 3D vertices
Definition: DataStructs.h:84
geo::TPCID TPCID
Definition: DataStructs.h:223
void PFPVertexCheck(TCSlice &slc)
Definition: PFPUtils.cxx:2502
geo::Length_t DetHalfHeight(geo::TPCID const &tpcid) const
Returns the half height of the active volume of the specified TPC.
TCanvas * c1
Definition: plotHisto.C:7
std::vector< Tj2Pt > mallTraj
vector of trajectory points ordered by increasing X
Definition: DataStructs.h:571
TCanvas * c2
Definition: plot_hist.C:75
std::array< float, 2 > Point2_t
Definition: DataStructs.h:37
float unitsPerTick
scale factor from Tick to WSE equivalent units
Definition: DataStructs.h:488
std::array< Point3_t, 2 > XYZ
Definition: DataStructs.h:212
const detinfo::DetectorProperties * detprop
Definition: DataStructs.h:494
geo::TPCID TPCID
Definition: DataStructs.h:93
DebugStuff debug
Definition: DebugStruct.cxx:4
Vector3_t PointDirection(const Point3_t p1, const Point3_t p2)
Definition: PFPUtils.cxx:1643
CTP_t CTP
Cryostat, TPC, Plane code.
Definition: DataStructs.h:144
int UID
unique global ID
Definition: DataStructs.h:96
std::array< Vector3_t, 2 > DirErr
Definition: DataStructs.h:214
std::vector< TrajPoint > Pts
Trajectory points.
Definition: DataStructs.h:143
unsigned short GetPFPIndex(TCSlice &slc, int tjID)
Definition: Utils.cxx:1127
std::array< Vector3_t, 2 > Dir
Definition: DataStructs.h:213
bool DefinePFP(std::string inFcnLabel, TCSlice &slc, PFPStruct &pfp, bool prt)
Definition: PFPUtils.cxx:2233
bool valDecreasings(SortEntry c1, SortEntry c2)
Definition: PFPUtils.cxx:12
std::array< unsigned short, 2 > EndPt
First and last point in the trajectory that has charge.
Definition: DataStructs.h:156
bool SignalAtTp(TCSlice &slc, const TrajPoint &tp)
Definition: Utils.cxx:1814
std::vector< float > match3DCuts
3D matching cuts
Definition: DataStructs.h:476
void UpdateMatchStructs(TCSlice &slc, int oldTj, int newTj)
Definition: PFPUtils.cxx:160
geo::Length_t DetLength(geo::TPCID const &tpcid) const
Returns the length of the active volume of the specified TPC.
void MakeTrajectoryObsolete(TCSlice &slc, unsigned int itj)
Definition: Utils.cxx:1922
void fill(const art::PtrVector< recob::Hit > &hits, int only_plane)
double PosSep2(const Point3_t &pos1, const Point3_t &pos2)
Definition: PFPUtils.cxx:1660
unsigned int index
Definition: PFPUtils.cxx:8
void SetPDGCode(TCSlice &slc, unsigned short itj, bool tjDone)
Definition: Utils.cxx:3795
Point2_t Pos
Definition: DataStructs.h:55
Float_t mat
Definition: plot.C:40
void FollowTp3s(TCSlice &slc, PFPStruct &pfp, bool prt)
Definition: PFPUtils.cxx:405
const geo::GeometryCore * geom
Definition: DataStructs.h:493
bool PointDirIntersect(Point3_t p1, Vector3_t p1Dir, Point3_t p2, Vector3_t p2Dir, Point3_t &intersect, float &doca)
Definition: PFPUtils.cxx:2867
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
void FindMissedTjsInTp3s(TCSlice &slc, PFPStruct &pfp, std::vector< int > &missTjs, std::vector< float > &missFrac)
Definition: PFPUtils.cxx:950
float EffPur
Efficiency * Purity.
Definition: DataStructs.h:224
unsigned short FarEnd(TCSlice &slc, const PFPStruct &pfp, const Point3_t &pos)
Definition: PFPUtils.cxx:3032
bool SetMag(Vector3_t &v1, double mag)
Definition: PFPUtils.cxx:1670
void ReverseTraj(TCSlice &slc, Trajectory &tj)
Definition: Utils.cxx:2751
float TPHitsRMSTime(TCSlice &slc, TrajPoint &tp, HitStatus_t hitRequest)
Definition: Utils.cxx:3668
bool AnalyzePFP(TCSlice &slc, PFPStruct &pfp, bool prt)
Definition: PFPUtils.cxx:2421
int ID
ID that is local to one slice.
Definition: DataStructs.h:157
std::vector< TCSlice > slices
Definition: DataStructs.cxx:10
std::array< unsigned short, 2 > VtxID
ID of 2D vertex.
Definition: DataStructs.h:155
unsigned short NumPtsWithCharge(TCSlice &slc, const Trajectory &tj, bool includeDeadWires)
Definition: Utils.cxx:1863
std::bitset< 16 > modes
number of points to find AveChg
Definition: DataStructs.h:524
unsigned int NTPC(unsigned int cstat=0) const
Returns the total number of TPCs in the specified cryostat.
std::vector< TCHit > slHits
Definition: DataStructs.h:569
void FindPFParticles(TCSlice &slc)
Definition: PFPUtils.cxx:1998
int ID
set to 0 if killed
Definition: DataStructs.h:64
virtual double ConvertTicksToX(double ticks, int p, int t, int c) const =0
float ChgFracNearPos(TCSlice &slc, const Point2_t &pos, const std::vector< int > &tjIDs)
Definition: Utils.cxx:2698
Float_t sw
Definition: plot.C:23
void FilldEdx(TCSlice &slc, PFPStruct &pfp)
Definition: PFPUtils.cxx:1683
bool InsideFV(TCSlice &slc, PFPStruct &pfp, unsigned short end)
Definition: PFPUtils.cxx:2810
double DotProd(const Vector3_t &v1, const Vector3_t &v2)
Definition: PFPUtils.h:59
unsigned int CTP_t
Definition: DataStructs.h:41
geo::TPCID TPCID
Definition: DataStructs.h:562
std::bitset< 128 > useAlg
Max hit separation for making junk trajectories. < 0 to turn off.
Definition: DataStructs.h:504
Float_t norm
TDirectory * dir
Definition: macro.C:5
std::vector< float > pfpStitchCuts
cuts for stitching between TPCs
Definition: DataStructs.h:480
Int_t min
Definition: plot.C:26
PFPStruct CreatePFP(TCSlice &slc)
Definition: PFPUtils.cxx:1973
std::vector< VtxStore > vtxs
2D vertices
Definition: DataStructs.h:576
short StepDir
-1 = going US (-> small wire#), 1 = going DS (-> large wire#)
Definition: DataStructs.h:162
std::bitset< 128 > AlgMod
Bit set if algorithm AlgBit_t modifed the trajectory.
Definition: DataStructs.h:145
geo::PlaneID DecodeCTP(CTP_t CTP)
void ReversePFP(TCSlice &slc, PFPStruct &pfp)
Definition: PFPUtils.cxx:2935
std::vector< int > GetVtxTjIDs(const TCSlice &slc, const VtxStore &vx2)
Definition: TCVertex.cxx:3226
unsigned short MatchVecIndex
Definition: DataStructs.h:226
bool SetStart(TCSlice &slc, PFPStruct &pfp, bool prt)
Definition: PFPUtils.cxx:351
double PosSep(const Point3_t &pos1, const Point3_t &pos2)
Definition: PFPUtils.cxx:1654
std::vector< int > TjIDs
Definition: DataStructs.h:207
bool FitTp3s(TCSlice &slc, const std::vector< TrajPoint3 > &tp3s, Point3_t &pos, Vector3_t &dir, float &rCorr)
Definition: PFPUtils.cxx:556
std::vector< recob::Hit > const * allHits
Definition: DataStructs.h:536
std::string PrintPos(TCSlice &slc, const TrajPoint &tp)
Definition: Utils.cxx:5712
CTP_t EncodeCTP(unsigned int cryo, unsigned int tpc, unsigned int plane)
Definition: DataStructs.h:45
std::pair< unsigned short, unsigned short > GetSliceIndex(std::string typeName, int uID)
Definition: Utils.cxx:4496
std::vector< unsigned int > nWires
Definition: DataStructs.h:553
double KinkAngle(TCSlice &slc, const std::vector< TrajPoint3 > &tp3s, unsigned short atPt, double sep)
Definition: PFPUtils.cxx:1943
void FillmAllTraj(TCSlice &slc)
Definition: PFPUtils.cxx:271
std::array< double, 3 > Vector3_t
Definition: DataStructs.h:36
bool SharesHighScoreVx(TCSlice &slc, const PFPStruct &pfp, const Trajectory &tj)
Definition: PFPUtils.cxx:1026
std::array< std::vector< float >, 2 > dEdx
Definition: DataStructs.h:215
bool PFPVxTjOK(TCSlice &slc, PFPStruct &pfp, bool prt)
Definition: PFPUtils.cxx:2366
bool AddMissedTj(TCSlice &slc, PFPStruct &pfp, unsigned short itj, bool looseCuts, bool prt)
Definition: PFPUtils.cxx:1159
Vector3_t Dir
Definition: DataStructs.h:189
std::vector< T > SetDifference(const std::vector< T > &set1, const std::vector< T > &set2)
Definition: Utils.h:253
void DefinePFPParents(TCSlice &slc, bool prt)
Definition: PFPUtils.cxx:2530
unsigned short nPlanes
Definition: DataStructs.h:563
std::vector< MatchStruct > matchVec
3D matching vector
Definition: DataStructs.h:578
std::vector< evd::details::RawDigitInfo_t >::const_iterator end(RawDigitCacheDataClass const &cache)
TPCGeo const & TPC(unsigned int const tpc=0, unsigned int const cstat=0) const
Returns the specified TPC.
int SSID
ID of a 2D shower struct that this tj is in.
Definition: DataStructs.h:159
std::vector< PFPStruct > pfps
Definition: DataStructs.h:579
TCEvent evt
Definition: DataStructs.cxx:5
float ChgFracBetween(TCSlice &slc, Point3_t pos1, Point3_t pos2)
Definition: PFPUtils.cxx:2957
bool SplitTraj(TCSlice &slc, unsigned short itj, float XPos, bool makeVx2, bool prt)
Definition: Utils.cxx:2004
float ChgFracNearEnd(TCSlice &slc, PFPStruct &pfp, unsigned short end)
Definition: PFPUtils.cxx:2986
TPCID_t TPC
Index of the TPC within its cryostat.
Definition: geo_types.h:203
bool StorePFP(TCSlice &slc, PFPStruct &pfp)
Definition: PFPUtils.cxx:2749
size_t ParentUID
Definition: DataStructs.h:222
void StitchPFPs()
Definition: PFPUtils.cxx:16
Float_t w
Definition: plot.C:23
master switch for turning on debug mode
Definition: DataStructs.h:449
void LocalToWorld(const double *tpc, double *world) const
Transform point from local TPC frame to world frame.
Definition: TPCGeo.h:490
void DefinePFPParentsTestBeam(TCSlice &slc, bool prt)
Definition: PFPUtils.cxx:2661
bool CompatibleMerge(TCSlice &slc, std::vector< int > &tjIDs, bool prt)
Definition: Utils.cxx:534
bool HasWire(geo::WireID const &wireid) const
Returns whether we have the specified wire.
void Match3DVtxTjs(TCSlice &slc, bool prt)
Definition: TCVertex.cxx:1693