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