All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
PFPUtils.cxx
Go to the documentation of this file.
2 
3 #include "messagefacility/MessageLogger/MessageLogger.h"
4 
5 #include "TDecompSVD.h"
6 #include "TMatrixD.h"
7 #include "TVectorD.h"
8 
9 #include <algorithm>
10 #include <array>
11 #include <bitset>
12 #include <cmath>
13 #include <iomanip>
14 #include <iostream>
15 #include <limits.h>
16 #include <limits>
17 #include <stdlib.h>
18 #include <utility>
19 #include <vector>
20 
35 
36 namespace tca {
37 
38  using namespace detail; // SortEntry, valsDecreasing(), valsIncreasing();
39 
40  /////////////////////////////////////////
41  void
43  {
44  // Stitch PFParticles in different TPCs. This does serious damage to PFPStruct and should
45  // only be called from TrajCluster module just before making PFParticles to put in the event
46  if (slices.size() < 2) return;
47  if (tcc.geom->NTPC() == 1) return;
48  if (tcc.pfpStitchCuts.size() < 2) return;
49  if (tcc.pfpStitchCuts[0] <= 0) return;
50 
51  bool prt = tcc.dbgStitch;
52 
53  if (prt) {
54  mf::LogVerbatim myprt("TC");
55  std::string fcnLabel = "SP";
56  myprt << fcnLabel << " cuts " << sqrt(tcc.pfpStitchCuts[0]) << " " << tcc.pfpStitchCuts[1]
57  << "\n";
58  bool printHeader = true;
59  for (size_t isl = 0; isl < slices.size(); ++isl) {
60  if (debug.Slice >= 0 && int(isl) != debug.Slice) continue;
61  auto& slc = slices[isl];
62  if (slc.pfps.empty()) continue;
63  for (auto& pfp : slc.pfps)
64  PrintP(fcnLabel, myprt, pfp, printHeader);
65  } // slc
66  } // prt
67 
68  // lists of pfp UIDs to stitch
69  std::vector<std::vector<int>> stLists;
70  for (std::size_t sl1 = 0; sl1 < slices.size() - 1; ++sl1) {
71  auto& slc1 = slices[sl1];
72  for (std::size_t sl2 = sl1 + 1; sl2 < slices.size(); ++sl2) {
73  auto& slc2 = slices[sl2];
74  // look for PFParticles in the same recob::Slice
75  if (slc1.ID != slc2.ID) continue;
76  for (auto& p1 : slc1.pfps) {
77  if (p1.ID <= 0) continue;
78  // Can't stitch shower PFPs
79  if (p1.PDGCode == 1111) continue;
80  for (auto& p2 : slc2.pfps) {
81  if (p2.ID <= 0) continue;
82  // Can't stitch shower PFPs
83  if (p2.PDGCode == 1111) continue;
84  float maxSep2 = tcc.pfpStitchCuts[0];
85  float maxCth = tcc.pfpStitchCuts[1];
86  bool gotit = false;
87  for (unsigned short e1 = 0; e1 < 2; ++e1) {
88  auto pos1 = PosAtEnd(p1, e1);
89  // require the end to be close to a TPC boundary
90  if (InsideFV(slc1, p1, e1)) continue;
91  auto dir1 = DirAtEnd(p1, e1);
92  for (unsigned short e2 = 0; e2 < 2; ++e2) {
93  auto pos2 = PosAtEnd(p2, e2);
94  // require the end to be close to a TPC boundary
95  if (InsideFV(slc2, p2, e2)) continue;
96  auto dir2 = DirAtEnd(p2, e2);
97  float sep = PosSep2(pos1, pos2);
98  if (sep > maxSep2) continue;
99  float cth = std::abs(DotProd(dir1, dir2));
100  if (cth < maxCth) continue;
101  maxSep2 = sep;
102  maxCth = cth;
103  gotit = true;
104  } // e2
105  } // e1
106  if (!gotit) continue;
107  if (prt) {
108  mf::LogVerbatim myprt("TC");
109  myprt << "Stitch slice " << slc1.ID << " P" << p1.UID << " TPC " << p1.TPCID.TPC;
110  myprt << " and P" << p2.UID << " TPC " << p2.TPCID.TPC;
111  myprt << " sep " << sqrt(maxSep2) << " maxCth " << maxCth;
112  }
113  // see if either of these are in a list
114  bool added = false;
115  for (auto& pm : stLists) {
116  bool p1InList = (std::find(pm.begin(), pm.end(), p1.UID) != pm.end());
117  bool p2InList = (std::find(pm.begin(), pm.end(), p2.UID) != pm.end());
118  if (p1InList || p2InList) {
119  if (p1InList) pm.push_back(p2.UID);
120  if (p2InList) pm.push_back(p1.UID);
121  added = true;
122  }
123  } // pm
124  if (added) continue;
125  // start a new list
126  std::vector<int> tmp(2);
127  tmp[0] = p1.UID;
128  tmp[1] = p2.UID;
129  stLists.push_back(tmp);
130  break;
131  } // p2
132  } // p1
133  } // sl2
134  } // sl1
135  if (stLists.empty()) return;
136 
137  for (auto& stl : stLists) {
138  // Find the endpoints of the stitched pfp
139  float minZ = 1E6;
140  std::pair<unsigned short, unsigned short> minZIndx;
141  unsigned short minZEnd = 2;
142  for (auto puid : stl) {
143  auto slcIndex = GetSliceIndex("P", puid);
144  if (slcIndex.first == USHRT_MAX) continue;
145  auto& pfp = slices[slcIndex.first].pfps[slcIndex.second];
146  for (unsigned short end = 0; end < 2; ++end) {
147  auto pos = PosAtEnd(pfp, end);
148  if (pos[2] < minZ) {
149  minZ = pos[2];
150  minZIndx = slcIndex;
151  minZEnd = end;
152  }
153  } // end
154  } // puid
155  if (minZEnd > 1) continue;
156  // preserve the pfp with the min Z position
157  auto& pfp = slices[minZIndx.first].pfps[minZIndx.second];
158  if (prt) mf::LogVerbatim("TC") << "SP: P" << pfp.UID;
159  // add the Tjs in the other slices to it
160  for (auto puid : stl) {
161  if (puid == pfp.UID) continue;
162  auto sIndx = GetSliceIndex("P", puid);
163  if (sIndx.first == USHRT_MAX) continue;
164  auto& opfp = slices[sIndx.first].pfps[sIndx.second];
165  if (prt) mf::LogVerbatim("TC") << " +P" << opfp.UID;
166  pfp.TjUIDs.insert(pfp.TjUIDs.end(), opfp.TjUIDs.begin(), opfp.TjUIDs.end());
167  if (prt) mf::LogVerbatim();
168  // Check for parents and daughters
169  if (opfp.ParentUID > 0) {
170  auto pSlcIndx = GetSliceIndex("P", opfp.ParentUID);
171  if (pSlcIndx.first < slices.size()) {
172  auto& parpfp = slices[pSlcIndx.first].pfps[pSlcIndx.second];
173  std::replace(parpfp.DtrUIDs.begin(), parpfp.DtrUIDs.begin(), opfp.UID, pfp.UID);
174  } // valid pSlcIndx
175  } // has a parent
176  for (auto dtruid : opfp.DtrUIDs) {
177  auto dSlcIndx = GetSliceIndex("P", dtruid);
178  if (dSlcIndx.first < slices.size()) {
179  auto& dtrpfp = slices[dSlcIndx.first].pfps[dSlcIndx.second];
180  dtrpfp.ParentUID = pfp.UID;
181  } // valid dSlcIndx
182  } // dtruid
183  // declare it obsolete
184  opfp.ID = 0;
185  } // puid
186  } // stl
187 
188  } // StitchPFPs
189 
190  void
193  TCSlice& slc)
194  {
195  // Match Tjs in 3D and create PFParticles
196 
197  if (tcc.match3DCuts[0] <= 0) return;
198 
200 
201  // clear the TP -> P assn Tjs so that all are considered
202  for (auto& tj : slc.tjs) {
203  for (auto& tp : tj.Pts)
204  tp.InPFP = 0;
205  } // tj
206 
207  bool prt = (tcc.dbgPFP && tcc.dbgSlc);
208 
209  // Match these points in 3D
210  std::vector<MatchStruct> matVec;
211 
212  // iterate twice (at most), looking for 3-plane matches in long tjs in 3-plane TPCs on the
213  // first iteration, 3-plane matches in short tjs on the second iteration.
214  // and 2-plane matches + dead regions in 3-plane TPCs on the last iteration
215  slc.mallTraj.clear();
216 
217  unsigned short maxNit = 2;
218  if (slc.nPlanes == 2) maxNit = 1;
219  if (std::nearbyint(tcc.match3DCuts[2]) == 0) maxNit = 1;
220  // fill the mAllTraj vector with TPs if we aren't using SpacePoints
221  if (evt.sptHits.empty()) FillmAllTraj(detProp, slc);
222  for (unsigned short nit = 0; nit < maxNit; ++nit) {
223  matVec.clear();
224  if (slc.nPlanes == 3 && nit == 0) {
225  // look for match triplets
226  Match3Planes(slc, matVec);
227  }
228  else {
229  // look for match doublets requiring a dead region in the 3rd plane for 3-plane TPCs
230  Match2Planes(slc, matVec);
231  }
232  if (matVec.empty()) continue;
233  if (prt) {
234  mf::LogVerbatim myprt("TC");
235  myprt << "nit " << nit << " MVI Count Tjs\n";
236  for (unsigned int indx = 0; indx < matVec.size(); ++indx) {
237  auto& ms = matVec[indx];
238  myprt << std::setw(5) << indx << std::setw(6) << (int)ms.Count;
239  for (auto tid : ms.TjIDs)
240  myprt << " T" << tid;
241  // count the number of TPs in all Tjs
242  float tpCnt = 0;
243  for (auto tid : ms.TjIDs) {
244  auto& tj = slc.tjs[tid - 1];
245  tpCnt += NumPtsWithCharge(slc, tj, false);
246  } // tid
247  float frac = ms.Count / tpCnt;
248  myprt << " matFrac " << std::fixed << std::setprecision(3) << frac;
249  myprt << "\n";
250  } // indx
251  } // prt
252  MakePFParticles(clockData, detProp, slc, matVec, nit);
253  } // nit
254 
255  // a last debug print
256  if (tcc.dbgPFP && debug.MVI != UINT_MAX) {
257  for (auto& pfp : slc.pfps)
258  if (tcc.dbgPFP && pfp.MVI == debug.MVI)
259  PrintTP3Ds(clockData, detProp, "FPFP", slc, pfp, -1);
260  } // last debug print
261 
262  slc.mallTraj.resize(0);
263 
264  } // FindPFParticles
265 
266  ////////////////////////////////////////////////
267  void
270  TCSlice& slc,
271  std::vector<MatchStruct> matVec,
272  unsigned short matVec_Iter)
273  {
274  // Makes PFParticles using Tjs listed in matVec
275  if (matVec.empty()) return;
276 
277  bool prt = (tcc.dbgPFP && tcc.dbgSlc);
278 
279  // create a PFParticle for each valid match combination
280  for (std::size_t indx = 0; indx < matVec.size(); ++indx) {
281  // tone down the level of printing in ReSection
282  bool foundMVI = (tcc.dbgPFP && indx == debug.MVI && matVec_Iter == debug.MVI_Iter);
283  if (foundMVI) prt = true;
284  auto& ms = matVec[indx];
285  if (foundMVI) {
286  std::cout << "found MVI " << indx << " in MakePFParticles ms.Count = " << ms.Count << "\n";
287  }
288  // ignore dead matches
289  if (ms.Count == 0) continue;
290  // count the number of TPs that are available (not already 3D-matched) and used in a pfp
291  float npts = 0;
292  for (std::size_t itj = 0; itj < ms.TjIDs.size(); ++itj) {
293  auto& tj = slc.tjs[ms.TjIDs[itj] - 1];
294  for (unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt)
295  if (tj.Pts[ipt].InPFP == 0) ++npts;
296  } // tjID
297  // Create a vector of PFPs for this match so that we can split it later on if a kink is found
298  std::vector<PFPStruct> pfpVec(1);
299  pfpVec[0] = CreatePFP(slc);
300  // Define the starting set of tjs that were matched. TPs from other tjs may be added later
301  pfpVec[0].TjIDs = ms.TjIDs;
302  pfpVec[0].MVI = indx;
303  // fill the TP3D points using the 2D trajectory points for Tjs in TjIDs. All
304  // points are put in one section
305  if (!MakeTP3Ds(detProp, slc, pfpVec[0], foundMVI)) {
306  if (foundMVI) mf::LogVerbatim("TC") << " MakeTP3Ds failed. Too many points already used ";
307  continue;
308  }
309  // fit all the points to get the general direction
310  if (!FitSection(clockData, detProp, slc, pfpVec[0], 0)) continue;
311  if (pfpVec[0].SectionFits[0].ChiDOF > 500 && !pfpVec[0].Flags[kSmallAngle]) {
312  if (foundMVI)
313  mf::LogVerbatim("TC") << " crazy high ChiDOF P" << pfpVec[0].ID << " "
314  << pfpVec[0].SectionFits[0].ChiDOF << "\n";
315  Recover(clockData, detProp, slc, pfpVec[0], foundMVI);
316  continue;
317  }
318  // sort the points by the distance along the general direction vector
319  if (!SortSection(pfpVec[0], 0)) continue;
320  // define a junk pfp to be short with low MCSMom. These are likely to be shower-like
321  // pfps. A simple 3D line fit will be done. No attempt will be made to reconstruct it
322  // in sections or to look for kinks
323  npts = pfpVec[0].TP3Ds.size();
324  pfpVec[0].AlgMod[kJunk3D] = (npts < 20 && MCSMom(slc, pfpVec[0].TjIDs) < 50) || (npts < 10);
325  if (prt) {
326  auto& pfp = pfpVec[0];
327  mf::LogVerbatim myprt("TC");
328  myprt << " indx " << matVec_Iter << "/" << indx << " Count " << std::setw(5)
329  << (int)ms.Count;
330  myprt << " P" << pfpVec[0].ID;
331  myprt << " ->";
332  for (auto& tjid : pfp.TjIDs)
333  myprt << " T" << tjid;
334  myprt << " projInPlane";
335  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
336  CTP_t inCTP = EncodeCTP(pfp.TPCID.Cryostat, pfp.TPCID.TPC, plane);
337  auto tp = MakeBareTP(detProp, slc, pfp.SectionFits[0].Pos, pfp.SectionFits[0].Dir, inCTP);
338  myprt << " " << std::setprecision(2) << tp.Delta;
339  } // plane
340  myprt << " maxTjLen " << (int)MaxTjLen(slc, pfp.TjIDs);
341  myprt << " MCSMom " << MCSMom(slc, pfp.TjIDs);
342  myprt << " PDGCodeVote " << PDGCodeVote(clockData, detProp, slc, pfp);
343  myprt << " nTP3Ds " << pfp.TP3Ds.size();
344  myprt << " Reco3DRange "
345  << Find3DRecoRange(slc, pfp, 0, (unsigned short)tcc.match3DCuts[3], 1);
346  } // prt
347  if (foundMVI) { PrintTP3Ds(clockData, detProp, "FF", slc, pfpVec[0], -1); }
348  for (unsigned short ip = 0; ip < pfpVec.size(); ++ip) {
349  auto& pfp = pfpVec[ip];
350  // set the end flag bits
351  geo::TPCID tpcid;
352  for (unsigned short end = 0; end < 2; ++end) {
353  // first set them all to 0
354  pfp.EndFlag[end].reset();
355  auto pos = PosAtEnd(pfp, end);
356  if (!InsideTPC(pos, tpcid)) pfp.EndFlag[end][kOutFV] = true;
357  } // end
358  // Set kink flag and create a vertex between this pfp and the previous one that was stored
359  if (ip > 0) {
360  pfp.EndFlag[0][kAtKink] = true;
361  Vtx3Store vx3;
362  vx3.TPCID = pfp.TPCID;
363  vx3.X = pfp.TP3Ds[0].Pos[0];
364  vx3.Y = pfp.TP3Ds[0].Pos[1];
365  vx3.Z = pfp.TP3Ds[0].Pos[2];
366  // TODO: Errors, Score?
367  vx3.Score = 100;
368  vx3.Vx2ID.resize(slc.nPlanes);
369  vx3.Wire = -2;
370  vx3.ID = slc.vtx3s.size() + 1;
371  vx3.Primary = false;
372  ++evt.global3V_UID;
373  vx3.UID = evt.global3V_UID;
374  slc.vtx3s.push_back(vx3);
375  pfp.Vx3ID[0] = vx3.ID;
376  auto& prevPFP = slc.pfps[slc.pfps.size() - 1];
377  prevPFP.Vx3ID[1] = vx3.ID;
378  } // ip > 0
379  // check for a valid two-plane match with a Tj in the third plane for long pfps.
380  // For short pfps, it is possible that a Tj would be too short to be reconstructed
381  // in the third plane.
382  if (pfp.TjIDs.size() == 2 && slc.nPlanes == 3 && pfp.TP3Ds.size() > 20 &&
383  !ValidTwoPlaneMatch(detProp, slc, pfp)) {
384  continue;
385  }
386  // Skip this combination if it isn't reconstructable in 3D
387  if (Find3DRecoRange(slc, pfp, 0, (unsigned short)tcc.match3DCuts[3], 1) == USHRT_MAX)
388  continue;
389  // See if it possible to reconstruct in more than one section
390  pfp.Flags[kCanSection] = CanSection(slc, pfp);
391  // Do a fit in multiple sections if the initial fit is poor
392  if (pfp.SectionFits[0].ChiDOF < tcc.match3DCuts[5]) {
393  // Good fit with one section
394  pfp.Flags[kNeedsUpdate] = false;
395  }
396  else if (pfp.Flags[kCanSection]) {
397  if (!ReSection(clockData, detProp, slc, pfp, foundMVI)) continue;
398  } // CanSection
399  if (foundMVI) { PrintTP3Ds(clockData, detProp, "RS", slc, pfp, -1); }
400  // FillGaps3D looks for gaps in the TP3Ds vector caused by broken trajectories and
401  // inserts new TP3Ds if there are hits in the gaps. This search is only done in a
402  // plane if the projection of the pfp results in a large angle where 2D reconstruction
403  // is likely to be poor - not true for TCWork2
404  FillGaps3D(clockData, detProp, slc, pfp, foundMVI);
405  // Check the TP3D -> TP assn, resolve conflicts and set TP -> InPFP
406  if (!ReconcileTPs(slc, pfp, foundMVI)) continue;
407  // Look for mis-placed 2D and 3D vertices
408  ReconcileVertices(slc, pfp, foundMVI);
409  // Set isGood
410  for (auto& tp3d : pfp.TP3Ds) {
411  if (tp3d.Flags[kTP3DBad]) continue;
412  auto& tp = slc.tjs[tp3d.TjID - 1].Pts[tp3d.TPIndex];
413  if (tp.Environment[kEnvOverlap]) tp3d.Flags[kTP3DGood] = false;
414  } // tp3d
415  FilldEdx(clockData, detProp, slc, pfp);
416  pfp.PDGCode = PDGCodeVote(clockData, detProp, slc, pfp);
417  if (tcc.dbgPFP && pfp.MVI == debug.MVI)
418  PrintTP3Ds(clockData, detProp, "STORE", slc, pfp, -1);
419  if (!StorePFP(slc, pfp)) break;
420  } // ip (iterate over split pfps)
421  } // indx (iterate over matchVec entries)
422  slc.mallTraj.resize(0);
423  } // MakePFParticles
424 
425  ////////////////////////////////////////////////
426  bool
427  ReconcileTPs(TCSlice& slc, PFPStruct& pfp, bool prt)
428  {
429  // Reconcile TP -> P assns before the pfp is stored. The TP3D -> TP is defined but
430  // the TP -> P assn may not have been done. This function overwrites the TjIDs
431  // vector to be the list of Tjs that contribute > 80% of their TPs to this pfp.
432  // This function returns true if the assns are consistent.
433 
434  if (!tcc.useAlg[kRTPs3D]) return true;
435  if(pfp.Flags[kSmallAngle]) return true;
436  if (pfp.TjIDs.empty()) return false;
437  if (pfp.TP3Ds.empty()) return false;
438  if (pfp.ID <= 0) return false;
439 
440  // Tj ID, TP count
441  std::vector<std::pair<int, float>> tjTPCnt;
442  for (auto& tp3d : pfp.TP3Ds) {
443  if (tp3d.Flags[kTP3DBad]) continue;
444  if (tp3d.TjID <= 0) return false;
445  // compare the TP3D -> TP -> P assn with the P -> TP assn
446  auto& tp = slc.tjs[tp3d.TjID - 1].Pts[tp3d.TPIndex];
447  if (tp.InPFP > 0 && tp.InPFP != pfp.ID) return false;
448  // find the (Tj ID, TP count) pair in the list
449  unsigned short indx = 0;
450  for (indx = 0; indx < tjTPCnt.size(); ++indx)
451  if (tjTPCnt[indx].first == tp3d.TjID) break;
452  if (indx == tjTPCnt.size()) tjTPCnt.push_back(std::make_pair(tp3d.TjID, 0));
453  ++tjTPCnt[indx].second;
454  // make the TP -> P assn
455  tp.InPFP = pfp.ID;
456  } // tp3d
457 
458  std::vector<int> nTjIDs;
459  for (auto& tjtpcnt : tjTPCnt) {
460  auto& tj = slc.tjs[tjtpcnt.first - 1];
461  float npwc = NumPtsWithCharge(slc, tj, false);
462  if (tjtpcnt.second > 0.8 * npwc) nTjIDs.push_back(tjtpcnt.first);
463  } // tjtpcnt
464  if (prt) { mf::LogVerbatim("TC") << "RTPs3D: P" << pfp.ID << " nTjIDs " << nTjIDs.size(); }
465  // TODO: is this really a failure?
466  if (nTjIDs.size() < 2) { return false; }
467  pfp.TjIDs = nTjIDs;
468 
469  return true;
470  } // ReconcileTPs
471 
472  ////////////////////////////////////////////////
473  void
475  {
476  // Reconciles TP ownership conflicts between PFParticles
477  // Make a one-to-one TP -> P assn and look for one-to-many assns.
478  // Note: Comparing the pulls for a TP to two different PFParticles generally results
479  // in selecting the first PFParticle that was made which is not too surprising considering
480  // the order in which they were created. This comparison has been commented out in favor
481  // of simply keeping the old assn and removing the new one by setting IsBad true.
482 
483  if (!tcc.useAlg[kRTPs3D]) return;
484 
485  // make a list of T -> P assns
486  std::vector<int> TinP;
487  for (auto& pfp : slc.pfps) {
488  if (pfp.ID <= 0) continue;
489  if(pfp.Flags[kSmallAngle]) continue;
490  for (std::size_t ipt = 0; ipt < pfp.TP3Ds.size(); ++ipt) {
491  auto& tp3d = pfp.TP3Ds[ipt];
492  if (tp3d.TjID <= 0) continue;
493  if (std::find(TinP.begin(), TinP.end(), tp3d.TjID) == TinP.end()) TinP.push_back(tp3d.TjID);
494  auto& tp = slc.tjs[tp3d.TjID - 1].Pts[tp3d.TPIndex];
495  if (tp.InPFP > 0) {
496  // an assn exists. Set the overlap bit and check consistency
497  tp.Environment[kEnvOverlap] = true;
498  // keep the previous assn (since it was created earlier and is more credible) and remove the new one
499  tp3d.Flags[kTP3DBad] = true;
500  tp3d.Flags[kTP3DGood] = false;
501  tp.InPFP = 0;
502  }
503  else {
504  // no assn exists
505  tp.InPFP = pfp.ID;
506  } // tp.InPFP > 0
507  } // ipt
508  } // pfp
509  } // ReconcileTPs
510 
511  /////////////////////////////////////////
512  void
514  {
515  // This function clobbers all of the tjs that are used in TP3Ds in the pfp and replaces
516  // them with new tjs that have a consistent set of TPs to prepare for putting them
517  // into the event. Note that none of the Tjs are attached to 2D vertices.
518  if (!tcc.useAlg[kMakePFPTjs]) return;
519 
520  // kill trajectories
521  std::vector<int> killme;
522  for (auto& pfp : slc.pfps) {
523  if (pfp.ID <= 0) continue;
524  for (auto& tp3d : pfp.TP3Ds) {
525  if (tp3d.TjID <= 0) continue;
526  if (tp3d.Flags[kTP3DBad]) continue;
527  if (std::find(killme.begin(), killme.end(), tp3d.TjID) == killme.end())
528  killme.push_back(tp3d.TjID);
529  } // tp3d
530  } // pfp
531 
532  bool prt = (tcc.dbgPFP);
533 
534  for (auto tid : killme)
535  MakeTrajectoryObsolete(slc, (unsigned int)(tid - 1));
536 
537  // Make template trajectories in each plane. These will be re-used by
538  // each PFParticle
539  std::vector<Trajectory> ptjs(slc.nPlanes);
540  // define the basic tj variables
541  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
542  ptjs[plane].Pass = 0;
543  ptjs[plane].CTP = EncodeCTP(slc.TPCID.Cryostat, slc.TPCID.TPC, plane);
544  // This Tj wasn't created by stepping
545  ptjs[plane].StepDir = 0;
546  // It was created by this function however
547  ptjs[plane].AlgMod[kMakePFPTjs] = true;
548  // and is 3D matched
549  ptjs[plane].AlgMod[kMat3D] = true;
550  } // plane
551 
552  // now make the new Tjs
553  for (auto& pfp : slc.pfps) {
554  if (pfp.ID <= 0) continue;
555  // initialize the tjs
556  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
557  ptjs[plane].Pts.clear();
558  --evt.WorkID;
559  if (evt.WorkID == INT_MIN) evt.WorkID = -1;
560  ptjs[plane].ID = evt.WorkID;
561  } // plane
562  pfp.TjIDs.clear();
563  // iterate through all of the TP3Ds, adding TPs to the TJ in the appropriate plane.
564  // The assumption here is that TP order reflects the TP3D order
565  for (auto& tp3d : pfp.TP3Ds) {
566  if (tp3d.TjID <= 0) continue;
567  if (tp3d.Flags[kTP3DBad]) continue;
568  // make a copy of the 2D TP
569  auto tp = slc.tjs[tp3d.TjID - 1].Pts[tp3d.TPIndex];
570  if (tp.InPFP > 0 && tp.InPFP != pfp.ID) continue;
571  tp.InPFP = pfp.ID;
572  // the TP Step isn't useful anymore, so stash the original TJ ID into it
573  tp.Step = tp3d.TjID;
574  unsigned short plane = DecodeCTP(tp.CTP).Plane;
575  // append it to Pts
576  ptjs[plane].Pts.push_back(tp);
577  } // tp3d
578  // finish defining each of the Tjs and store them
579  // new tj ID indexed by plane
580  std::vector<int> tids(ptjs.size(), 0);
581  for (unsigned short plane = 0; plane < ptjs.size(); ++plane) {
582  auto& tj = ptjs[plane];
583  if (tj.Pts.size() < 2) continue;
584  tj.PDGCode = pfp.PDGCode;
585  tj.MCSMom = MCSMom(slc, tj);
586  if (!StoreTraj(slc, tj)) continue;
587  // associate it with the pfp
588  auto& newTj = slc.tjs.back();
589  pfp.TjIDs.push_back(newTj.ID);
590  tids[plane] = newTj.ID;
591  } // tj
592  // preserve the PFP -> 3V -> 2V -> T assns
593  for(unsigned short end = 0; end < 2; ++end) {
594  if(pfp.Vx3ID[end] <= 0) continue;
595  auto& vx3 = slc.vtx3s[pfp.Vx3ID[end] - 1];
596  for(unsigned short plane = 0; plane < ptjs.size(); ++plane) {
597  if(tids[plane] == 0) continue;
598  if(vx3.Vx2ID[plane] <= 0) continue;
599  auto& vx2 = slc.vtxs[vx3.Vx2ID[plane] - 1];
600  auto& tj = slc.tjs[tids[plane] - 1];
601  auto tend = CloseEnd(slc, tj, vx2.Pos);
602  tj.VtxID[tend] = vx2.ID;
603  if(prt) mf::LogVerbatim("TC") << "MPFPTjs: 3V" << vx3.ID << " -> 2V" << vx2.ID
604  << " -> T" << tj.ID << "_" << tend << " in plane " << plane;
605  } // plane
606  } // end
607  } // pfp
608  } // MakePFPTjs
609 
610  /////////////////////////////////////////
611  void
613  {
614  // Find wire intersections and put them in evt.wireIntersections
615 
616  // see if anything needs to be done
617  if (!evt.wireIntersections.empty() && evt.wireIntersections[0].tpc == slc.TPCID.TPC) return;
618 
619  evt.wireIntersections.clear();
620 
621  unsigned int cstat = slc.TPCID.Cryostat;
622  unsigned int tpc = slc.TPCID.TPC;
623  // find the minMax number of wires in each plane of the TPC
624  unsigned int maxWire = slc.nWires[0];
625  for (auto nw : slc.nWires)
626  if (nw < maxWire) maxWire = nw;
627  // Start looking for intersections in the middle
628  unsigned int firstWire = maxWire / 2;
629 
630  // find a valid wire intersection in all plane combinations
631  std::vector<std::pair<unsigned short, unsigned short>> pln1pln2;
632  for (unsigned short pln1 = 0; pln1 < slc.nPlanes - 1; ++pln1) {
633  for (unsigned short pln2 = pln1 + 1; pln2 < slc.nPlanes; ++pln2) {
634  auto p1p2 = std::make_pair(pln1, pln2);
635  if (std::find(pln1pln2.begin(), pln1pln2.end(), p1p2) != pln1pln2.end()) continue;
636  // find two wires that have a valid intersection
637  for (unsigned int wire = firstWire; wire < maxWire; ++wire) {
638  double y00, z00;
639  if (!tcc.geom->IntersectionPoint(wire, wire, pln1, pln2, cstat, tpc, y00, z00)) continue;
640  // increment by one wire in pln1 and find another valid intersection
641  double y10, z10;
642  if (!tcc.geom->IntersectionPoint(wire + 10, wire, pln1, pln2, cstat, tpc, y10, z10))
643  continue;
644  // increment by one wire in pln2 and find another valid intersection
645  double y01, z01;
646  if (!tcc.geom->IntersectionPoint(wire, wire + 10, pln1, pln2, cstat, tpc, y01, z01))
647  continue;
648  TCWireIntersection tcwi;
649  tcwi.tpc = tpc;
650  tcwi.pln1 = pln1;
651  tcwi.pln2 = pln2;
652  tcwi.wir1 = wire;
653  tcwi.wir2 = wire;
654  tcwi.y = y00;
655  tcwi.z = z00;
656  tcwi.dydw1 = (y10 - y00) / 10;
657  tcwi.dzdw1 = (z10 - z00) / 10;
658  tcwi.dydw2 = (y01 - y00) / 10;
659  tcwi.dzdw2 = (z01 - z00) / 10;
660  evt.wireIntersections.push_back(tcwi);
661  break;
662  } // wire
663  } // pln2
664  } // pln1
665  } // FillWireIntersections
666 
667  /////////////////////////////////////////
668  bool
669  TCIntersectionPoint(unsigned int wir1,
670  unsigned int wir2,
671  unsigned int pln1,
672  unsigned int pln2,
673  float& y,
674  float& z)
675  {
676  // A TrajCluster analog of geometry IntersectionPoint that uses local wireIntersections with
677  // float precision. The (y,z) position is only used to match TPs between planes - not for 3D fitting
678  if (evt.wireIntersections.empty()) return false;
679  if (pln1 == pln2) return false;
680 
681  if (pln1 > pln2) {
682  std::swap(pln1, pln2);
683  std::swap(wir1, wir2);
684  }
685 
686  for (auto& wi : evt.wireIntersections) {
687  if (wi.pln1 != pln1) continue;
688  if (wi.pln2 != pln2) continue;
689  // estimate the position using the wire differences
690  double dw1 = wir1 - wi.wir1;
691  double dw2 = wir2 - wi.wir2;
692  y = (float)(wi.y + dw1 * wi.dydw1 + dw2 * wi.dydw2);
693  z = (float)(wi.z + dw1 * wi.dzdw1 + dw2 * wi.dzdw2);
694  return true;
695  } // wi
696  return false;
697  } // TCIntersectionPoint
698 
699  /////////////////////////////////////////
700  void
701  Match3PlanesSpt(TCSlice& slc, std::vector<MatchStruct>& matVec)
702  {
703  // fill matVec using SpacePoint -> Hit -> TP -> tj assns
704  if (evt.sptHits.empty()) return;
705 
706  // create a local vector of allHit -> Tj assns and populate it
707  std::vector<int> inTraj((*evt.allHits).size(), 0);
708  for (auto& tch : slc.slHits)
709  inTraj[tch.allHitsIndex] = tch.InTraj;
710 
711  // the TJ IDs for one match
712  std::array<int, 3> tIDs;
713  // vector for matched Tjs
714  std::vector<std::array<int, 3>> mtIDs;
715  // and a matching vector for the count
716  std::vector<unsigned short> mCnt;
717  // ignore Tj matches after hitting a user-defined limit
718  unsigned short maxCnt = USHRT_MAX;
719  if (tcc.match3DCuts[1] < (float)USHRT_MAX) maxCnt = (unsigned short)tcc.match3DCuts[1];
720  // a list of those Tjs
721  std::vector<unsigned short> tMaxed;
722 
723  unsigned int tpc = slc.TPCID.TPC;
724 
725  for (auto& sptHits : evt.sptHits) {
726  if (sptHits.size() != 3) continue;
727  // ensure that the SpacePoint is in the requested TPC
728  if (!SptInTPC(sptHits, tpc)) continue;
729  unsigned short cnt = 0;
730  for (unsigned short plane = 0; plane < 3; ++plane) {
731  unsigned int iht = sptHits[plane];
732  if (iht == UINT_MAX) continue;
733  if (inTraj[iht] <= 0) continue;
734  tIDs[plane] = inTraj[iht];
735  ++cnt;
736  } // iht
737  if (cnt != 3) continue;
738  // look for it in the list of tj combinations
739  unsigned short indx = 0;
740  for (indx = 0; indx < mtIDs.size(); ++indx)
741  if (tIDs == mtIDs[indx]) break;
742  if (indx == mtIDs.size()) {
743  // not found so add it to mtIDs and add another element to mCnt
744  mtIDs.push_back(tIDs);
745  mCnt.push_back(0);
746  }
747  ++mCnt[indx];
748  if (mCnt[indx] == maxCnt) {
749  // add the Tjs to the list
750  tMaxed.insert(tMaxed.end(), tIDs[0]);
751  tMaxed.insert(tMaxed.end(), tIDs[1]);
752  tMaxed.insert(tMaxed.end(), tIDs[2]);
753  break;
754  } // hit maxCnt
755  ++cnt;
756  } // sptHit
757 
758  std::vector<SortEntry> sortVec;
759  for (unsigned short indx = 0; indx < mCnt.size(); ++indx) {
760  auto& tIDs = mtIDs[indx];
761  // find the fraction of TPs on the shortest tj that are matched
762  float minTPCnt = USHRT_MAX;
763  for (auto tid : tIDs) {
764  auto& tj = slc.tjs[tid - 1];
765  float tpcnt = NumPtsWithCharge(slc, tj, false);
766  if (tpcnt < minTPCnt) minTPCnt = tpcnt;
767  } // tid
768  float frac = (float)mCnt[indx] / minTPCnt;
769  // ignore matches with a very low match fraction
770  if (frac < 0.05) continue;
771  SortEntry se;
772  se.index = indx;
773  se.val = mCnt[indx];
774  sortVec.push_back(se);
775  } // ii
776  if (sortVec.size() > 1) std::sort(sortVec.begin(), sortVec.end(), valsDecreasing);
777 
778  matVec.resize(sortVec.size());
779 
780  for (unsigned short ii = 0; ii < sortVec.size(); ++ii) {
781  unsigned short indx = sortVec[ii].index;
782  auto& ms = matVec[ii];
783  ms.Count = mCnt[indx];
784  ms.TjIDs.resize(3);
785  for (unsigned short plane = 0; plane < 3; ++plane)
786  ms.TjIDs[plane] = mtIDs[indx][plane];
787  } // indx
788 
789  } // Match3PlanesSpt
790 
791  /////////////////////////////////////////
792  bool
793  SptInTPC(const std::array<unsigned int, 3>& sptHits, unsigned int tpc)
794  {
795  // returns true if a hit referenced in sptHits resides in the requested tpc. We assume
796  // that if one does, then all of them do
797 
798  unsigned int ahi = UINT_MAX;
799  for (auto ii : sptHits)
800  if (ii != UINT_MAX) {
801  ahi = ii;
802  break;
803  }
804  if (ahi >= (*evt.allHits).size()) return false;
805  // get a reference to the hit and see if it is in the desired tpc
806  auto& hit = (*evt.allHits)[ahi];
807  if (hit.WireID().TPC == tpc) return true;
808  return false;
809 
810  } // SptInTPC
811 
812  /////////////////////////////////////////
813  void
814  Match3Planes(TCSlice& slc, std::vector<MatchStruct>& matVec)
815  {
816  // A simpler and faster version of MatchPlanes that only creates three plane matches
817 
818  if (slc.nPlanes != 3) return;
819 
820  // use SpacePoint -> Hit -> TP assns?
821  if (!evt.sptHits.empty()) {
822  Match3PlanesSpt(slc, matVec);
823  return;
824  }
825 
826  if (slc.mallTraj.empty()) return;
827  float xcut = tcc.match3DCuts[0];
828  double yzcut = 1.5 * tcc.wirePitch;
829 
830  // the TJ IDs for one match
831  std::array<unsigned short, 3> tIDs;
832  // vector for matched Tjs
833  std::vector<std::array<unsigned short, 3>> mtIDs;
834  // and a matching vector for the count
835  std::vector<unsigned short> mCnt;
836  // ignore Tj matches after hitting a user-defined limit
837  unsigned short maxCnt = USHRT_MAX;
838  if (tcc.match3DCuts[1] < (float)USHRT_MAX) maxCnt = (unsigned short)tcc.match3DCuts[1];
839  // a list of those Tjs
840  std::vector<unsigned short> tMaxed;
841 
842  for (std::size_t ipt = 0; ipt < slc.mallTraj.size() - 1; ++ipt) {
843  auto& iTjPt = slc.mallTraj[ipt];
844  // see if we hit the maxCnt limit
845  if (std::find(tMaxed.begin(), tMaxed.end(), iTjPt.id) != tMaxed.end()) continue;
846  auto& itp = slc.tjs[iTjPt.id - 1].Pts[iTjPt.ipt];
847  unsigned int iPlane = iTjPt.plane;
848  unsigned int iWire = std::nearbyint(itp.Pos[0]);
849  tIDs[iPlane] = iTjPt.id;
850  bool hitMaxCnt = false;
851  for (std::size_t jpt = ipt + 1; jpt < slc.mallTraj.size() - 1; ++jpt) {
852  auto& jTjPt = slc.mallTraj[jpt];
853  // ensure that the planes are different
854  if (jTjPt.plane == iTjPt.plane) continue;
855  // check for x range overlap. We know that jTjPt.xlo is >= iTjPt.xlo because of the sort
856  if (jTjPt.xlo > iTjPt.xhi) continue;
857  // break out if the x range difference becomes large
858  if (jTjPt.xlo > iTjPt.xhi + xcut) break;
859  // see if we hit the maxCnt limit
860  if (std::find(tMaxed.begin(), tMaxed.end(), jTjPt.id) != tMaxed.end()) continue;
861  auto& jtp = slc.tjs[jTjPt.id - 1].Pts[jTjPt.ipt];
862  unsigned short jPlane = jTjPt.plane;
863  unsigned int jWire = jtp.Pos[0];
864  Point2_t ijPos;
865  if (!TCIntersectionPoint(iWire, jWire, iPlane, jPlane, ijPos[0], ijPos[1])) continue;
866  tIDs[jPlane] = jTjPt.id;
867  for (std::size_t kpt = jpt + 1; kpt < slc.mallTraj.size(); ++kpt) {
868  auto& kTjPt = slc.mallTraj[kpt];
869  // ensure that the planes are different
870  if (kTjPt.plane == iTjPt.plane || kTjPt.plane == jTjPt.plane) continue;
871  if (kTjPt.xlo > iTjPt.xhi) continue;
872  // break out if the x range difference becomes large
873  if (kTjPt.xlo > iTjPt.xhi + xcut) break;
874  // see if we hit the maxCnt limit
875  if (std::find(tMaxed.begin(), tMaxed.end(), kTjPt.id) != tMaxed.end()) continue;
876  auto& ktp = slc.tjs[kTjPt.id - 1].Pts[kTjPt.ipt];
877  unsigned short kPlane = kTjPt.plane;
878  unsigned int kWire = ktp.Pos[0];
879  Point2_t ikPos;
880  if (!TCIntersectionPoint(iWire, kWire, iPlane, kPlane, ikPos[0], ikPos[1])) continue;
881  if (std::abs(ijPos[0] - ikPos[0]) > yzcut) continue;
882  if (std::abs(ijPos[1] - ikPos[1]) > yzcut) continue;
883  // we have a match
884  tIDs[kPlane] = kTjPt.id;
885  // look for it in the list
886  unsigned int indx = 0;
887  for (indx = 0; indx < mtIDs.size(); ++indx)
888  if (tIDs == mtIDs[indx]) break;
889  if (indx == mtIDs.size()) {
890  // not found so add it to mtIDs and add another element to mCnt
891  mtIDs.push_back(tIDs);
892  mCnt.push_back(0);
893  }
894  ++mCnt[indx];
895  if (mCnt[indx] == maxCnt) {
896  // add the Tjs to the list
897  tMaxed.insert(tMaxed.end(), tIDs[0]);
898  tMaxed.insert(tMaxed.end(), tIDs[1]);
899  tMaxed.insert(tMaxed.end(), tIDs[2]);
900  hitMaxCnt = true;
901  break;
902  } // hit maxCnt
903  } // kpt
904  if (hitMaxCnt) break;
905  } // jpt
906  } // ipt
907 
908  if (mCnt.empty()) return;
909 
910  std::vector<SortEntry> sortVec;
911  for (std::size_t indx = 0; indx < mCnt.size(); ++indx) {
912  auto& tIDs = mtIDs[indx];
913  // count the number of TPs in all Tjs
914  float tpCnt = 0;
915  for (auto tid : tIDs) {
916  auto& tj = slc.tjs[tid - 1];
917  tpCnt += NumPtsWithCharge(slc, tj, false);
918  } // tid
919  float frac = mCnt[indx] / tpCnt;
920  frac /= 3;
921  // ignore matches with a very low match fraction
922  if (frac < 0.05) continue;
923  SortEntry se;
924  se.index = indx;
925  se.val = mCnt[indx];
926  sortVec.push_back(se);
927  } // ii
928  if (sortVec.size() > 1) std::sort(sortVec.begin(), sortVec.end(), valsDecreasing);
929 
930  matVec.resize(sortVec.size());
931 
932  for (std::size_t ii = 0; ii < sortVec.size(); ++ii) {
933  unsigned short indx = sortVec[ii].index;
934  auto& ms = matVec[ii];
935  ms.Count = mCnt[indx];
936  ms.TjIDs.resize(3);
937  for (unsigned short plane = 0; plane < 3; ++plane)
938  ms.TjIDs[plane] = (int)mtIDs[indx][plane];
939  } // indx
940 
941  } // Match3Planes
942 
943  /////////////////////////////////////////
944  void
945  Match2Planes(TCSlice& slc, std::vector<MatchStruct>& matVec)
946  {
947  // A simpler faster version of MatchPlanes that only creates two plane matches
948 
949  matVec.clear();
950  if (slc.mallTraj.empty()) return;
951 
952  int cstat = slc.TPCID.Cryostat;
953  int tpc = slc.TPCID.TPC;
954 
955  float xcut = tcc.match3DCuts[0];
956 
957  // the TJ IDs for one match
958  std::array<unsigned short, 2> tIDs;
959  // vector for matched Tjs
960  std::vector<std::array<unsigned short, 2>> mtIDs;
961  // and a matching vector for the count
962  std::vector<unsigned short> mCnt;
963  // ignore Tj matches after hitting a user-defined limit
964  unsigned short maxCnt = USHRT_MAX;
965  if (tcc.match3DCuts[1] < (float)USHRT_MAX) maxCnt = (unsigned short)tcc.match3DCuts[1];
966  // a list of those Tjs
967  std::vector<unsigned short> tMaxed;
968 
969  for (std::size_t ipt = 0; ipt < slc.mallTraj.size() - 1; ++ipt) {
970  auto& iTjPt = slc.mallTraj[ipt];
971  // see if we hit the maxCnt limit
972  if (std::find(tMaxed.begin(), tMaxed.end(), iTjPt.id) != tMaxed.end()) continue;
973  auto& itp = slc.tjs[iTjPt.id - 1].Pts[iTjPt.ipt];
974  unsigned short iPlane = iTjPt.plane;
975  unsigned int iWire = itp.Pos[0];
976  bool hitMaxCnt = false;
977  for (std::size_t jpt = ipt + 1; jpt < slc.mallTraj.size() - 1; ++jpt) {
978  auto& jTjPt = slc.mallTraj[jpt];
979  // ensure that the planes are different
980  if (jTjPt.plane == iTjPt.plane) continue;
981  // check for x range overlap. We know that jTjPt.xlo is >= iTjPt.xlo because of the sort
982  if (jTjPt.xlo > iTjPt.xhi) continue;
983  // break out if the x range difference becomes large
984  if (jTjPt.xlo > iTjPt.xhi + xcut) break;
985  // see if we hit the maxCnt limit
986  if (std::find(tMaxed.begin(), tMaxed.end(), jTjPt.id) != tMaxed.end()) continue;
987  auto& jtp = slc.tjs[jTjPt.id - 1].Pts[jTjPt.ipt];
988  unsigned short jPlane = jTjPt.plane;
989  unsigned int jWire = jtp.Pos[0];
990  Point3_t ijPos;
991  ijPos[0] = itp.Pos[0];
992  if (!tcc.geom->IntersectionPoint(
993  iWire, jWire, iPlane, jPlane, cstat, tpc, ijPos[1], ijPos[2]))
994  continue;
995  tIDs[0] = iTjPt.id;
996  tIDs[1] = jTjPt.id;
997  // swap the order so that the == operator works correctly
998  if (tIDs[0] > tIDs[1]) std::swap(tIDs[0], tIDs[1]);
999  // look for it in the list
1000  std::size_t indx = 0;
1001  for (indx = 0; indx < mtIDs.size(); ++indx)
1002  if (tIDs == mtIDs[indx]) break;
1003  if (indx == mtIDs.size()) {
1004  // not found so add it to mtIDs and add another element to mCnt
1005  mtIDs.push_back(tIDs);
1006  mCnt.push_back(0);
1007  }
1008  ++mCnt[indx];
1009  if (mCnt[indx] == maxCnt) {
1010  // add the Tjs to the list
1011  tMaxed.insert(tMaxed.end(), tIDs[0]);
1012  tMaxed.insert(tMaxed.end(), tIDs[1]);
1013  hitMaxCnt = true;
1014  break;
1015  } // hit maxCnt
1016  if (hitMaxCnt) break;
1017  } // jpt
1018  } // ipt
1019 
1020  if (mCnt.empty()) return;
1021 
1022  std::vector<SortEntry> sortVec;
1023  for (std::size_t indx = 0; indx < mCnt.size(); ++indx) {
1024  auto& tIDs = mtIDs[indx];
1025  // count the number of TPs in all Tjs
1026  float tpCnt = 0;
1027  for (auto tid : tIDs) {
1028  auto& tj = slc.tjs[tid - 1];
1029  tpCnt += NumPtsWithCharge(slc, tj, false);
1030  } // tid
1031  float frac = mCnt[indx] / tpCnt;
1032  frac /= 2;
1033  // ignore matches with a very low match fraction
1034  if (frac < 0.05) continue;
1035  SortEntry se;
1036  se.index = indx;
1037  se.val = mCnt[indx];
1038  sortVec.push_back(se);
1039  } // ii
1040  if (sortVec.size() > 1) std::sort(sortVec.begin(), sortVec.end(), valsDecreasing);
1041 
1042  matVec.resize(sortVec.size());
1043 
1044  for (std::size_t ii = 0; ii < sortVec.size(); ++ii) {
1045  unsigned short indx = sortVec[ii].index;
1046  auto& ms = matVec[ii];
1047  ms.Count = mCnt[indx];
1048  ms.TjIDs.resize(2);
1049  for (unsigned short plane = 0; plane < 2; ++plane)
1050  ms.TjIDs[plane] = (int)mtIDs[indx][plane];
1051  } // indx
1052 
1053  } // Match2Planes
1054 
1055  /////////////////////////////////////////
1056  bool
1059  const TCSlice& slc,
1060  PFPStruct& pfp,
1061  bool prt)
1062  {
1063  // This function only updates SectionFits that need to be re-sorted or re-fit. It returns
1064  // false if there was a serious error indicating that the pfp should be abandoned
1065  if (pfp.TP3Ds.empty() || pfp.SectionFits.empty()) return false;
1066 
1067  // special handling for small angle tracks
1068  if(pfp.AlgMod[kSmallAngle]) {
1069  for(unsigned short sfi = 0; sfi < pfp.SectionFits.size(); ++sfi) {
1070  auto& sf = pfp.SectionFits[sfi];
1071  if (!sf.NeedsUpdate) continue;
1072  if (!SortSection(pfp, sfi)) return false;
1073  sf.NPts = 0;
1074  sf.ChiDOF = 0;
1075  for(unsigned short ipt = 0; ipt < pfp.TP3Ds.size(); ++ipt) {
1076  auto& tp3d = pfp.TP3Ds[ipt];
1077  if(tp3d.SFIndex < sfi) continue;
1078  if(tp3d.SFIndex > sfi) break;
1079  ++sf.NPts;
1080  double delta = tp3d.Pos[0] - tp3d.TPX;
1081  sf.ChiDOF += delta * delta / tp3d.TPXErr2;
1082  } // ipt
1083  if(sf.NPts < 5) {
1084  sf.ChiDOF = 0;
1085  } else {
1086  sf.ChiDOF /= (float)(sf.NPts - 4);
1087  }
1088  sf.NeedsUpdate = false;
1089  } // sfi
1090  pfp.Flags[kNeedsUpdate] = false;
1091  return true;
1092  } // kSmallAngle
1093 
1094  for (std::size_t sfi = 0; sfi < pfp.SectionFits.size(); ++sfi) {
1095  auto& sf = pfp.SectionFits[sfi];
1096  if (!sf.NeedsUpdate) continue;
1097  if (!FitSection(clockData, detProp, slc, pfp, sfi)) return false;
1098  if (!SortSection(pfp, sfi)) return false;
1099  sf.NeedsUpdate = false;
1100  } // sfi
1101 
1102  // ensure that all points (good or not) have a valid SFIndex
1103  for (auto& tp3d : pfp.TP3Ds) {
1104  if (tp3d.SFIndex >= pfp.SectionFits.size()) SetSection(detProp, slc, pfp, tp3d);
1105  } // tp3d
1106  pfp.Flags[kNeedsUpdate] = false;
1107  return true;
1108  } // Update
1109 
1110  /////////////////////////////////////////
1111  bool
1114  const TCSlice& slc,
1115  PFPStruct& pfp,
1116  bool prt)
1117  {
1118  // Re-fit the TP3Ds in sections and add/remove sections to keep ChiDOF of each section close to 1.
1119  // This function only fails when there is a serious error, otherwise if reasonable fits cannot be
1120  // achieved, the CanSection flag is set false.
1121  if (pfp.SectionFits.empty()) return false;
1122  // This function shouldn't be called if this is the case but it isn't a major failure if it is
1123  if (!pfp.Flags[kCanSection]) return true;
1124  if(pfp.Flags[kSmallAngle]) return true;
1125  // Likewise this shouldn't be attempted if there aren't at least 3 points in 2 planes in 2 sections
1126  // but it isn't a failure
1127  if (pfp.TP3Ds.size() < 12) {
1128  pfp.Flags[kCanSection] = false;
1129  return true;
1130  }
1131 
1132  prt = (pfp.MVI == debug.MVI);
1133 
1134  // try to keep ChiDOF between chiLo and chiHi
1135  float chiLo = 0.5 * tcc.match3DCuts[5];
1136  float chiHi = 1.5 * tcc.match3DCuts[5];
1137 
1138  // clobber the old sections if more than one exists
1139  if (pfp.SectionFits.size() > 1) {
1140  // make one section
1141  pfp.SectionFits.resize(1);
1142  // put all of the points in it and fit
1143  for (auto& tp3d : pfp.TP3Ds) {
1144  tp3d.SFIndex = 0;
1145  tp3d.Flags[kTP3DGood] = true;
1146  }
1147  auto& sf = pfp.SectionFits[0];
1148  if (!FitSection(clockData, detProp, slc, pfp, 0)) { return false; }
1149  if (sf.ChiDOF < tcc.match3DCuts[5]) return true;
1150  } // > 1 SectionFit
1151  // sort by distance from the start
1152  if (!SortSection(pfp, 0)) return false;
1153  // require a minimum of 3 points in 2 planes
1154  unsigned short min2DPts = 3;
1155  unsigned short fromPt = 0;
1156  // set the section index to invalid for all points
1157  for (auto& tp3d : pfp.TP3Ds)
1158  tp3d.SFIndex = USHRT_MAX;
1159  // Guess how many points should be added in each iteration
1160  unsigned short nPtsToAdd = pfp.TP3Ds.size() / 4;
1161  // the actual number of points that will be fit in the section
1162  unsigned short nPts = nPtsToAdd;
1163  // the minimum number of points
1164  unsigned short nPtsMin = Find3DRecoRange(slc, pfp, fromPt, min2DPts, 1) - fromPt + 1;
1165  if (nPtsMin >= pfp.TP3Ds.size()) {
1166  pfp.Flags[kCanSection] = false;
1167  return true;
1168  }
1169  float chiDOF = 0;
1170  if (nPts < nPtsMin) nPts = nPtsMin;
1171  // Try to reduce the number of iterations for long pfps
1172  if (pfp.TP3Ds.size() > 100) {
1173  unsigned short nhalf = pfp.TP3Ds.size() / 2;
1174  FitTP3Ds(detProp, slc, pfp, fromPt, nhalf, USHRT_MAX, chiDOF);
1175  if (chiDOF < tcc.match3DCuts[5]) nPts = nhalf;
1176  }
1177  bool lastSection = false;
1178  for (unsigned short sfIndex = 0; sfIndex < 20; ++sfIndex) {
1179  // Try to add/remove points in each section no more than 20 times
1180  float chiDOFPrev = 0;
1181  short nHiChi = 0;
1182  for (unsigned short nit = 0; nit < 10; ++nit) {
1183  // Decide how many points to add or subtract after doing the fit
1184  unsigned short nPtsNext = nPts;
1185  if (!FitTP3Ds(detProp, slc, pfp, fromPt, nPts, USHRT_MAX, chiDOF)) {
1186  nPtsNext += 1.5 * nPtsToAdd;
1187  }
1188  else if (chiDOF < chiLo) {
1189  // low chiDOF
1190  if (nHiChi > 2) {
1191  // declare it close enough if several attempts were made
1192  nPtsNext = 0;
1193  }
1194  else {
1195  nPtsNext += nPtsToAdd;
1196  } // nHiChi < 2
1197  nHiChi = 0;
1198  }
1199  else if (chiDOF > chiHi) {
1200  // high chiDOF
1201  ++nHiChi;
1202  if (nHiChi == 1 && chiDOFPrev > tcc.match3DCuts[5]) {
1203  // reduce the number of points by 1/2 on the first attempt
1204  nPtsNext /= 2;
1205  }
1206  else {
1207  // that didn't work so start subtracting groups of points
1208  short npnext = (short)nPts - nHiChi * 5;
1209  // assume this won't work
1210  nPtsNext = 0;
1211  if (npnext > nPtsMin) nPtsNext = npnext;
1212  }
1213  }
1214  else {
1215  // just right
1216  nPtsNext = 0;
1217  }
1218  // check for passing the end
1219  if (fromPt + nPtsNext >= pfp.TP3Ds.size()) {
1220  nPtsNext = pfp.TP3Ds.size() - fromPt;
1221  lastSection = true;
1222  }
1223  if (prt) {
1224  mf::LogVerbatim myprt("TC");
1225  myprt << " RS: P" << pfp.ID << " sfi/nit/npts " << sfIndex << "/" << nit << "/" << nPts;
1226  myprt << std::fixed << std::setprecision(1) << " chiDOF " << chiDOF;
1227  myprt << " fromPt " << fromPt;
1228  myprt << " nPtsNext " << nPtsNext;
1229  myprt << " nHiChi " << nHiChi;
1230  myprt << " lastSection? " << lastSection;
1231  }
1232  if (nPtsNext == 0) break;
1233  // see if this is the last section
1234  if (lastSection) break;
1235  if (chiDOF == chiDOFPrev) {
1236  if (prt) mf::LogVerbatim("TC") << " MVI " << pfp.MVI << " chiDOF not changing\n";
1237  break;
1238  }
1239  nPts = nPtsNext;
1240  chiDOFPrev = chiDOF;
1241  } // nit
1242  // finished this section. Assign the points to it
1243  unsigned short toPt = fromPt + nPts;
1244  if (toPt > pfp.TP3Ds.size()) toPt = pfp.TP3Ds.size();
1245  for (unsigned short ipt = fromPt; ipt < toPt; ++ipt)
1246  pfp.TP3Ds[ipt].SFIndex = sfIndex;
1247  // See if there are enough points remaining to reconstruct another section if this isn't known
1248  // to be the last section
1249  if (!lastSection) {
1250  // this will be the first point in the next section
1251  unsigned short nextFromPt = fromPt + nPts;
1252  // See if it will have enough points to be reconstructed
1253  unsigned short nextToPtMin = Find3DRecoRange(slc, pfp, nextFromPt, min2DPts, 1);
1254  if (nextToPtMin == USHRT_MAX) {
1255  // not enough points so this is the last section
1256  lastSection = true;
1257  // assign the remaining points to the last section
1258  for (std::size_t ipt = nextFromPt; ipt < pfp.TP3Ds.size(); ++ipt)
1259  pfp.TP3Ds[ipt].SFIndex = sfIndex;
1260  }
1261  } // !lastSection
1262  // Do a final fit and update the points. Don't worry about a poor ChiDOF
1263  FitSection(clockData, detProp, slc, pfp, sfIndex);
1264  if (!SortSection(pfp, 0)) { return false; }
1265  if (lastSection) break;
1266  // Prepare for the next section.
1267  fromPt = fromPt + nPts;
1268  nPts = nPtsToAdd;
1269  nPtsMin = Find3DRecoRange(slc, pfp, fromPt, min2DPts, 1) - fromPt + 1;
1270  if (nPtsMin >= pfp.TP3Ds.size()) break;
1271  // add a new section
1272  pfp.SectionFits.resize(pfp.SectionFits.size() + 1);
1273  } // snit
1274 
1275  // see if the last sf is valid
1276  if (pfp.SectionFits.size() > 1 && pfp.SectionFits.back().ChiDOF < 0) {
1277  unsigned short badSFI = pfp.SectionFits.size() - 1;
1278  // remove it
1279  pfp.SectionFits.pop_back();
1280  for (std::size_t ipt = pfp.TP3Ds.size() - 1; ipt > 0; --ipt) {
1281  auto& tp3d = pfp.TP3Ds[ipt];
1282  if (tp3d.SFIndex < badSFI) break;
1283  --tp3d.SFIndex;
1284  }
1285  pfp.SectionFits.back().NeedsUpdate = true;
1286  } // bad last SF
1287 
1288  // Ensure that the points at the end are in the last section
1289  for (std::size_t ipt = pfp.TP3Ds.size() - 1; ipt > 0; --ipt) {
1290  auto& tp3d = pfp.TP3Ds[ipt];
1291  if (tp3d.SFIndex < pfp.SectionFits.size()) break;
1292  tp3d.SFIndex = pfp.SectionFits.size() - 1;
1293  pfp.Flags[kNeedsUpdate] = true;
1294  pfp.SectionFits[tp3d.SFIndex].NeedsUpdate = true;
1295  } // tp3d
1296 
1297  Update(clockData, detProp, slc, pfp, prt);
1298 
1299  // set CanSection false if the chisq is poor in any section
1300  for (auto& sf : pfp.SectionFits) {
1301  if (sf.ChiDOF > tcc.match3DCuts[5]) pfp.Flags[kCanSection] = false;
1302  }
1303 
1304  return true;
1305  } // resection
1306 
1307  /////////////////////////////////////////
1308  void
1310  const PFPStruct& pfp,
1311  unsigned short fromPt,
1312  unsigned short toPt,
1313  unsigned short& nBadPts,
1314  unsigned short& firstBadPt)
1315  {
1316  // Count the number of points whose pull exceeds tcc.match3DCuts[4]
1317  firstBadPt = USHRT_MAX;
1318  nBadPts = 0;
1319  if (fromPt > pfp.TP3Ds.size() - 1) {
1320  nBadPts = USHRT_MAX;
1321  return;
1322  }
1323  if (toPt > pfp.TP3Ds.size()) toPt = pfp.TP3Ds.size();
1324  bool first = true;
1325  for (unsigned short ipt = fromPt; ipt < toPt; ++ipt) {
1326  auto& tp3d = pfp.TP3Ds[ipt];
1327  if (!tp3d.Flags[kTP3DGood]) continue;
1328  // don't clobber a point if it is on a TP that is overlapping another Tj. This will
1329  // happen for points close to a vertex and when trajectories cross
1330  auto& tp = slc.tjs[tp3d.TjID - 1].Pts[tp3d.TPIndex];
1331  if (tp.Environment[kEnvOverlap]) continue;
1332  if (PointPull(pfp, tp3d) < tcc.match3DCuts[4]) continue;
1333  ++nBadPts;
1334  if (first) {
1335  first = false;
1336  firstBadPt = ipt;
1337  }
1338  } // ipt
1339  } // CountBadPoints
1340 
1341  /////////////////////////////////////////
1342  bool
1343  CanSection(const TCSlice& slc, const PFPStruct& pfp)
1344  {
1345  // analyze the TP3D vector to determine if it can be reconstructed in 3D in more than one section with
1346  // the requirement that there are at least 3 points in two planes
1347  if (pfp.AlgMod[kJunk3D]) return false;
1348  if (pfp.AlgMod[kSmallAngle]) return false;
1349  if (pfp.TP3Ds.size() < 12) return false;
1350  unsigned short toPt = Find3DRecoRange(slc, pfp, 0, 3, 1);
1351  if (toPt > pfp.TP3Ds.size()) return false;
1352  unsigned short nextToPt = Find3DRecoRange(slc, pfp, toPt, 3, 1);
1353  if (nextToPt > pfp.TP3Ds.size()) return false;
1354  return true;
1355  } // CanSection
1356 
1357  /////////////////////////////////////////
1358  unsigned short
1360  const PFPStruct& pfp,
1361  unsigned short fromPt,
1362  unsigned short min2DPts,
1363  short dir)
1364  {
1365  // Scans the TP3Ds vector starting at fromPt until it finds min2DPts in two planes. It returns
1366  // with the index of that point (+1) in the TP3Ds vector. The dir variable defines the scan direction in
1367  // the TP3Ds vector
1368  if (fromPt > pfp.TP3Ds.size() - 1) return USHRT_MAX;
1369  if (pfp.TP3Ds.size() < 2 * min2DPts) return USHRT_MAX;
1370  if (dir == 0) return USHRT_MAX;
1371 
1372  std::vector<unsigned short> cntInPln(slc.nPlanes);
1373  for (std::size_t ii = 0; ii < pfp.TP3Ds.size(); ++ii) {
1374  unsigned short ipt = fromPt + ii;
1375  if (dir < 0) ipt = fromPt - ii;
1376  if (ipt >= pfp.TP3Ds.size()) break;
1377  auto& tp3d = pfp.TP3Ds[ipt];
1378  if (!tp3d.Flags[kTP3DGood]) continue;
1379  unsigned short plane = DecodeCTP(slc.tjs[tp3d.TjID - 1].CTP).Plane;
1380  ++cntInPln[plane];
1381  unsigned short enufInPlane = 0;
1382  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane)
1383  if (cntInPln[plane] >= min2DPts) ++enufInPlane;
1384  if (enufInPlane > 1) return ipt + 1;
1385  if (dir < 0 && ipt == 0) break;
1386  } // ipt
1387  return USHRT_MAX;
1388  } // Find3DRecoRange
1389 
1390  /////////////////////////////////////////
1391  void
1392  GetRange(const PFPStruct& pfp,
1393  unsigned short sfIndex,
1394  unsigned short& fromPt,
1395  unsigned short& npts)
1396  {
1397  fromPt = USHRT_MAX;
1398  if (sfIndex >= pfp.SectionFits.size()) return;
1399  if (pfp.TP3Ds.empty()) return;
1400  fromPt = USHRT_MAX;
1401  npts = 0;
1402  // Note that no test is made for not-good TP3Ds here since that would give a wrong npts count
1403  for (std::size_t ipt = 0; ipt < pfp.TP3Ds.size(); ++ipt) {
1404  auto& tp3d = pfp.TP3Ds[ipt];
1405  if (tp3d.SFIndex < sfIndex) continue;
1406  if (tp3d.SFIndex > sfIndex) break;
1407  if (fromPt == USHRT_MAX) fromPt = ipt;
1408  ++npts;
1409  } // ipt
1410  } // GetRange
1411 
1412  /////////////////////////////////////////
1413  bool
1416  const TCSlice& slc,
1417  PFPStruct& pfp,
1418  unsigned short sfIndex)
1419  {
1420  // Fits the TP3D points in the selected section to a 3D line with the origin at the center of
1421  // the section
1422  if (pfp.TP3Ds.size() < 4) return false;
1423  if (sfIndex >= pfp.SectionFits.size()) return false;
1424  // don't fit a small angle PFP
1425  if(pfp.Flags[kSmallAngle]) return true;
1426 
1427  unsigned short fromPt = USHRT_MAX;
1428  unsigned short npts = 0;
1429  GetRange(pfp, sfIndex, fromPt, npts);
1430  if (fromPt == USHRT_MAX) return false;
1431  if (npts < 4) return false;
1432 
1433  // check for errors
1434  for (unsigned short ipt = fromPt; ipt < fromPt + npts; ++ipt) {
1435  auto& tp3d = pfp.TP3Ds[ipt];
1436  if (tp3d.SFIndex != sfIndex) return false;
1437  } // ipt
1438 
1439  // fit these points and update
1440  float chiDOF = 999;
1441  return FitTP3Ds(detProp, slc, pfp, fromPt, npts, sfIndex, chiDOF);
1442 
1443  } // FitSection
1444 
1445  /////////////////////////////////////////
1446  SectionFit
1448  const TCSlice& slc,
1449  const std::vector<TP3D>& tp3ds,
1450  unsigned short fromPt,
1451  short fitDir,
1452  unsigned short nPtsFit)
1453  {
1454  // fits the points and returns the fit results in a SectionFit struct. This function assumes that the
1455  // vector of TP3Ds exists in the slc.TPCID
1456 
1457  SectionFit sf;
1458  sf.ChiDOF = 999;
1459  if (nPtsFit < 5) return sf;
1460  if (!(fitDir == -1 || fitDir == 1)) return sf;
1461  if (fitDir == 1 && fromPt + nPtsFit > tp3ds.size()) return sf;
1462  if (fitDir == -1 && fromPt < 3) return sf;
1463 
1464  // put the offset, cosine-like and sine-like components in a vector
1465  std::vector<std::array<double, 3>> ocs(slc.nPlanes);
1466  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
1467  auto planeID = geo::PlaneID(slc.TPCID.Cryostat, slc.TPCID.TPC, plane);
1468  // plane offset
1469  ocs[plane][0] = tcc.geom->WireCoordinate(0, 0, planeID);
1470  // get the "cosine-like" component
1471  ocs[plane][1] = tcc.geom->WireCoordinate(1, 0, planeID) - ocs[plane][0];
1472  // the "sine-like" component
1473  ocs[plane][2] = tcc.geom->WireCoordinate(0, 1, planeID) - ocs[plane][0];
1474  } // plane
1475 
1476  const unsigned int nvars = 4;
1477  unsigned int npts = 0;
1478 
1479  // count the number of TPs in each plane
1480  std::vector<unsigned short> cntInPln(slc.nPlanes, 0);
1481  // and define the X position for the fit origin
1482  double x0 = 0.;
1483  for (short ii = 0; ii < nPtsFit; ++ii) {
1484  short ipt = fromPt + fitDir * ii;
1485  if (ipt < 0 || ipt >= (short)tp3ds.size()) break;
1486  auto& tp3d = tp3ds[ipt];
1487  if (!tp3d.Flags[kTP3DGood]) continue;
1488  if (tp3d.TPXErr2 < 0.0001) return sf;
1489  x0 += tp3d.TPX;
1490  unsigned short plane = DecodeCTP(tp3d.CTP).Plane;
1491  ++cntInPln[plane];
1492  ++npts;
1493  } // ipt
1494  if (npts < 6) return sf;
1495  // ensure there are at least three points in at least two planes
1496  unsigned short enufInPlane = 0;
1497  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane)
1498  if (cntInPln[plane] > 2) ++enufInPlane;
1499  if (enufInPlane < 2) return sf;
1500 
1501  x0 /= (double)npts;
1502 
1503  TMatrixD A(npts, nvars);
1504  // vector holding the Wire number
1505  TVectorD w(npts);
1506  unsigned short cnt = 0;
1507  double weight = 1;
1508  for (short ii = 0; ii < nPtsFit; ++ii) {
1509  short ipt = fromPt + fitDir * ii;
1510  auto& tp3d = tp3ds[ipt];
1511  if (!tp3d.Flags[kTP3DGood]) continue;
1512  unsigned short plane = DecodeCTP(tp3d.CTP).Plane;
1513  double x = tp3d.TPX - x0;
1514  A[cnt][0] = weight * ocs[plane][1];
1515  A[cnt][1] = weight * ocs[plane][2];
1516  A[cnt][2] = weight * ocs[plane][1] * x;
1517  A[cnt][3] = weight * ocs[plane][2] * x;
1518  w[cnt] = weight * (tp3d.Wire - ocs[plane][0]);
1519  ++cnt;
1520  } // ipt
1521 
1522  TDecompSVD svd(A);
1523  bool ok;
1524  TVectorD tVec = svd.Solve(w, ok);
1525  if (!ok) return sf;
1526  double norm = sqrt(1 + tVec[2] * tVec[2] + tVec[3] * tVec[3]);
1527 
1528  norm *= -1;
1529 
1530  sf.Dir[0] = 1 / norm;
1531  sf.Dir[1] = tVec[2] / norm;
1532  sf.Dir[2] = tVec[3] / norm;
1533  sf.Pos[0] = x0;
1534  sf.Pos[1] = tVec[0];
1535  sf.Pos[2] = tVec[1];
1536  sf.NPts = npts;
1537 
1538  // Calculate errors from sigma * (A^T * A)^(-1) where sigma is the
1539  // error on the wire number (= 1)
1540  TMatrixD AT(nvars, npts);
1541  AT.Transpose(A);
1542  TMatrixD ATA = AT * A;
1543  double* det = 0;
1544  try{ ATA.Invert(det); }
1545  catch(...) { return sf; }
1546  sf.DirErr[1] = -sqrt(ATA[2][2]) / norm;
1547  sf.DirErr[2] = -sqrt(ATA[3][3]) / norm;
1548 
1549  // calculate ChiDOF
1550  sf.ChiDOF = 0;
1551  // project this 3D vector into a TP in every plane
1552  std::vector<TrajPoint> plnTP(slc.nPlanes);
1553  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
1554  CTP_t inCTP = EncodeCTP(slc.TPCID.Cryostat, slc.TPCID.TPC, plane);
1555  plnTP[plane] = MakeBareTP(detProp, slc, sf.Pos, sf.Dir, inCTP);
1556  } // plane
1557  // a local position
1558  Point3_t pos;
1559  sf.DirErr[0] = 0.;
1560  for (short ii = 0; ii < nPtsFit; ++ii) {
1561  short ipt = fromPt + fitDir * ii;
1562  auto& tp3d = tp3ds[ipt];
1563  if (!tp3d.Flags[kTP3DGood]) continue;
1564  unsigned short plane = DecodeCTP(tp3d.CTP).Plane;
1565  double dw = tp3d.Wire - plnTP[plane].Pos[0];
1566  // dt/dW was stored in DeltaRMS by MakeBareTP
1567  double t = dw * plnTP[plane].DeltaRMS;
1568  for (unsigned short xyz = 0; xyz < 3; ++xyz)
1569  pos[xyz] = sf.Pos[xyz] + t * sf.Dir[xyz];
1570  // Note that the tp3d position is directly above the wire position and not the
1571  // point at the distance of closest approach. Delta is the difference in the
1572  // drift direction in cm
1573  double delta = pos[0] - tp3d.TPX;
1574  sf.ChiDOF += delta * delta / tp3d.TPXErr2;
1575  // estimate the X slope error ~ X direction vector with an overly simple average
1576  double dangErr = delta / dw;
1577  sf.DirErr[0] += dangErr * dangErr;
1578  } // indx
1579  sf.DirErr[0] = sqrt(sf.DirErr[0]) / (double)nPtsFit;
1580  sf.ChiDOF /= (float)(npts - 4);
1581  return sf;
1582 
1583  } // FitTP3Ds
1584 
1585  /////////////////////////////////////////
1586  bool
1588  const TCSlice& slc,
1589  PFPStruct& pfp,
1590  unsigned short fromPt,
1591  unsigned short nPtsFit,
1592  unsigned short sfIndex,
1593  float& chiDOF)
1594  {
1595  // Fit points in the pfp.TP3Ds vector fromPt. This function
1596  // doesn't update the TP3Ds unless sfIndex refers to a valid SectionFit in the pfp.
1597  // No check is made to ensure that the TP3D SFIndex variable is compatible with sfIndex
1598 
1599  if (nPtsFit < 5) return false;
1600  if (fromPt + nPtsFit > pfp.TP3Ds.size()) return false;
1601 
1602  auto sf = FitTP3Ds(detProp, slc, pfp.TP3Ds, fromPt, 1, nPtsFit);
1603  if (sf.ChiDOF > 900) return false;
1604 
1605  // don't update the pfp?
1606  if (sfIndex >= pfp.SectionFits.size()) return true;
1607 
1608  // update the pfp Sectionfit
1609  pfp.SectionFits[sfIndex] = sf;
1610  // update the TP3Ds
1611  // project this 3D vector into a TP in every plane
1612  std::vector<TrajPoint> plnTP(slc.nPlanes);
1613  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
1614  CTP_t inCTP = EncodeCTP(pfp.TPCID.Cryostat, pfp.TPCID.TPC, plane);
1615  plnTP[plane] = MakeBareTP(detProp, slc, sf.Pos, sf.Dir, inCTP);
1616  } // plane
1617  Point3_t pos;
1618  bool needsSort = false;
1619  double prevAlong = 0;
1620  for (unsigned short ipt = fromPt; ipt < fromPt + nPtsFit; ++ipt) {
1621  auto& tp3d = pfp.TP3Ds[ipt];
1622  unsigned short plane = DecodeCTP(tp3d.CTP).Plane;
1623  double dw = tp3d.Wire - plnTP[plane].Pos[0];
1624  // dt/dW was stored in DeltaRMS by MakeBareTP
1625  double t = dw * plnTP[plane].DeltaRMS;
1626  if (ipt == fromPt) { prevAlong = t; }
1627  else {
1628  if (t < prevAlong) needsSort = true;
1629  prevAlong = t;
1630  }
1631  for (unsigned short xyz = 0; xyz < 3; ++xyz)
1632  pos[xyz] = sf.Pos[xyz] + t * sf.Dir[xyz];
1633  // Note that the tp3d position is directly above the wire position and not the
1634  // distance of closest approach. The Delta variable is the difference in the
1635  // drift direction in cm
1636  double delta = pos[0] - tp3d.TPX;
1637  tp3d.Pos = pos;
1638  tp3d.Dir = sf.Dir;
1639  tp3d.along = t;
1640  if (tp3d.Flags[kTP3DGood]) sf.ChiDOF += delta * delta / tp3d.TPXErr2;
1641  } // ipt
1642  if (needsSort) SortSection(pfp, sfIndex);
1643  pfp.SectionFits[sfIndex].NeedsUpdate = false;
1644  return true;
1645 
1646  } // FitTP3Ds
1647 
1648  /////////////////////////////////////////
1649  void
1650  ReconcileVertices(TCSlice& slc, PFPStruct& pfp, bool prt)
1651  {
1652  // Checks for mis-placed 2D and 3D vertices and either attaches them
1653  // to a vertex or deletes(?) the vertex while attempting to preserve or
1654  // correct the P -> T -> 2V -> 3V assn. After this is done, the function
1655  // TCVertex/AttachToAnyVertex is called.
1656  // This function returns true if something was done to the pfp that requires
1657  // a re-definition of the pfp, e.g. adding or removing TP3Ds. Note that this
1658  // never occurs as the function is currently written
1659 
1660  if (tcc.vtx3DCuts.size() < 3) return;
1661  if (pfp.TP3Ds.empty()) return;
1662  if (pfp.Flags[kJunk3D]) return;
1663  if(pfp.Flags[kSmallAngle]) return;
1664 
1665  // first make a list of all Tjs
1666  std::vector<int> tjList;
1667  for (auto& tp3d : pfp.TP3Ds) {
1668  if (!tp3d.Flags[kTP3DGood]) continue;
1669  // ignore single hits
1670  if (tp3d.TjID <= 0) continue;
1671  if (std::find(tjList.begin(), tjList.end(), tp3d.TjID) == tjList.end())
1672  tjList.push_back(tp3d.TjID);
1673  } // tp3d
1674  // look for 3D vertices associated with these Tjs and list of
1675  // orphan 2D vertices - those that are not matched to 3D vertices
1676  std::vector<int> vx2List, vx3List;
1677  for (auto tid : tjList) {
1678  auto& tj = slc.tjs[tid - 1];
1679  for (unsigned short end = 0; end < 2; ++end) {
1680  if (tj.VtxID[end] <= 0) continue;
1681  auto& vx2 = slc.vtxs[tj.VtxID[end] - 1];
1682  if (vx2.Vx3ID > 0) {
1683  if (std::find(vx3List.begin(), vx3List.end(), vx2.Vx3ID) == vx3List.end())
1684  vx3List.push_back(vx2.Vx3ID);
1685  // 3D vertex exists
1686  }
1687  else {
1688  // no 3D vertex
1689  if (std::find(vx2List.begin(), vx2List.end(), tj.VtxID[end]) == vx2List.end())
1690  vx2List.push_back(tj.VtxID[end]);
1691  } // no 3D vertex
1692  } // end
1693  } // tid
1694  // no vertex reconciliation is necessary
1695  if (vx2List.empty() && vx3List.empty()) return;
1696  if (prt) {
1697  mf::LogVerbatim myprt("TC");
1698  myprt << "RV: P" << pfp.ID << " ->";
1699  for (auto tid : tjList)
1700  myprt << " T" << tid;
1701  myprt << " ->";
1702  for (auto vid : vx3List)
1703  myprt << " 3V" << vid;
1704  if (!vx2List.empty()) {
1705  myprt << " orphan";
1706  for (auto vid : vx2List)
1707  myprt << " 2V" << vid;
1708  }
1709  } // prt
1710  // Just kill the orphan 2D vertices regardless of their score.
1711  // This is an indicator that the vertex was created between two tjs
1712  // that maybe should have been reconstructed as one or alternatively
1713  // as two Tjs. This decision presumes the existence of a 3D kink
1714  // algorithm that doesn't yet exist...
1715  for (auto vid : vx2List) {
1716  auto& vx2 = slc.vtxs[vid - 1];
1717  MakeVertexObsolete("RV", slc, vx2, true);
1718  } // vx2List
1719  // ignore the T -> 2V -> 3V assns (if any exist) and try to directly
1720  // attach to 3D vertices at both ends
1721  AttachToAnyVertex(slc, pfp, tcc.vtx3DCuts[2], prt);
1722  // check for differences and while we are here, see if the pfp was attached
1723  // to a neutrino vertex and the direction is wrong
1724  int neutrinoVx = 0;
1725  if (!slc.pfps.empty()) {
1726  auto& npfp = slc.pfps[0];
1727  bool neutrinoPFP = (npfp.PDGCode == 12 || npfp.PDGCode == 14);
1728  if (neutrinoPFP) neutrinoVx = npfp.Vx3ID[0];
1729  } // pfps exist
1730  unsigned short neutrinoVxEnd = 2;
1731  for (unsigned short end = 0; end < 2; ++end) {
1732  // see if a vertex got attached
1733  if (pfp.Vx3ID[end] <= 0) continue;
1734  if (pfp.Vx3ID[end] == neutrinoVx) neutrinoVxEnd = end;
1735  // see if this is a vertex in the list using the T -> 2V -> 3V assns
1736  if (std::find(vx3List.begin(), vx3List.end(), pfp.Vx3ID[end]) != vx3List.end()) continue;
1737  } // end
1738  if (neutrinoVxEnd < 2 && neutrinoVxEnd != 0) Reverse(slc, pfp);
1739 
1740  return;
1741  } // ReconcileVertices
1742 
1743  /////////////////////////////////////////
1744  void
1747  TCSlice& slc,
1748  PFPStruct& pfp,
1749  bool prt)
1750  {
1751  // Look for gaps in each plane in the TP3Ds vector in planes in which
1752  // the projection of the pfp angle is large (~> 60 degrees). Hits
1753  // reconstructed at large angles are poorly reconstructed which results
1754  // in poorly reconstructed 2D trajectories
1755 
1756  if (pfp.ID <= 0) return;
1757  if (pfp.TP3Ds.empty()) return;
1758  if (pfp.SectionFits.empty()) return;
1759  if (!tcc.useAlg[kFillGaps3D]) return;
1760  if (pfp.Flags[kJunk3D]) return;
1761  if (pfp.Flags[kSmallAngle]) return;
1762 
1763  // Only print APIR details if MVI is set
1764  bool foundMVI = (tcc.dbgPFP && pfp.MVI == debug.MVI);
1765 
1766  // make a copy in case something goes wrong
1767  auto pWork = pfp;
1768 
1769  unsigned short nPtsAdded = 0;
1770  unsigned short fromPt = 0;
1771  unsigned short toPt = pWork.TP3Ds.size();
1772  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
1773  CTP_t inCTP = EncodeCTP(pWork.TPCID.Cryostat, pWork.TPCID.TPC, plane);
1774  unsigned short nWires, nAdd;
1775  AddPointsInRange(clockData,
1776  detProp,
1777  slc,
1778  pWork,
1779  fromPt,
1780  toPt,
1781  inCTP,
1782  tcc.match3DCuts[4],
1783  nWires,
1784  nAdd,
1785  foundMVI);
1786  if (pWork.Flags[kNeedsUpdate]) Update(clockData, detProp, slc, pWork, prt);
1787  nPtsAdded += nAdd;
1788  } // plane
1789  if (prt) mf::LogVerbatim("TC") << "FG3D P" << pWork.ID << " added " << nPtsAdded << " points";
1790  if (pWork.Flags[kNeedsUpdate] && !Update(clockData, detProp, slc, pWork, prt)) return;
1791  pfp = pWork;
1792  return;
1793  } // FillGaps3D
1794 
1795  /////////////////////////////////////////
1796  bool
1798  const TCSlice& slc,
1799  const PFPStruct& pfp)
1800  {
1801  // This function checks the third plane in the PFP when only two Tjs are 3D-matched to
1802  // ensure that the reason for the lack of a 3rd plane match is that it is in a dead region.
1803  // This function should be used after an initial fit is done and the TP3Ds are sorted
1804  if (pfp.TjIDs.size() != 2) return false;
1805  if (slc.nPlanes != 3) return false;
1806  if (pfp.TP3Ds.empty()) return false;
1807 
1808  // find the third plane
1809  std::vector<unsigned short> planes;
1810  for (auto tid : pfp.TjIDs)
1811  planes.push_back(DecodeCTP(slc.tjs[tid - 1].CTP).Plane);
1812  unsigned short thirdPlane = 3 - planes[0] - planes[1];
1813  CTP_t inCTP = EncodeCTP(slc.TPCID.Cryostat, slc.TPCID.TPC, thirdPlane);
1814  // Project the 3D position at the start into the third plane
1815  auto tp = MakeBareTP(detProp, slc, pfp.TP3Ds[0].Pos, inCTP);
1816  unsigned int wire0 = 0;
1817  if (tp.Pos[0] > 0) wire0 = std::nearbyint(tp.Pos[0]);
1818  if (wire0 > slc.nWires[thirdPlane]) wire0 = slc.nWires[thirdPlane];
1819  // Do the same for the end
1820  unsigned short lastPt = pfp.TP3Ds.size() - 1;
1821  tp = MakeBareTP(detProp, slc, pfp.TP3Ds[lastPt].Pos, inCTP);
1822  unsigned int wire1 = 0;
1823  if (tp.Pos[0] > 0) wire1 = std::nearbyint(tp.Pos[0]);
1824  if (wire1 > slc.nWires[thirdPlane]) wire1 = slc.nWires[thirdPlane];
1825  if (wire0 == wire1) return !evt.goodWire[thirdPlane][wire0];
1826  if (wire1 < wire0) std::swap(wire0, wire1);
1827  // count the number of good wires
1828  int dead = 0;
1829  int wires = wire1 - wire0;
1830  for (unsigned int wire = wire0; wire < wire1; ++wire)
1831  if (!evt.goodWire[thirdPlane][wire]) ++dead;
1832  // require that most of the wires are dead
1833  return (dead > 0.8 * wires);
1834  } // ValidTwoPlaneMatch
1835 
1836  /////////////////////////////////////////
1837  void
1840  TCSlice& slc,
1841  PFPStruct& pfp,
1842  unsigned short fromPt,
1843  unsigned short toPt,
1844  CTP_t inCTP,
1845  float maxPull,
1846  unsigned short& nWires,
1847  unsigned short& nAdd,
1848  bool prt)
1849  {
1850  // Try to insert 2D trajectory points into the 3D trajectory point vector pfp.TP3Ds.
1851  // This function inserts new TP3Ds and sets the NeedsUpdate flags true.
1852  // The calling function should call Update
1853  // Note that maxPull is used for the charge pull as well as the position pull
1854  nWires = 0;
1855  nAdd = 0;
1856  if (fromPt > toPt) return;
1857  if (toPt >= pfp.TP3Ds.size()) toPt = pfp.TP3Ds.size() - 1;
1858 
1859  // Find the average dE/dx so we can apply a generous min/max dE/dx cut
1860  float dEdXAve = 0;
1861  float dEdXRms = 0;
1862  Average_dEdX(clockData, detProp, slc, pfp, dEdXAve, dEdXRms);
1863  float dEdxMin = 0.5, dEdxMax = 50.;
1864  if (dEdXAve > 0.5) {
1865  dEdxMin = dEdXAve - maxPull * dEdXRms;
1866  if (dEdxMin < 0.5) dEdxMin = 0.5;
1867  dEdxMax = dEdXAve + maxPull * dEdXRms;
1868  if (dEdxMax > 50.) dEdxMax = 50.;
1869  } // dEdXAve > 0.5
1870 
1871  // Split the range into sub-ranges; one for each SectionFit and make 2D TPs at the
1872  // start of each sub-range.
1873  std::vector<TrajPoint> sfTPs;
1874  // form a list of TPs that are used in this CTP
1875  std::vector<std::pair<int, unsigned short>> tpUsed;
1876  for (auto& tp3d : pfp.TP3Ds) {
1877  if (tp3d.CTP != inCTP) continue;
1878  tpUsed.push_back(std::make_pair(tp3d.TjID, tp3d.TPIndex));
1879  } // tp3d
1880  unsigned int toWire = 0;
1881  unsigned int fromWire = UINT_MAX;
1882 
1883  unsigned short inSF = USHRT_MAX;
1884  unsigned short pln = DecodeCTP(inCTP).Plane;
1885  for (unsigned short ipt = 0; ipt < pfp.TP3Ds.size(); ++ipt) {
1886  auto& tp3d = pfp.TP3Ds[ipt];
1887  // Skip if not the last point and we already found a point in this SectionFit
1888  if (ipt < pfp.TP3Ds.size() - 1 && tp3d.SFIndex == inSF) continue;
1889  unsigned int wire;
1890  if (tp3d.CTP == inCTP) {
1891  // Found the first tp3d in a new SectionFit and it is in the right CTP
1892  auto& tp = slc.tjs[tp3d.TjID - 1].Pts[tp3d.TPIndex];
1893  sfTPs.push_back(tp);
1894  wire = std::nearbyint(tp.Pos[0]);
1895  if (wire >= slc.nWires[pln]) break;
1896  }
1897  else {
1898  // Found the first tp3d in a new SectionFit and it is in a different CTP.
1899  // Make a TP in the right CTP
1900  auto tp = MakeBareTP(detProp, slc, tp3d.Pos, tp3d.Dir, inCTP);
1901  wire = std::nearbyint(tp.Pos[0]);
1902  if (wire >= slc.nWires[pln]) break;
1903  sfTPs.push_back(tp);
1904  }
1905  if (wire < fromWire) fromWire = wire;
1906  if (wire > toWire) toWire = wire;
1907  inSF = tp3d.SFIndex;
1908  } // tp3d
1909  if (sfTPs.empty()) return;
1910  // reverse the vector if necessary so the wires will be in increasing order
1911  if (sfTPs.size() > 1 && sfTPs[0].Pos[0] > sfTPs.back().Pos[0]) {
1912  std::reverse(sfTPs.begin(), sfTPs.end());
1913  }
1914 
1915  // set a generous search window in WSE units
1916  float window = 50;
1917 
1918  if (prt)
1919  mf::LogVerbatim("TC") << "APIR: inCTP " << inCTP << " fromWire " << fromWire << " toWire "
1920  << toWire;
1921 
1922  // iterate over the sub-ranges
1923  for (unsigned short subr = 0; subr < sfTPs.size(); ++subr) {
1924  auto& fromTP = sfTPs[subr];
1925  unsigned int toWireInSF = toWire;
1926  if (subr < sfTPs.size() - 1) toWireInSF = std::nearbyint(sfTPs[subr + 1].Pos[0]);
1927  SetAngleCode(fromTP);
1928  unsigned int fromWire = std::nearbyint(fromTP.Pos[0]);
1929  if (fromWire > toWire) continue;
1930  if (prt)
1931  mf::LogVerbatim("TC") << " inCTP " << inCTP << " subr " << subr << " fromWire " << fromWire
1932  << " toWireInSF " << toWireInSF;
1933  for (unsigned int wire = fromWire; wire <= toWireInSF; ++wire) {
1934  MoveTPToWire(fromTP, (float)wire);
1935  if (!FindCloseHits(slc, fromTP, window, kUsedHits)) continue;
1936  if (fromTP.Environment[kEnvNotGoodWire]) continue;
1937  float bestPull = maxPull;
1938  TP3D bestTP3D;
1939  for (auto iht : fromTP.Hits) {
1940  if (slc.slHits[iht].InTraj <= 0) continue;
1941  // this hit is used in a TP so find the tpIndex
1942  auto& utj = slc.tjs[slc.slHits[iht].InTraj - 1];
1943  unsigned short tpIndex = 0;
1944  for (tpIndex = utj.EndPt[0]; tpIndex <= utj.EndPt[1]; ++tpIndex) {
1945  auto& utp = utj.Pts[tpIndex];
1946  if (utp.Chg <= 0) continue;
1947  // This doesn't check for UseHit true but that is probably ok here
1948  if (std::find(utp.Hits.begin(), utp.Hits.end(), iht) != utp.Hits.end()) break;
1949  } // ipt
1950  if (tpIndex > utj.EndPt[1]) continue;
1951  // see if it is already used in this pfp
1952  std::pair<int, unsigned short> tppr = std::make_pair(utj.ID, tpIndex);
1953  if (std::find(tpUsed.begin(), tpUsed.end(), tppr) != tpUsed.end()) continue;
1954  tpUsed.push_back(tppr);
1955  auto& utp = utj.Pts[tpIndex];
1956  // see if it is used in a different PFP
1957  if (utp.InPFP > 0) continue;
1958  // or if it overlaps another trajectory near a 2D vertex
1959  if (utp.Environment[kEnvOverlap]) continue;
1960  auto newTP3D = CreateTP3D(detProp, slc, utj.ID, tpIndex);
1961  if (!SetSection(detProp, slc, pfp, newTP3D)) continue;
1962  // set the direction to the direction of the SectionFit it is in so we can calculate dE/dx
1963  newTP3D.Dir = pfp.SectionFits[newTP3D.SFIndex].Dir;
1964  float pull = PointPull(pfp, newTP3D);
1965  float dedx = dEdx(clockData, detProp, slc, newTP3D);
1966  // Require a good pull and a consistent dE/dx (MeV/cm)
1967  bool useIt = (pull < bestPull && dedx > dEdxMin && dedx < dEdxMax);
1968  if (!useIt) continue;
1969  bestTP3D = newTP3D;
1970  bestPull = pull;
1971  } // iht
1972  if (bestPull < maxPull) {
1973  if (prt && bestPull < 10) {
1974  mf::LogVerbatim myprt("TC");
1975  auto& tp = slc.tjs[bestTP3D.TjID - 1].Pts[bestTP3D.TPIndex];
1976  myprt << "APIR: P" << pfp.ID << " added TP " << PrintPos(slc, tp);
1977  myprt << " pull " << std::fixed << std::setprecision(2) << bestPull;
1978  myprt << " dx " << bestTP3D.TPX - bestTP3D.Pos[0] << " in section " << bestTP3D.SFIndex;
1979  }
1980  if (InsertTP3D(pfp, bestTP3D) == USHRT_MAX) continue;
1981  ++nAdd;
1982  } // bestPull < maxPull
1983  } // wire
1984  } // subr
1985  } // AddPointsInRange
1986 
1987  /////////////////////////////////////////
1988  unsigned short
1990  {
1991  // inserts the tp3d into the section defined by tp3d.SFIndex
1992  if (tp3d.SFIndex >= pfp.SectionFits.size()) return USHRT_MAX;
1993  // Find the first occurrence of this SFIndex
1994  std::size_t ipt = 0;
1995  for (ipt = 0; ipt < pfp.TP3Ds.size(); ++ipt)
1996  if (tp3d.SFIndex == pfp.TP3Ds[ipt].SFIndex) break;
1997  if (ipt == pfp.TP3Ds.size()) return USHRT_MAX;
1998  // next see if we can insert it so that re-sorting of this section isn't required
1999  auto lastTP3D = pfp.TP3Ds.back();
2000  if (ipt == 0 && tp3d.along < pfp.TP3Ds[0].along) {
2001  // insert at the beginning. No search needs to be done
2002  }
2003  else if (tp3d.SFIndex == lastTP3D.SFIndex && tp3d.along > lastTP3D.along) {
2004  // insert at the end. Use push_back and return
2005  pfp.TP3Ds.push_back(tp3d);
2006  pfp.SectionFits[tp3d.SFIndex].NeedsUpdate = true;
2007  pfp.Flags[kNeedsUpdate] = true;
2008  return pfp.TP3Ds.size() - 1;
2009  }
2010  else {
2011  for (std::size_t iipt = ipt; iipt < pfp.TP3Ds.size() - 1; ++iipt) {
2012  // break out if the next point is in a different section
2013  if (pfp.TP3Ds[iipt + 1].SFIndex != tp3d.SFIndex) break;
2014  if (tp3d.along > pfp.TP3Ds[iipt].along && tp3d.along < pfp.TP3Ds[iipt + 1].along) {
2015  ipt = iipt + 1;
2016  break;
2017  }
2018  } // iipt
2019  } // insert in the middle
2020  pfp.TP3Ds.insert(pfp.TP3Ds.begin() + ipt, tp3d);
2021  pfp.SectionFits[tp3d.SFIndex].NeedsUpdate = true;
2022  pfp.Flags[kNeedsUpdate] = true;
2023  return ipt;
2024  } // InsertTP3D
2025 
2026  /////////////////////////////////////////
2027  bool
2028  SortSection(PFPStruct& pfp, unsigned short sfIndex)
2029  {
2030  // sorts the TP3Ds by the distance from the start of a fit section
2031 
2032  if (sfIndex > pfp.SectionFits.size() - 1) return false;
2033  auto& sf = pfp.SectionFits[sfIndex];
2034  if (sf.Pos[0] == 0.0 && sf.Pos[1] == 0.0 && sf.Pos[2] == 0.0) return false;
2035 
2036  // a temp vector of points in this section
2037  std::vector<TP3D> temp;
2038  // and the index into TP3Ds
2039  std::vector<unsigned short> indx;
2040  // See if the along variable is monotonically increasing
2041  float prevAlong = 0;
2042  bool first = true;
2043  bool needsSort = false;
2044  for (std::size_t ii = 0; ii < pfp.TP3Ds.size(); ++ii) {
2045  auto& tp3d = pfp.TP3Ds[ii];
2046  if (tp3d.SFIndex != sfIndex) continue;
2047  if (first) {
2048  first = false;
2049  prevAlong = tp3d.along;
2050  }
2051  else {
2052  if (tp3d.along < prevAlong) needsSort = true;
2053  prevAlong = tp3d.along;
2054  }
2055  temp.push_back(tp3d);
2056  indx.push_back(ii);
2057  } // tp3d
2058  if (temp.empty()) return false;
2059  // no sort needed?
2060  if (temp.size() == 1) return true;
2061  if (!needsSort) {
2062  sf.NeedsUpdate = false;
2063  return true;
2064  }
2065  // see if the points are not-contiguous
2066  bool contiguous = true;
2067  for (std::size_t ipt = 1; ipt < indx.size(); ++ipt) {
2068  if (indx[ipt] != indx[ipt - 1] + 1) contiguous = false;
2069  } // ipt
2070  if (!contiguous) { return false; }
2071 
2072  std::vector<SortEntry> sortVec(temp.size());
2073  for (std::size_t ii = 0; ii < temp.size(); ++ii) {
2074  sortVec[ii].index = ii;
2075  sortVec[ii].val = temp[ii].along;
2076  } // ipt
2077  std::sort(sortVec.begin(), sortVec.end(), valsIncreasing);
2078  for (std::size_t ii = 0; ii < temp.size(); ++ii) {
2079  // overwrite the tp3d
2080  auto& tp3d = pfp.TP3Ds[indx[ii]];
2081  tp3d = temp[sortVec[ii].index];
2082  } // ii
2083  sf.NeedsUpdate = false;
2084  return true;
2085  } // SortSection
2086 
2087  /////////////////////////////////////////
2088  void
2091  TCSlice& slc, PFPStruct& pfp, bool prt)
2092  {
2093  // try to recover from a poor initial fit
2094  if(pfp.AlgMod[kSmallAngle]) return;
2095  if(pfp.SectionFits.size() != 1) return;
2096  if(pfp.TP3Ds.size() < 20) return;
2097  if(!CanSection(slc, pfp)) return;
2098 
2099  // make a copy
2100  auto p2 = pfp;
2101  // try two sections
2102  p2.SectionFits.resize(2);
2103  unsigned short halfPt = p2.TP3Ds.size() / 2;
2104  for(unsigned short ipt = halfPt; ipt < p2.TP3Ds.size(); ++ipt) p2.TP3Ds[ipt].SFIndex = 1;
2105  // Confirm that both sections can be reconstructed
2106  unsigned short toPt = Find3DRecoRange(slc, p2, 0, 3, 1);
2107  if(toPt > p2.TP3Ds.size()) return;
2108  toPt = Find3DRecoRange(slc, p2, halfPt, 3, 1);
2109  if(toPt > p2.TP3Ds.size()) return;
2110  if(!FitSection(clockData, detProp, slc, p2, 0) || !FitSection(clockData, detProp, slc, p2, 1)) {
2111  if(prt) {
2112  mf::LogVerbatim myprt("TC");
2113  myprt << "Recover failed MVI " << p2.MVI << " in TPC " << p2.TPCID.TPC;
2114  for(auto tid : p2.TjIDs) myprt << " T" << tid;
2115  } // prt
2116  return;
2117  }
2118  if(prt) mf::LogVerbatim("TC")<<"Recover: P" << pfp.ID << " success";
2119  pfp = p2;
2120 
2121  } // Recover
2122 
2123  /////////////////////////////////////////
2124  bool
2126  {
2127  // Create and populate the TP3Ds vector. This function is called before the first
2128  // fit is done so the TP3D along variable can't be determined. It returns false
2129  // if a majority of the tj points in TjIDs are already assigned to a different pfp
2130  pfp.TP3Ds.clear();
2131  if (!pfp.TP3Ds.empty() || pfp.SectionFits.size() != 1) return false;
2132 
2133  // Look for TPs that are ~parallel to the wire plane and trajectory points
2134  // where the min/max Pos[1] value is not near an end.
2135  // number of Small Angle Tjs
2136  // stash the inflection point index in the TjUIDs vector
2137  pfp.TjUIDs.resize(pfp.TjIDs.size(), -1);
2138  // count TPs with charge in all of the Tjs
2139  float cnt = 0;
2140  // and the number of TPs available for use
2141  float avail = 0;
2142  // and the number of junk Tjs
2143  unsigned short nJunk = 0;
2144  unsigned short nSA = 0;
2145  for(unsigned short itj = 0; itj < pfp.TjIDs.size(); ++itj) {
2146  auto& tj = slc.tjs[pfp.TjIDs[itj] - 1];
2147  if(tj.AlgMod[kJunkTj]) ++nJunk;
2148  float posMin = 1E6;
2149  unsigned short iptMin = USHRT_MAX;
2150  float posMax = -1E6;
2151  unsigned short iptMax = USHRT_MAX;
2152  float aveAng = 0;
2153  float npwc = 0;
2154  for(unsigned short ipt = tj.EndPt[0]; ipt < tj.EndPt[1]; ++ipt) {
2155  auto& tp = tj.Pts[ipt];
2156  if(tp.Chg <= 0) continue;
2157  ++cnt;
2158  if (tp.InPFP > 0) continue;
2159  ++avail;
2160  if(tp.Pos[1] > posMax) { posMax = tp.Pos[1]; iptMax = ipt; }
2161  if(tp.Pos[1] < posMin) { posMin = tp.Pos[1]; iptMin = ipt; }
2162  aveAng += tp.Ang;
2163  ++npwc;
2164  } // ipt
2165  if(npwc == 0) continue;
2166  aveAng /= npwc;
2167  if(std::abs(aveAng) < 0.05) ++nSA;
2168  // No problem if the min/max points are near the ends
2169  if(iptMin > tj.EndPt[0] + 4 && iptMin < tj.EndPt[1] - 4) pfp.TjUIDs[itj] = iptMin;
2170  if(iptMax > tj.EndPt[0] + 4 && iptMax < tj.EndPt[1] - 4) pfp.TjUIDs[itj] = iptMax;
2171  } // tid
2172  if(avail < 0.8 * cnt) return false;
2173  // small angle trajectory?
2174  if(nSA > 1) pfp.AlgMod[kSmallAngle] = true;
2175  if(prt) mf::LogVerbatim("TC")<<" P"<<pfp.ID<<" MVI "<<pfp.MVI<<" nJunkTj "<<nJunk<<" SmallAngle? "<<pfp.AlgMod[kSmallAngle];
2176 
2177  if(pfp.AlgMod[kSmallAngle]) return MakeSmallAnglePFP(detProp, slc, pfp, prt);
2178 
2179  // Add the points associated with the Tjs that were used to create the PFP
2180  for (auto tid : pfp.TjIDs) {
2181  auto& tj = slc.tjs[tid - 1];
2182  // There is one TP for every hit in a junk Tj so we can skip one, if there is only one
2183  if(nJunk == 1 && tj.AlgMod[kJunkTj]) continue;
2184  // All of the Tj's may be junk, especially for those at very high angle, so the
2185  // X position of the TP's isn't high quality. Inflate the errors below.
2186  bool isJunk = tj.AlgMod[kJunkTj];
2187  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
2188  auto& tp = tj.Pts[ipt];
2189  if (tp.Chg <= 0) continue;
2190  if (tp.InPFP > 0) continue;
2191  ++avail;
2192  auto tp3d = CreateTP3D(detProp, slc, tid, ipt);
2193  if(tp3d.Flags[kTP3DBad]) continue;
2194  tp3d.SFIndex = 0;
2195  if(isJunk) tp3d.TPXErr2 *= 4;
2196  // We need to assume that all points are good or the first fit will fail
2197  tp3d.Flags[kTP3DGood] = true;
2198  pfp.TP3Ds.push_back(tp3d);
2199  } // ipt
2200  } // tid
2201  if(prt) mf::LogVerbatim("TC")<<" has "<<pfp.TP3Ds.size()<<" TP3Ds";
2202  return true;
2203  } // MakeTP3Ds
2204 
2205  /////////////////////////////////////////
2207  TCSlice& slc, PFPStruct& pfp,
2208  bool prt)
2209  {
2210  // Create and populate the TP3Ds vector for a small-angle track. The standard track fit
2211  // will fail for these tracks. The kSmallAngle AlgMod bit
2212  // is set true. Assume that the calling function, MakeTP3Ds, has decided that this is a
2213  // small-angle track.
2214 
2215  if(!tcc.useAlg[kSmallAngle]) return false;
2216  if(pfp.TjIDs.size() < 2) return false;
2217 
2218  std::vector<SortEntry> sortVec(pfp.TjIDs.size());
2219  unsigned short sbCnt = 0;
2220  for (unsigned short itj = 0; itj < pfp.TjIDs.size(); ++itj) {
2221  sortVec[itj].index = itj;
2222  auto& tj = slc.tjs[pfp.TjIDs[itj] - 1];
2223  sortVec[itj].val = NumPtsWithCharge(slc, tj, false);
2224  if(pfp.TjUIDs[itj] > 0) ++sbCnt;
2225  } // ipt
2226  std::sort(sortVec.begin(), sortVec.end(), valsDecreasing);
2227 
2228  // Decide whether to use the inflection points to add another section. Inflection
2229  // points must exist in the two longest Tjs
2230  unsigned short tlIndex = sortVec[0].index;
2231  unsigned short nlIndex = sortVec[1].index;
2232  auto& tlong = slc.tjs[pfp.TjIDs[tlIndex] - 1];
2233  auto& nlong = slc.tjs[pfp.TjIDs[nlIndex] - 1];
2234  bool twoSections = (sbCnt > 1 && pfp.TjUIDs[tlIndex] > 0 && pfp.TjUIDs[nlIndex] > 0);
2235  unsigned short tStartPt = tlong.EndPt[0];
2236  unsigned short tEndPt = tlong.EndPt[1];
2237  unsigned short nStartPt = nlong.EndPt[0];
2238  unsigned short nEndPt = nlong.EndPt[1];
2239  if(twoSections) {
2240  pfp.SectionFits.resize(2);
2241  tEndPt = pfp.TjUIDs[tlIndex];
2242  nEndPt = pfp.TjUIDs[nlIndex];
2243  if(prt) {
2244  mf::LogVerbatim myprt("TC");
2245  myprt<<"MakeSmallAnglePFP: creating two sections using points";
2246  myprt<<" T"<<tlong.ID<<"_"<<tEndPt;
2247  myprt<<" T"<<nlong.ID<<"_"<<nEndPt;
2248  } // prt
2249  } // two Sections
2250  std::vector<Point3_t> sfEndPos;
2251  for(unsigned short isf = 0; isf < pfp.SectionFits.size(); ++isf) {
2252  // get the start and end TPs in this section
2253  auto& ltp0 = tlong.Pts[tStartPt];
2254  auto& ltp1 = tlong.Pts[tEndPt];
2255  auto& ntp0 = nlong.Pts[nStartPt];
2256  auto& ntp1 = nlong.Pts[nEndPt];
2257  // Get the 3D end points
2258  auto start = MakeTP3D(detProp, slc, ltp0, ntp0);
2259  auto end = MakeTP3D(detProp, slc, ltp1, ntp1);
2260  if(!start.Flags[kTP3DGood] || !end.Flags[kTP3DGood]) {
2261  std::cout<<" Start/end fail in section "<<isf<<". Add recovery code\n";
2262  return false;
2263  } // failure
2264  if(!InsideTPC(start.Pos, pfp.TPCID)) {
2265  mf::LogVerbatim("TC")<<" Start is outside the TPC "<<start.Pos[0]<<" "<<start.Pos[1]<<" "<<start.Pos[2];
2266  }
2267  if(!InsideTPC(end.Pos, pfp.TPCID)) {
2268  mf::LogVerbatim("TC")<<" End is outside the TPC "<<end.Pos[0]<<" "<<end.Pos[1]<<" "<<end.Pos[2];
2269  }
2270  if(isf == 0) sfEndPos.push_back(start.Pos);
2271  sfEndPos.push_back(end.Pos);
2272  auto& sf = pfp.SectionFits[isf];
2273  // Find the start and end positions
2274  for(unsigned short xyz = 0; xyz < 3; ++xyz) {
2275  sf.Dir[xyz] = end.Pos[xyz] - start.Pos[xyz];
2276  sf.Pos[xyz] = (end.Pos[xyz] + start.Pos[xyz]) / 2.;
2277  }
2278  SetMag(sf.Dir, 1.);
2279  sf.ChiDOF = 0.;
2280  sf.NPts = 0;
2281  // move the start/end point indices
2282  tStartPt = tEndPt + 1; tEndPt = tlong.EndPt[1];
2283  nStartPt = nEndPt + 1; nEndPt = nlong.EndPt[1];
2284  } // isf
2285  // Create TP3Ds
2286  // a temporary vector to hold TP3Ds for the second SectionFit
2287  std::vector<TP3D> sf2pts;
2288  for(unsigned short itj = 0; itj < sortVec.size(); ++itj) {
2289  int tid = pfp.TjIDs[sortVec[itj].index];
2290  // don't add points for the Tj that doesn't have an inflection point. It is
2291  // probably broken and would probably be put in the wrong section
2292  if(twoSections && pfp.TjUIDs[sortVec[itj].index] < 0) continue;
2293  auto& tj = slc.tjs[tid - 1];
2294  unsigned short sb = tj.EndPt[1];
2295  if(twoSections && pfp.TjUIDs[sortVec[itj].index] > 0) sb = pfp.TjUIDs[sortVec[itj].index];
2296  // count the number of good TPs in each section
2297  std::vector<double> npwc(pfp.SectionFits.size(), 0);
2298  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
2299  auto& tp = tj.Pts[ipt];
2300  if(tp.Chg <= 0) continue;
2301  if(ipt > sb) { ++npwc[1]; } else { ++npwc[0]; }
2302  } // ipt
2303  double length = PosSep(sfEndPos[0], sfEndPos[1]);
2304  double step = length / npwc[0];
2305  double along = -length / 2;
2306  unsigned short sfi = 0;
2307  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
2308  auto& tp = tj.Pts[ipt];
2309  if(tp.Chg <= 0) continue;
2310  auto tp3d = CreateTP3D(detProp, slc, tid, ipt);
2311  if(tp3d.Flags[kTP3DBad]) continue;
2312  if(ipt == sb + 1) {
2313  sfi = 1;
2314  length = PosSep(sfEndPos[1], sfEndPos[2]);
2315  step = length / npwc[1];
2316  along = -length / 2;
2317  }
2318  tp3d.SFIndex = sfi;
2319  auto& sf = pfp.SectionFits[sfi];
2320  ++sf.NPts;
2321  tp3d.along = along;
2322  for(unsigned short xyz = 0; xyz < 3; ++xyz) tp3d.Pos[xyz] = sf.Pos[xyz] + along * sf.Dir[xyz];
2323  tp3d.Dir = sf.Dir;
2324  along += step;
2325  double delta = tp3d.Pos[0] - tp3d.TPX;
2326  sf.ChiDOF += delta * delta / tp3d.TPXErr2;
2327  // Assume that all points are good
2328  tp3d.Flags[kTP3DGood] = true;
2329  if(sfi == 0) {
2330  pfp.TP3Ds.push_back(tp3d);
2331  } else {
2332  sf2pts.push_back(tp3d);
2333  }
2334  } // ipt
2335  } // tid
2336  if(pfp.TP3Ds.size() < 4) return false;
2337  for(auto& sf : pfp.SectionFits) {
2338  if(sf.NPts < 5) return false;
2339  sf.ChiDOF /= (float)(sf.NPts - 4);
2340  } // sf
2341  if(!SortSection(pfp, 0)) return false;
2342  if(!sf2pts.empty()) {
2343  // append the points and sort
2344  pfp.TP3Ds.insert(pfp.TP3Ds.end(), sf2pts.begin(), sf2pts.end());
2345  if(!SortSection(pfp, 1)) return false;
2346  } // two sections
2347  pfp.Flags[kCanSection] = false;
2348  pfp.AlgMod[kSmallAngle] = true;
2349  if(prt) {
2350  mf::LogVerbatim("TC")<<"Created SmallAngle P"<<pfp.ID
2351  <<" with "<<pfp.TP3Ds.size()
2352  <<" points in "<<pfp.SectionFits.size()<<" sections\n";
2353  }
2354  return true;
2355  } // MakeSmallAnglePFP
2356 
2357 
2358  /////////////////////////////////////////
2359  void
2361  {
2362  // reverse the PFParticle
2363  std::reverse(pfp.TP3Ds.begin(), pfp.TP3Ds.end());
2364  std::reverse(pfp.SectionFits.begin(), pfp.SectionFits.end());
2365  for (std::size_t sfi = 0; sfi < pfp.SectionFits.size(); ++sfi) {
2366  auto& sf = pfp.SectionFits[sfi];
2367  // flip the direction vector
2368  for (unsigned short xyz = 0; xyz < 3; ++xyz)
2369  sf.Dir[xyz] *= -1;
2370  } // sf
2371  // correct the along variable
2372  for (auto& tp3d : pfp.TP3Ds)
2373  tp3d.along *= -1;
2374  std::swap(pfp.dEdx[0], pfp.dEdx[1]);
2375  std::swap(pfp.dEdxErr[0], pfp.dEdxErr[1]);
2376  std::swap(pfp.Vx3ID[0], pfp.Vx3ID[1]);
2377  std::swap(pfp.EndFlag[0], pfp.EndFlag[1]);
2378  } // Reverse
2379 
2380  /////////////////////////////////////////
2381  void
2383  {
2384  // Fills the mallTraj vector with trajectory points in the tpc and sorts
2385  // them by increasing X
2386 
2387  int cstat = slc.TPCID.Cryostat;
2388  int tpc = slc.TPCID.TPC;
2389 
2390  // define mallTraj
2391  slc.mallTraj.clear();
2392  Tj2Pt tj2pt;
2393  unsigned short cnt = 0;
2394 
2395  // try to reduce CPU time by not attempting to match tjs that are near muons
2396  bool muFuzzCut = (tcc.match3DCuts.size() > 6 && tcc.match3DCuts[6] > 0);
2397 
2398  float rms = tcc.match3DCuts[0];
2399  for (auto& tj : slc.tjs) {
2400  if (tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) continue;
2401  // ignore already matched
2402  if (tj.AlgMod[kMat3D]) continue;
2403  geo::PlaneID planeID = DecodeCTP(tj.CTP);
2404  if ((int)planeID.Cryostat != cstat) continue;
2405  if ((int)planeID.TPC != tpc) continue;
2406  int plane = planeID.Plane;
2407  if (tj.ID <= 0) continue;
2408  unsigned short tjID = tj.ID;
2409  for (unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
2410  auto& tp = tj.Pts[ipt];
2411  if (tp.Chg <= 0) continue;
2412  if (tp.Pos[0] < -0.4) continue;
2413  // ignore already matched
2414  if (tp.InPFP > 0) continue;
2415  if (muFuzzCut && tp.Environment[kEnvNearMuon]) continue;
2416  tj2pt.wire = std::nearbyint(tp.Pos[0]);
2417  ++cnt;
2418  // don't try matching if the wire doesn't exist
2419  if (!tcc.geom->HasWire(geo::WireID(cstat, tpc, plane, tj2pt.wire))) continue;
2420  float xpos = detProp.ConvertTicksToX(tp.Pos[1] / tcc.unitsPerTick, plane, tpc, cstat);
2421  tj2pt.xlo = xpos - rms;
2422  tj2pt.xhi = xpos + rms;
2423  tj2pt.plane = plane;
2424  tj2pt.id = tjID;
2425  tj2pt.ipt = ipt;
2426  tj2pt.npts = tj.EndPt[1] - tj.EndPt[0] + 1;
2427  slc.mallTraj.push_back(tj2pt);
2428  } // tp
2429  } // tj
2430 
2431  // sort by increasing x
2432  std::vector<SortEntry> sortVec(slc.mallTraj.size());
2433  for (std::size_t ipt = 0; ipt < slc.mallTraj.size(); ++ipt) {
2434  // populate the sort vector
2435  sortVec[ipt].index = ipt;
2436  sortVec[ipt].val = slc.mallTraj[ipt].xlo;
2437  } // ipt
2438  // sort by increasing xlo
2439  std::sort(sortVec.begin(), sortVec.end(), valsIncreasing);
2440  // put slc.mallTraj into sorted order
2441  auto tallTraj = slc.mallTraj;
2442  for (std::size_t ii = 0; ii < sortVec.size(); ++ii)
2443  slc.mallTraj[ii] = tallTraj[sortVec[ii].index];
2444 
2445  } // FillmAllTraj
2446 
2447  /////////////////////////////////////////
2449  TCSlice& slc, const TrajPoint& itp, const TrajPoint& jtp)
2450  {
2451  // Make a 3D trajectory point using two 2D trajectory points. The TP3D Pos and Wire
2452  // variables are defined using itp. The SectionFit variables are un-defined
2453  TP3D tp3d;
2454  tp3d.TPIndex = 0;
2455  tp3d.TjID = 0;
2456  tp3d.CTP = itp.CTP;
2457  // assume failure
2458  tp3d.Flags[kTP3DGood] = false;
2459  tp3d.Dir = {{0.0, 0.0, 1.0}};
2460  tp3d.Pos = {{999.0, 999.0, 999.0}};
2461  geo::PlaneID iPlnID = DecodeCTP(itp.CTP);
2462  geo::PlaneID jPlnID = DecodeCTP(jtp.CTP);
2463  if(iPlnID == jPlnID) return tp3d;
2464  double upt = tcc.unitsPerTick;
2465  double ix = detProp.ConvertTicksToX(itp.Pos[1] / upt, iPlnID);
2466  double jx = detProp.ConvertTicksToX(jtp.Pos[1] / upt, jPlnID);
2467 
2468  // don't continue if the points are wildly far apart in X
2469  double dx = std::abs(ix - jx);
2470  if(dx > 20) return tp3d;
2471  tp3d.Pos[0] = (ix + jx) / 2;
2472  tp3d.TPX = ix;
2473  // Fake the error
2474  tp3d.TPXErr2 = dx;
2475  // determine the wire orientation and offsets using WireCoordinate
2476  // wire = yp * OrthY + zp * OrthZ - Wire0 = cs * yp + sn * zp - wire0
2477  // wire offset
2478  double iw0 = tcc.geom->WireCoordinate(0, 0, iPlnID);
2479  // cosine-like component
2480  double ics = tcc.geom->WireCoordinate(1, 0, iPlnID) - iw0;
2481  // sine-like component
2482  double isn = tcc.geom->WireCoordinate(0, 1, iPlnID) - iw0;
2483  double jw0 = tcc.geom->WireCoordinate(0, 0, jPlnID);
2484  double jcs = tcc.geom->WireCoordinate(1, 0, jPlnID) - jw0;
2485  double jsn = tcc.geom->WireCoordinate(0, 1, jPlnID) - jw0;
2486  double den = isn * jcs - ics * jsn;
2487  if(den == 0) return tp3d;
2488  double iPos0 = itp.Pos[0];
2489  double jPos0 = jtp.Pos[0];
2490  // Find the Z position of the intersection
2491  tp3d.Pos[2] = (jcs * (iPos0 - iw0) - ics * (jPos0 - jw0)) / den;
2492  // and the Y position
2493  bool useI = std::abs(ics) > std::abs(jcs);
2494  if(useI) {
2495  tp3d.Pos[1] = (iPos0 - iw0 - isn * tp3d.Pos[2]) / ics;
2496  } else {
2497  tp3d.Pos[1] = (jPos0 - jw0 - jsn * tp3d.Pos[2]) / jcs;
2498  }
2499 
2500  // Now find the direction. Protect against large angles first
2501  if(jtp.Dir[1] == 0) {
2502  // Going either in the +X direction or -X direction
2503  if(jtp.Dir[0] > 0) { tp3d.Dir[0] = 1; } else { tp3d.Dir[0] = -1; }
2504  tp3d.Dir[1] = 0;
2505  tp3d.Dir[2] = 0;
2506  return tp3d;
2507  } // jtp.Dir[1] == 0
2508 
2509  tp3d.Wire = iPos0;
2510 
2511  // make a copy of itp and shift it by many wires to avoid precision problems
2512  double itp2_0 = itp.Pos[0] + 100;
2513  double itp2_1 = itp.Pos[1];
2514  if(std::abs(itp.Dir[0]) > 0.01) itp2_1 += 100 * itp.Dir[1] / itp.Dir[0];
2515  // Create a second Point3 for the shifted point
2516  Point3_t pos2;
2517  // Find the X position corresponding to the shifted point
2518  pos2[0] = detProp.ConvertTicksToX(itp2_1 / upt, iPlnID);
2519  // Convert X to Ticks in the j plane and then to WSE units
2520  double jtp2Pos1 = detProp.ConvertXToTicks(pos2[0], jPlnID) * upt;
2521  // Find the wire position (Pos0) in the j plane that this corresponds to
2522  double jtp2Pos0 = (jtp2Pos1 - jtp.Pos[1]) * (jtp.Dir[0] / jtp.Dir[1]) + jtp.Pos[0];
2523  // Find the Y,Z position using itp2 and jtp2Pos0
2524  pos2[2] = (jcs * (itp2_0 - iw0) - ics * (jtp2Pos0 - jw0)) / den;
2525  if(useI) {
2526  pos2[1] = (itp2_0 - iw0 - isn * pos2[2]) / ics;
2527  } else {
2528  pos2[1] = (jtp2Pos0 - jw0 - jsn * pos2[2]) / jcs;
2529  }
2530  double sep = PosSep(tp3d.Pos, pos2);
2531  if(sep == 0) return tp3d;
2532  for(unsigned short ixyz = 0; ixyz < 3; ++ixyz) tp3d.Dir[ixyz] = (pos2[ixyz] - tp3d.Pos[ixyz]) /sep;
2533  tp3d.Flags[kTP3DGood] = true;
2534  return tp3d;
2535 
2536  } // MakeTP3D
2537 
2538  ////////////////////////////////////////////////
2539  double
2540  DeltaAngle(const Vector3_t v1, const Vector3_t v2)
2541  {
2542  if (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2]) return 0;
2543  return acos(DotProd(v1, v2));
2544  }
2545 
2546  ////////////////////////////////////////////////
2547  Vector3_t
2549  {
2550  // Finds the direction vector between the two points from p1 to p2
2551  Vector3_t dir;
2552  for (unsigned short xyz = 0; xyz < 3; ++xyz)
2553  dir[xyz] = p2[xyz] - p1[xyz];
2554  if (dir[0] == 0 && dir[1] == 0 && dir[2] == 0) return dir;
2555  if (!SetMag(dir, 1)) {
2556  dir[0] = 0;
2557  dir[1] = 0;
2558  dir[3] = 0;
2559  }
2560  return dir;
2561  } // PointDirection
2562 
2563  //////////////////////////////////////////
2564  double
2565  PosSep(const Point3_t& pos1, const Point3_t& pos2)
2566  {
2567  return sqrt(PosSep2(pos1, pos2));
2568  } // PosSep
2569 
2570  //////////////////////////////////////////
2571  double
2572  PosSep2(const Point3_t& pos1, const Point3_t& pos2)
2573  {
2574  // returns the separation distance^2 between two positions in 3D
2575  double d0 = pos1[0] - pos2[0];
2576  double d1 = pos1[1] - pos2[1];
2577  double d2 = pos1[2] - pos2[2];
2578  return d0 * d0 + d1 * d1 + d2 * d2;
2579  } // PosSep2
2580 
2581  //////////////////////////////////////////
2582  bool
2583  SetMag(Vector3_t& v1, double mag)
2584  {
2585  double den = v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2];
2586  if (den == 0) return false;
2587  den = sqrt(den);
2588 
2589  v1[0] *= mag / den;
2590  v1[1] *= mag / den;
2591  v1[2] *= mag / den;
2592  return true;
2593  } // SetMag
2594 
2595  /////////////////////////////////////////
2596  void
2599  const TCSlice& slc,
2600  PFPStruct& pfp)
2601  {
2602  // Fills dE/dx variables in the pfp struct
2603 
2604  // don't attempt to find dE/dx at the end of a shower
2605  unsigned short numEnds = 2;
2606  if (pfp.PDGCode == 1111) numEnds = 1;
2607 
2608  // set dE/dx to 0 to indicate that a valid dE/dx is expected
2609  for (unsigned short end = 0; end < numEnds; ++end) {
2610  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane)
2611  pfp.dEdx[end][plane] = 0;
2612  } // end
2613 
2614  // square of the maximum length that is used for finding the average dE/dx
2615  float maxSep2 = 5 * tcc.wirePitch;
2616  maxSep2 *= maxSep2;
2617 
2618  for (unsigned short end = 0; end < numEnds; ++end) {
2619  std::vector<float> cnt(slc.nPlanes);
2620  short dir = 1 - 2 * end;
2621  auto endPos = PosAtEnd(pfp, end);
2622  for (std::size_t ii = 0; ii < pfp.TP3Ds.size(); ++ii) {
2623  unsigned short ipt;
2624  if (dir > 0) { ipt = ii; }
2625  else {
2626  ipt = pfp.TP3Ds.size() - ii - 1;
2627  }
2628  if (ipt >= pfp.TP3Ds.size()) break;
2629  auto& tp3d = pfp.TP3Ds[ipt];
2630  if (tp3d.Flags[kTP3DBad]) continue;
2631  if (PosSep2(tp3d.Pos, endPos) > maxSep2) break;
2632  // require good points
2633  if (!tp3d.Flags[kTP3DGood]) continue;
2634  float dedx = dEdx(clockData, detProp, slc, tp3d);
2635  if (dedx < 0.5) continue;
2636  unsigned short plane = DecodeCTP(tp3d.CTP).Plane;
2637  pfp.dEdx[end][plane] += dedx;
2638  ++cnt[plane];
2639  } // ii
2640  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
2641  if (cnt[plane] == 0) continue;
2642  pfp.dEdx[end][plane] /= cnt[plane];
2643  } // plane
2644  } // end
2645 
2646  } // FilldEdx
2647 
2648  /////////////////////////////////////////
2649  void
2652  const TCSlice& slc,
2653  PFPStruct& pfp,
2654  float& dEdXAve,
2655  float& dEdXRms)
2656  {
2657  // Return a simple average of dE/dx and rms using ALL points in all planes, not
2658  // just those at the ends ala FilldEdx
2659  dEdXAve = -1.;
2660  dEdXRms = -1.;
2661 
2662  double sum = 0;
2663  double sum2 = 0;
2664  double cnt = 0;
2665  for (auto& tp3d : pfp.TP3Ds) {
2666  if (!tp3d.Flags[kTP3DGood] || tp3d.Flags[kTP3DBad]) continue;
2667  double dedx = dEdx(clockData, detProp, slc, tp3d);
2668  if (dedx < 0.5 || dedx > 80.) continue;
2669  sum += dedx;
2670  sum2 += dedx * dedx;
2671  ++cnt;
2672  } // tp3d
2673  if (cnt < 3) return;
2674  dEdXAve = sum / cnt;
2675  // Use a default rms of 30% of the average
2676  dEdXRms = 0.3 * dEdXAve;
2677  double arg = sum2 - cnt * dEdXAve * dEdXAve;
2678  if (arg < 0) return;
2679  dEdXRms = sqrt(arg) / (cnt - 1);
2680  // don't return a too-small rms
2681  double minRms = 0.05 * dEdXAve;
2682  if (dEdXRms < minRms) dEdXRms = minRms;
2683  } // Average_dEdX
2684 
2685  /////////////////////////////////////////
2686  float
2689  const TCSlice& slc,
2690  TP3D& tp3d)
2691  {
2692  if (!tp3d.Flags[kTP3DGood]) return 0;
2693  if (tp3d.TjID > (int)slc.slHits.size()) return 0;
2694  if (tp3d.TjID <= 0) return 0;
2695 
2696  auto& tp = slc.tjs[tp3d.TjID - 1].Pts[tp3d.TPIndex];
2697  if (tp.Environment[kEnvOverlap]) return 0;
2698 
2699  double dQ = 0.;
2700  double time = 0;
2701  for (unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
2702  if (!tp.UseHit[ii]) continue;
2703  auto& hit = (*evt.allHits)[slc.slHits[tp.Hits[ii]].allHitsIndex];
2704  dQ += hit.Integral();
2705  } // ii
2706  time = tp.Pos[1] / tcc.unitsPerTick;
2707  geo::PlaneID plnID = DecodeCTP(tp.CTP);
2708  if (dQ == 0) return 0;
2709  double angleToVert = tcc.geom->Plane(plnID).ThetaZ() - 0.5 * ::util::pi<>();
2710  double cosgamma =
2711  std::abs(std::sin(angleToVert) * tp3d.Dir[1] + std::cos(angleToVert) * tp3d.Dir[2]);
2712  if (cosgamma < 1.E-5) return 0;
2713  double dx = tcc.geom->WirePitch(plnID) / cosgamma;
2714  double dQdx = dQ / dx;
2715  double t0 = 0;
2716  float dedx = tcc.caloAlg->dEdx_AREA(clockData, detProp, dQdx, time, plnID.Plane, t0);
2717  if (std::isinf(dedx)) dedx = 0;
2718  return dedx;
2719  } // dEdx
2720 
2721  ////////////////////////////////////////////////
2722  TP3D
2724  const TCSlice& slc,
2725  int tjID,
2726  unsigned short tpIndex)
2727  {
2728  // create a TP3D with a single TP. Note that the SectionFit in which it
2729  // should be placed and the 3D position can't be determined until the the TP3D is
2730  // associated with a pfp. See SetSection()
2731 
2732  TP3D tp3d;
2733  tp3d.Flags[kTP3DBad] = true;
2734  if (tjID <= 0 || tjID > (int)slc.tjs.size()) return tp3d;
2735  auto& tj = slc.tjs[tjID - 1];
2736  if (tpIndex < tj.EndPt[0] || tpIndex > tj.EndPt[1]) return tp3d;
2737  tp3d.TjID = tjID;
2738  tp3d.TPIndex = tpIndex;
2739  auto& tp2 = tj.Pts[tp3d.TPIndex];
2740  auto plnID = DecodeCTP(tp2.CTP);
2741  tp3d.CTP = tp2.CTP;
2742  double tick = tp2.HitPos[1] / tcc.unitsPerTick;
2743  tp3d.TPX = detProp.ConvertTicksToX(tick, plnID);
2744  // Get the RMS of the TP in WSE units and convert to cm
2745  float rms = TPHitsRMSTime(slc, tp2, kAllHits) * tcc.wirePitch;
2746  // inflate the error for large angle TPs
2747  if (tp2.AngleCode == 1) rms *= 2;
2748  // a more careful treatment for long-pulse hits
2749  if (tp2.AngleCode > 1) {
2750  std::vector<unsigned int> hitMultiplet;
2751  for (std::size_t ii = 0; ii < tp2.Hits.size(); ++ii) {
2752  if (!tp2.UseHit[ii]) continue;
2753  GetHitMultiplet(slc, tp2.Hits[ii], hitMultiplet, true);
2754  if (hitMultiplet.size() > 1) break;
2755  } // ii
2756  rms = HitsRMSTime(slc, hitMultiplet, kAllHits) * tcc.wirePitch;
2757  // the returned RMS is closer to the FWHM, so divide by 2
2758  rms /= 2;
2759  } // tp2.AngleCode > 1
2760  tp3d.TPXErr2 = rms * rms;
2761  tp3d.Wire = tp2.Pos[0];
2762  // Can't declare it good since Pos and SFIndex aren't defined
2763  tp3d.Flags[kTP3DGood] = false;
2764  tp3d.Flags[kTP3DBad] = false;
2765  return tp3d;
2766  } // CreateTP3D
2767 
2768  /////////////////////////////////////////
2769  bool
2771  const TCSlice& slc,
2772  PFPStruct& pfp,
2773  TP3D& tp3d)
2774  {
2775  // Determine which SectionFit this tp3d should reside in, then calculate
2776  // the 3D position and the distance from the center of the SectionFit
2777 
2778  if (tp3d.Wire < 0) return false;
2779  if (pfp.SectionFits.empty()) return false;
2780  if (pfp.SectionFits[0].Pos[0] == -10.0) return false;
2781  if(pfp.Flags[kSmallAngle]) return true;
2782 
2783  auto plnID = DecodeCTP(tp3d.CTP);
2784 
2785  if (pfp.SectionFits.size() == 1) { tp3d.SFIndex = 0; }
2786  else {
2787  // Find the section center that is closest to this point in the wire coordinate
2788  float best = 1E6;
2789  for (std::size_t sfi = 0; sfi < pfp.SectionFits.size(); ++sfi) {
2790  auto& sf = pfp.SectionFits[sfi];
2791  float sfWire = tcc.geom->WireCoordinate(sf.Pos[1], sf.Pos[2], plnID);
2792  float sep = std::abs(sfWire - tp3d.Wire);
2793  if (sep < best) {
2794  best = sep;
2795  tp3d.SFIndex = sfi;
2796  }
2797  } // sfi
2798  } // pfp.SectionFits.size() > 1
2799  auto& sf = pfp.SectionFits[tp3d.SFIndex];
2800  auto plnTP = MakeBareTP(detProp, slc, sf.Pos, sf.Dir, tp3d.CTP);
2801  // the number of wires relative to the SectionFit center
2802  double dw = tp3d.Wire - plnTP.Pos[0];
2803  // dt/dW was stored in DeltaRMS
2804  double t = dw * plnTP.DeltaRMS;
2805  // define the 3D position
2806  for (unsigned short xyz = 0; xyz < 3; ++xyz)
2807  tp3d.Pos[xyz] = sf.Pos[xyz] + t * sf.Dir[xyz];
2808  tp3d.along = t;
2809  tp3d.Flags[kTP3DGood] = true;
2810  return true;
2811  } // SetSection
2812 
2813  ////////////////////////////////////////////////
2814  float
2815  PointPull(const PFPStruct& pfp, const TP3D& tp3d)
2816  {
2817  // returns the pull that the tp3d will cause in the pfp section fit. This
2818  // currently only uses position but eventually will include charge
2819  return std::abs(tp3d.Pos[0] - tp3d.TPX) / sqrt(tp3d.TPXErr2);
2820  } // PointPull
2821 
2822  ////////////////////////////////////////////////
2823  PFPStruct
2824  CreatePFP(const TCSlice& slc)
2825  {
2826  // The calling function should define the size of pfp.TjIDs
2827  PFPStruct pfp;
2828  pfp.ID = slc.pfps.size() + 1;
2829  pfp.ParentUID = 0;
2830  pfp.TPCID = slc.TPCID;
2831  // initialize arrays for both ends
2832  if (slc.nPlanes < 4) {
2833  pfp.dEdx[0].resize(slc.nPlanes, -1);
2834  pfp.dEdx[1].resize(slc.nPlanes, -1);
2835  pfp.dEdxErr[0].resize(slc.nPlanes, -1);
2836  pfp.dEdxErr[1].resize(slc.nPlanes, -1);
2837  }
2838  // create a single section fit to hold the start/end positions and direction
2839  pfp.SectionFits.resize(1);
2840  return pfp;
2841  } // CreatePFP
2842 
2843  /////////////////////////////////////////
2844  void
2846  {
2847  // Ensure that all PFParticles have a start vertex. It is possible for
2848  // PFParticles to be attached to a 3D vertex that is later killed.
2849  if (!slc.isValid) return;
2850  if (slc.pfps.empty()) return;
2851 
2852  for (auto& pfp : slc.pfps) {
2853  if (pfp.ID == 0) continue;
2854  if (pfp.Vx3ID[0] > 0) continue;
2855  if (pfp.SectionFits.empty()) continue;
2856  Vtx3Store vx3;
2857  vx3.TPCID = pfp.TPCID;
2858  vx3.Vx2ID.resize(slc.nPlanes);
2859  // Flag it as a PFP vertex that isn't required to have matched 2D vertices
2860  vx3.Wire = -2;
2861  Point3_t startPos;
2862  if (pfp.TP3Ds.empty()) {
2863  // must be a neutrino pfp
2864  startPos = pfp.SectionFits[0].Pos;
2865  }
2866  else if (!pfp.TP3Ds.empty()) {
2867  // normal pfp
2868  startPos = pfp.TP3Ds[0].Pos;
2869  }
2870  vx3.X = startPos[0];
2871  vx3.Y = startPos[1];
2872  vx3.Z = startPos[2];
2873  vx3.ID = slc.vtx3s.size() + 1;
2874  vx3.Primary = false;
2875  ++evt.global3V_UID;
2876  vx3.UID = evt.global3V_UID;
2877  slc.vtx3s.push_back(vx3);
2878  pfp.Vx3ID[0] = vx3.ID;
2879  } // pfp
2880  } // PFPVertexCheck
2881 
2882  /////////////////////////////////////////
2883  void
2884  DefinePFPParents(TCSlice& slc, bool prt)
2885  {
2886  /*
2887  This function reconciles vertices, PFParticles and slc, then
2888  defines the parent (j) - daughter (i) relationship and PDGCode. Here is a
2889  description of the conventions:
2890 
2891  V1 is the highest score 3D vertex in this tpcid so a neutrino PFParticle P1 is defined.
2892  V4 is a high-score vertex that has lower score than V1. It is declared to be a
2893  primary vertex because its score is higher than V5 and it is not associated with the
2894  neutrino interaction
2895  V6 was created to adhere to the convention that all PFParticles, in this case P9,
2896  be associated with a start vertex. There is no score for V6. P9 is it's own parent
2897  but is not a primary PFParticle.
2898 
2899  P1 - V1 - P2 - V2 - P4 - V3 - P5 V4 - P6 V6 - P9
2900  \ \
2901  P3 P7 - V5 - P8
2902 
2903  The PrimaryID in this table is the ID of the PFParticle that is attached to the
2904  primary vertex, which may or may not be a neutrino interaction vertex.
2905  The PrimaryID is returned by the PrimaryID function
2906  PFP parentID DtrIDs PrimaryID
2907  -----------------------------------
2908  P1 P1 P2, P3 P1
2909  P2 P1 P4 P2
2910  P3 P1 none P3
2911  P4 P2 P5 P2
2912  P5 P4 none P2
2913 
2914  P6 P6 none P6
2915  P7 P7 P8 P7
2916 
2917  P9 P9 none 0
2918 
2919  */
2920  if (slc.pfps.empty()) return;
2921  if (tcc.modes[kTestBeam]) return;
2922 
2923  int neutrinoPFPID = 0;
2924  for (auto& pfp : slc.pfps) {
2925  if (pfp.ID == 0) continue;
2926  if (!tcc.modes[kTestBeam] && neutrinoPFPID == 0 && (pfp.PDGCode == 12 || pfp.PDGCode == 14)) {
2927  neutrinoPFPID = pfp.ID;
2928  break;
2929  }
2930  } // pfp
2931 
2932  // define the end vertex if the Tjs have end vertices
2933  constexpr unsigned short end1 = 1;
2934  for (auto& pfp : slc.pfps) {
2935  if (pfp.ID == 0) continue;
2936  // already done?
2937  if (pfp.Vx3ID[end1] > 0) continue;
2938  // ignore shower-like pfps
2939  if (IsShowerLike(slc, pfp.TjIDs)) continue;
2940  // count 2D -> 3D matched vertices
2941  unsigned short cnt3 = 0;
2942  unsigned short vx3id = 0;
2943  // list of unmatched 2D vertices that should be merged
2944  std::vector<int> vx2ids;
2945  for (auto tjid : pfp.TjIDs) {
2946  auto& tj = slc.tjs[tjid - 1];
2947  if (tj.VtxID[end1] == 0) continue;
2948  auto& vx2 = slc.vtxs[tj.VtxID[end1] - 1];
2949  if (vx2.Vx3ID == 0) {
2950  if (vx2.Topo == 1 && vx2.NTraj == 2) vx2ids.push_back(vx2.ID);
2951  continue;
2952  }
2953  if (vx3id == 0) vx3id = vx2.Vx3ID;
2954  if (vx2.Vx3ID == vx3id) ++cnt3;
2955  } // tjid
2956  if (cnt3 > 1) {
2957  // ensure it isn't attached at the other end
2958  if (pfp.Vx3ID[1 - end1] == vx3id) continue;
2959  pfp.Vx3ID[end1] = vx3id;
2960  } // cnt3 > 1
2961  } // pfp
2962 
2963  // Assign a PDGCode to each PFParticle and look for a parent
2964  for (auto& pfp : slc.pfps) {
2965  if (pfp.ID == 0) continue;
2966  // skip a neutrino PFParticle
2967  if (pfp.PDGCode == 12 || pfp.PDGCode == 14 || pfp.PDGCode == 22) continue;
2968  // Define a PFP parent if there are two or more Tjs that are daughters of
2969  // Tjs that are used by the same PFParticle
2970  int pfpParentID = INT_MAX;
2971  unsigned short nParent = 0;
2972  for (auto tjid : pfp.TjIDs) {
2973  auto& tj = slc.tjs[tjid - 1];
2974  if (tj.ParentID <= 0) continue;
2975  auto parPFP = GetAssns(slc, "T", tj.ParentID, "P");
2976  if (parPFP.empty()) continue;
2977  if (pfpParentID == INT_MAX) pfpParentID = parPFP[0];
2978  if (parPFP[0] == pfpParentID) ++nParent;
2979  } // ii
2980  if (nParent > 1) {
2981  auto& ppfp = slc.pfps[pfpParentID - 1];
2982  // set the parent UID
2983  pfp.ParentUID = ppfp.UID;
2984  // add to the parent daughters list
2985  ppfp.DtrUIDs.push_back(pfp.UID);
2986  } // nParent > 1
2987  } // ipfp
2988  // associate primary PFParticles with a neutrino PFParticle
2989  if (neutrinoPFPID > 0) {
2990  auto& neutrinoPFP = slc.pfps[neutrinoPFPID - 1];
2991  int vx3id = neutrinoPFP.Vx3ID[1];
2992  for (auto& pfp : slc.pfps) {
2993  if (pfp.ID == 0 || pfp.ID == neutrinoPFPID) continue;
2994  if (pfp.Vx3ID[0] != vx3id) continue;
2995  pfp.ParentUID = (size_t)neutrinoPFPID;
2996  neutrinoPFP.DtrUIDs.push_back(pfp.ID);
2997  if (pfp.PDGCode == 111) neutrinoPFP.PDGCode = 12;
2998  } // pfp
2999  } // neutrino PFP exists
3000  } // DefinePFPParents
3001 
3002  ////////////////////////////////////////////////
3003  bool
3005  {
3006  // stores the PFParticle in the slice
3007  bool neutrinoPFP = (pfp.PDGCode == 12 || pfp.PDGCode == 14);
3008  if (!neutrinoPFP) {
3009  if (pfp.TjIDs.empty()) return false;
3010  if (pfp.PDGCode != 1111 && pfp.TP3Ds.size() < 2) return false;
3011  }
3012 
3013  if(pfp.Flags[kSmallAngle]) {
3014  // Make the PFP -> TP assn
3015  for(auto& tp3d : pfp.TP3Ds) {
3016  if(tp3d.TPIndex != USHRT_MAX) slc.tjs[tp3d.TjID - 1].Pts[tp3d.TPIndex].InPFP = pfp.ID;
3017  }
3018  }
3019 
3020  // ensure that the InPFP flag is set
3021  unsigned short nNotSet = 0;
3022  for (auto& tp3d : pfp.TP3Ds) {
3023  if (tp3d.Flags[kTP3DBad]) continue;
3024  auto& tp = slc.tjs[tp3d.TjID - 1].Pts[tp3d.TPIndex];
3025  if (tp.InPFP != pfp.ID) ++nNotSet;
3026  } // tp3d
3027  if (nNotSet > 0) return false;
3028  // check the ID and correct it if it is wrong
3029  if (pfp.ID != (int)slc.pfps.size() + 1) pfp.ID = slc.pfps.size() + 1;
3030  ++evt.globalP_UID;
3031  pfp.UID = evt.globalP_UID;
3032 
3033  // set the 3D match flag
3034  for (auto tjid : pfp.TjIDs) {
3035  auto& tj = slc.tjs[tjid - 1];
3036  tj.AlgMod[kMat3D] = true;
3037  } // tjid
3038 
3039  slc.pfps.push_back(pfp);
3040  return true;
3041  } // StorePFP
3042 
3043  ////////////////////////////////////////////////
3044  bool
3045  InsideFV(const TCSlice& slc, const PFPStruct& pfp, unsigned short end)
3046  {
3047  // returns true if the end of the pfp is inside the fiducial volume of the TPC
3048  if (pfp.ID <= 0) return false;
3049  if (end > 1) return false;
3050  if (pfp.SectionFits.empty()) return false;
3051  // require that the points are sorted which ensures that the start and end points
3052  // are the first and last points in the TP3Ds vector
3053  if (pfp.Flags[kNeedsUpdate]) return false;
3054  bool neutrinoPFP = pfp.PDGCode == 12 || pfp.PDGCode == 14;
3055 
3056  float abit = 5;
3057  Point3_t pos;
3058  if (neutrinoPFP) { pos = pfp.SectionFits[0].Pos; }
3059  else if (end == 0) {
3060  pos = pfp.TP3Ds[0].Pos;
3061  }
3062  else {
3063  pos = pfp.TP3Ds[pfp.TP3Ds.size() - 1].Pos;
3064  }
3065  return (pos[0] > slc.xLo + abit && pos[0] < slc.xHi - abit && pos[1] > slc.yLo + abit &&
3066  pos[1] < slc.yHi - abit && pos[2] > slc.zLo + abit && pos[2] < slc.zHi - abit);
3067 
3068  } // InsideFV
3069 
3070  ////////////////////////////////////////////////
3071  bool
3072  InsideTPC(const Point3_t& pos, geo::TPCID& inTPCID)
3073  {
3074  // determine which TPC this point is in. This function returns false
3075  // if the point is not inside any TPC
3076  float abit = 5;
3077  for (const geo::TPCID& tpcid : tcc.geom->IterateTPCIDs()) {
3078  const geo::TPCGeo& TPC = tcc.geom->TPC(tpcid);
3079  double local[3] = {0., 0., 0.};
3080  double world[3] = {0., 0., 0.};
3081  TPC.LocalToWorld(local, world);
3082  // reduce the active area of the TPC by a bit to be consistent with FillWireHitRange
3083  if (pos[0] < world[0] - tcc.geom->DetHalfWidth(tpcid) + abit) continue;
3084  if (pos[0] > world[0] + tcc.geom->DetHalfWidth(tpcid) - abit) continue;
3085  if (pos[1] < world[1] - tcc.geom->DetHalfHeight(tpcid) + abit) continue;
3086  if (pos[1] > world[1] + tcc.geom->DetHalfHeight(tpcid) - abit) continue;
3087  if (pos[2] < world[2] - tcc.geom->DetLength(tpcid) / 2 + abit) continue;
3088  if (pos[2] > world[2] + tcc.geom->DetLength(tpcid) / 2 - abit) continue;
3089  inTPCID = tpcid;
3090  return true;
3091  } // tpcid
3092  return false;
3093  } // InsideTPC
3094 
3095  ////////////////////////////////////////////////
3096  void
3097  FindAlongTrans(Point3_t pos1, Vector3_t dir1, Point3_t pos2, Point2_t& alongTrans)
3098  {
3099  // Calculate the distance along and transvers to the direction vector from pos1 to pos2
3100  alongTrans[0] = 0;
3101  alongTrans[1] = 0;
3102  if (pos1[0] == pos2[0] && pos1[1] == pos2[1] && pos1[2] == pos2[2]) return;
3103  auto ptDir = PointDirection(pos1, pos2);
3104  SetMag(dir1, 1.0);
3105  double costh = DotProd(dir1, ptDir);
3106  if (costh > 1) costh = 1;
3107  double sep = PosSep(pos1, pos2);
3108  alongTrans[0] = costh * sep;
3109  double sinth = sqrt(1 - costh * costh);
3110  alongTrans[1] = sinth * sep;
3111  } // FindAlongTrans
3112 
3113  ////////////////////////////////////////////////
3114  bool
3116  Vector3_t p1Dir,
3117  Point3_t p2,
3118  Vector3_t p2Dir,
3119  Point3_t& intersect,
3120  float& doca)
3121  {
3122  // Point - vector version
3123  Point3_t p1End, p2End;
3124  for (unsigned short xyz = 0; xyz < 3; ++xyz) {
3125  p1End[xyz] = p1[xyz] + 10 * p1Dir[xyz];
3126  p2End[xyz] = p2[xyz] + 10 * p2Dir[xyz];
3127  }
3128  return LineLineIntersect(p1, p1End, p2, p2End, intersect, doca);
3129  } // PointDirIntersect
3130 
3131  ////////////////////////////////////////////////
3132  bool
3134  Point3_t p2,
3135  Point3_t p3,
3136  Point3_t p4,
3137  Point3_t& intersect,
3138  float& doca)
3139  {
3140  /*
3141  Calculate the line segment PaPb that is the shortest route between
3142  two lines P1P2 and P3P4. Calculate also the values of mua and mub where
3143  Pa = P1 + mua (P2 - P1)
3144  Pb = P3 + mub (P4 - P3)
3145  Return FALSE if no solution exists.
3146  http://paulbourke.net/geometry/pointlineplane/
3147  */
3148 
3149  Point3_t p13, p43, p21;
3150  double d1343, d4321, d1321, d4343, d2121;
3151  double numer, denom;
3152  constexpr double EPS = std::numeric_limits<double>::min();
3153 
3154  p13[0] = p1[0] - p3[0];
3155  p13[1] = p1[1] - p3[1];
3156  p13[2] = p1[2] - p3[2];
3157  p43[0] = p4[0] - p3[0];
3158  p43[1] = p4[1] - p3[1];
3159  p43[2] = p4[2] - p3[2];
3160  if (std::abs(p43[0]) < EPS && std::abs(p43[1]) < EPS && std::abs(p43[2]) < EPS) return (false);
3161  p21[0] = p2[0] - p1[0];
3162  p21[1] = p2[1] - p1[1];
3163  p21[2] = p2[2] - p1[2];
3164  if (std::abs(p21[0]) < EPS && std::abs(p21[1]) < EPS && std::abs(p21[2]) < EPS) return (false);
3165 
3166  d1343 = p13[0] * p43[0] + p13[1] * p43[1] + p13[2] * p43[2];
3167  d4321 = p43[0] * p21[0] + p43[1] * p21[1] + p43[2] * p21[2];
3168  d1321 = p13[0] * p21[0] + p13[1] * p21[1] + p13[2] * p21[2];
3169  d4343 = p43[0] * p43[0] + p43[1] * p43[1] + p43[2] * p43[2];
3170  d2121 = p21[0] * p21[0] + p21[1] * p21[1] + p21[2] * p21[2];
3171 
3172  denom = d2121 * d4343 - d4321 * d4321;
3173  if (std::abs(denom) < EPS) return (false);
3174  numer = d1343 * d4321 - d1321 * d4343;
3175 
3176  double mua = numer / denom;
3177  double mub = (d1343 + d4321 * mua) / d4343;
3178 
3179  intersect[0] = p1[0] + mua * p21[0];
3180  intersect[1] = p1[1] + mua * p21[1];
3181  intersect[2] = p1[2] + mua * p21[2];
3182  Point3_t pb;
3183  pb[0] = p3[0] + mub * p43[0];
3184  pb[1] = p3[1] + mub * p43[1];
3185  pb[2] = p3[2] + mub * p43[2];
3186  doca = PosSep(intersect, pb);
3187  // average the closest points
3188  for (unsigned short xyz = 0; xyz < 3; ++xyz)
3189  intersect[xyz] += pb[xyz];
3190  for (unsigned short xyz = 0; xyz < 3; ++xyz)
3191  intersect[xyz] /= 2;
3192  return true;
3193  } // LineLineIntersect
3194 
3195  ////////////////////////////////////////////////
3196  float
3198  const TCSlice& slc,
3199  Point3_t pos1,
3200  Point3_t pos2)
3201  {
3202  // Step between pos1 and pos2 and find the fraction of the points that have nearby hits
3203  // in each plane. This function returns -1 if something is fishy, but this doesn't mean
3204  // that there is no charge. Note that there is no check for charge precisely at the pos1 and pos2
3205  // positions
3206  float sep = PosSep(pos1, pos2);
3207  if (sep == 0) return -1;
3208  unsigned short nstep = sep / tcc.wirePitch;
3209  auto dir = PointDirection(pos1, pos2);
3210  float sum = 0;
3211  float cnt = 0;
3212  TrajPoint tp;
3213  for (unsigned short step = 0; step < nstep; ++step) {
3214  for (unsigned short xyz = 0; xyz < 3; ++xyz)
3215  pos1[xyz] += tcc.wirePitch * dir[xyz];
3216  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
3217  tp.CTP = EncodeCTP(slc.TPCID.Cryostat, slc.TPCID.TPC, plane);
3218  tp.Pos[0] =
3219  tcc.geom->WireCoordinate(pos1[1], pos1[2], plane, slc.TPCID.TPC, slc.TPCID.Cryostat);
3220  tp.Pos[1] = detProp.ConvertXToTicks(pos1[0], plane, slc.TPCID.TPC, slc.TPCID.Cryostat) *
3221  tcc.unitsPerTick;
3222  ++cnt;
3223  if (SignalAtTp(tp)) ++sum;
3224  } // plane
3225  } // step
3226  if (cnt == 0) return -1;
3227  return sum / cnt;
3228  } // ChgFracBetween
3229 
3230  ////////////////////////////////////////////////
3231  float
3233  const TCSlice& slc,
3234  const PFPStruct& pfp,
3235  unsigned short end)
3236  {
3237  // returns the charge fraction near the end of the pfp. Note that this function
3238  // assumes that there is only one Tj in a plane.
3239  if (pfp.ID == 0) return 0;
3240  if (pfp.TjIDs.empty()) return 0;
3241  if (end < 0 || end > 1) return 0;
3242  if (pfp.TPCID != slc.TPCID) return 0;
3243  if (pfp.SectionFits.empty()) return 0;
3244 
3245  float sum = 0;
3246  float cnt = 0;
3247  // keep track of the lowest value and maybe reject it
3248  float lo = 1;
3249  float hi = 0;
3250  auto pos3 = PosAtEnd(pfp, end);
3251  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
3252  CTP_t inCTP = EncodeCTP(pfp.TPCID.Cryostat, pfp.TPCID.TPC, plane);
3253  std::vector<int> tjids(1);
3254  for (auto tjid : pfp.TjIDs) {
3255  auto& tj = slc.tjs[tjid - 1];
3256  if (tj.CTP != inCTP) continue;
3257  tjids[0] = tjid;
3258  Point2_t pos2;
3259  geo::PlaneID planeID = geo::PlaneID(pfp.TPCID.Cryostat, pfp.TPCID.TPC, plane);
3260  pos2[0] = tcc.geom->WireCoordinate(pos3[1], pos3[2], planeID);
3261  if (pos2[0] < -0.4) continue;
3262  // check for dead wires
3263  unsigned int wire = std::nearbyint(pos2[0]);
3264  if (wire > slc.nWires[plane]) continue;
3265  if (slc.wireHitRange[plane][wire].first == UINT_MAX) continue;
3266  pos2[1] = detProp.ConvertXToTicks(pos3[0], planeID) * tcc.unitsPerTick;
3267  float cf = ChgFracNearPos(slc, pos2, tjids);
3268  if (cf < lo) lo = cf;
3269  if (cf > hi) hi = cf;
3270  sum += cf;
3271  ++cnt;
3272  } // tjid
3273  } // plane
3274  if (cnt == 0) return 0;
3275  if (cnt > 1 && lo < 0.3 && hi > 0.8) {
3276  sum -= lo;
3277  --cnt;
3278  }
3279  return sum / cnt;
3280  } // ChgFracNearEnd
3281 
3282  ////////////////////////////////////////////////
3283  Vector3_t
3284  DirAtEnd(const PFPStruct& pfp, unsigned short end)
3285  {
3286  if (end > 1 || pfp.SectionFits.empty()) return {{0., 0., 0.}};
3287  if (end == 0) return pfp.SectionFits[0].Dir;
3288  return pfp.SectionFits[pfp.SectionFits.size() - 1].Dir;
3289  } // PosAtEnd
3290 
3291  ////////////////////////////////////////////////
3292  Point3_t
3293  PosAtEnd(const PFPStruct& pfp, unsigned short end)
3294  {
3295  if (end > 1 || pfp.SectionFits.empty()) return {{0., 0., 0.}};
3296  // handle a neutrino pfp that doesn't have any TP3Ds
3297  if (pfp.TP3Ds.empty()) return pfp.SectionFits[0].Pos;
3298  if (end == 0) return pfp.TP3Ds[0].Pos;
3299  return pfp.TP3Ds[pfp.TP3Ds.size() - 1].Pos;
3300  } // PosAtEnd
3301 
3302  ////////////////////////////////////////////////
3303  float
3304  Length(const PFPStruct& pfp)
3305  {
3306  if (pfp.TP3Ds.empty()) return 0;
3307  return PosSep(pfp.TP3Ds[0].Pos, pfp.TP3Ds[pfp.TP3Ds.size() - 1].Pos);
3308  } // Length
3309 
3310  ////////////////////////////////////////////////
3311  bool
3313  unsigned short sfIndex,
3314  unsigned short& startPt,
3315  unsigned short& endPt)
3316  {
3317  // this assumes that the TP3Ds vector is sorted
3318  startPt = USHRT_MAX;
3319  endPt = USHRT_MAX;
3320  if (sfIndex >= pfp.SectionFits.size()) return false;
3321 
3322  bool first = true;
3323  for (std::size_t ipt = 0; ipt < pfp.TP3Ds.size(); ++ipt) {
3324  auto& tp3d = pfp.TP3Ds[ipt];
3325  if (tp3d.SFIndex < sfIndex) continue;
3326  if (first) {
3327  first = false;
3328  startPt = ipt;
3329  } // first
3330  if (tp3d.SFIndex > sfIndex) break;
3331  endPt = ipt;
3332  } // ipt
3333  return true;
3334 
3335  } // SectionStartEnd
3336 
3337  ////////////////////////////////////////////////
3338  unsigned short
3339  FarEnd(const TCSlice& slc, const PFPStruct& pfp, const Point3_t& pos)
3340  {
3341  // Returns the end (0 or 1) of the pfp that is furthest away from the position pos
3342  if (pfp.ID == 0) return 0;
3343  if (pfp.TP3Ds.empty()) return 0;
3344  auto& pos0 = pfp.TP3Ds[0].Pos;
3345  auto& pos1 = pfp.TP3Ds[pfp.TP3Ds.size() - 1].Pos;
3346  if (PosSep2(pos1, pos) > PosSep2(pos0, pos)) return 1;
3347  return 0;
3348  } // FarEnd
3349 
3350  /////////////////////////////////////////
3351  int
3354  const TCSlice& slc,
3355  PFPStruct& pfp)
3356  {
3357  // returns a vote using PDG code assignments from dE/dx. A PDGCode of -1 is
3358  // returned if there was a failure and returns 0 if no decision can be made
3359  if (pfp.TP3Ds.empty()) return -1;
3360 
3361  // try to do better using dE/dx
3362  float dEdXAve = 0;
3363  float dEdXRms = 0;
3364  Average_dEdX(clockData, detProp, slc, pfp, dEdXAve, dEdXRms);
3365  if (dEdXAve < 0) return 0;
3366  // looks like a proton if dE/dx is high and the rms is low
3367  dEdXRms /= dEdXAve;
3368  float length = Length(pfp);
3369  float mcsmom = 0;
3370  float chgrms = 0;
3371  float cnt = 0;
3372  for (auto tjid : pfp.TjIDs) {
3373  auto& tj = slc.tjs[tjid - 1];
3374  float el = ElectronLikelihood(slc, tj);
3375  if (el <= 0) continue;
3376  mcsmom += MCSMom(slc, tj);
3377  chgrms += tj.ChgRMS;
3378  ++cnt;
3379  } // tjid
3380  if (cnt < 2) return 0;
3381  mcsmom /= cnt;
3382  chgrms /= cnt;
3383  int vote = 0;
3384  // call anything longer than 150 cm a muon
3385  if (length > 150) vote = 13;
3386  // or shorter with low dE/dx and really straight
3387  if (vote == 0 && length > 50 && dEdXAve < 2.5 && mcsmom > 500) vote = 13;
3388  // protons have high dE/dx, high MCSMom and low charge rms
3389  if (vote == 0 && dEdXAve > 3.0 && mcsmom > 200 && chgrms < 0.4) vote = 2212;
3390  // electrons have low MCSMom and large charge RMS
3391  if (vote == 0 && mcsmom < 50 && chgrms > 0.4) vote = 11;
3392  return vote;
3393  } // PDGCodeVote
3394 
3395  ////////////////////////////////////////////////
3396  void
3399  std::string someText,
3400  const TCSlice& slc,
3401  const PFPStruct& pfp,
3402  short printPts)
3403  {
3404  if (pfp.TP3Ds.empty()) return;
3405  mf::LogVerbatim myprt("TC");
3406  myprt << someText << " pfp P" << pfp.ID << " MVI " << pfp.MVI;
3407  for (auto tid : pfp.TjIDs)
3408  myprt << " T" << tid;
3409  myprt << " Flags: CanSection? " << pfp.Flags[kCanSection];
3410  myprt << " NeedsUpdate? " << pfp.Flags[kNeedsUpdate];
3411  myprt << " Algs:";
3412  for (unsigned short ib = 0; ib < pAlgModSize; ++ib) {
3413  if (pfp.AlgMod[ib]) myprt << " " << AlgBitNames[ib];
3414  } // ib
3415  myprt << "\n";
3416  if (!pfp.SectionFits.empty()) {
3417  myprt << someText
3418  << " SFI ________Pos________ ________Dir_______ _____EndPos________ ChiDOF NPts "
3419  "NeedsUpdate?\n";
3420  for (std::size_t sfi = 0; sfi < pfp.SectionFits.size(); ++sfi) {
3421  myprt << someText << std::setw(4) << sfi;
3422  auto& sf = pfp.SectionFits[sfi];
3423  myprt << std::fixed << std::setprecision(1);
3424  unsigned short startPt = 0, endPt = 0;
3425  if (SectionStartEnd(pfp, sfi, startPt, endPt)) {
3426  auto& start = pfp.TP3Ds[startPt].Pos;
3427  myprt << std::setw(7) << start[0] << std::setw(7) << start[1] << std::setw(7) << start[2];
3428  }
3429  else {
3430  myprt << " Invalid";
3431  }
3432  myprt << std::fixed << std::setprecision(2);
3433  myprt << std::setw(7) << sf.Dir[0] << std::setw(7) << sf.Dir[1] << std::setw(7)
3434  << sf.Dir[2];
3435  myprt << std::fixed << std::setprecision(1);
3436  if (endPt < pfp.TP3Ds.size()) {
3437  auto& end = pfp.TP3Ds[endPt].Pos;
3438  myprt << std::setw(7) << end[0] << std::setw(7) << end[1] << std::setw(7) << end[2];
3439  }
3440  else {
3441  myprt << " Invalid";
3442  }
3443  myprt << std::setprecision(1) << std::setw(6) << sf.ChiDOF;
3444  myprt << std::setw(6) << sf.NPts;
3445  myprt << std::setw(6) << sf.NeedsUpdate;
3446  myprt << "\n";
3447  } // sec
3448  } // SectionFits
3449  if (printPts < 0) {
3450  // print the head if we print all points
3451  myprt<<someText<<" Note: GBH = TP3D Flags. G = Good, B = Bad, H = High dE/dx \n";
3452  myprt<<someText<<" ipt SFI ________Pos________ Delta Pull GBH Path along dE/dx S? T_ipt_P:W:T\n";
3453  }
3454  unsigned short fromPt = 0;
3455  unsigned short toPt = pfp.TP3Ds.size() - 1;
3456  if (printPts >= 0) fromPt = toPt;
3457  // temp kink angle for each point
3458  std::vector<float> dang(pfp.TP3Ds.size(), -1);
3459  for (unsigned short ipt = fromPt; ipt <= toPt; ++ipt) {
3460  auto tp3d = pfp.TP3Ds[ipt];
3461  myprt << someText << std::setw(4) << ipt;
3462  myprt << std::setw(4) << tp3d.SFIndex;
3463  myprt << std::fixed << std::setprecision(1);
3464  myprt << std::setw(7) << tp3d.Pos[0] << std::setw(7) << tp3d.Pos[1] << std::setw(7)
3465  << tp3d.Pos[2];
3466  myprt << std::setprecision(1) << std::setw(6) << (tp3d.Pos[0] - tp3d.TPX);
3467  float pull = PointPull(pfp, tp3d);
3468  myprt << std::setprecision(1) << std::setw(6) << pull;
3469  myprt << std::setw(3) << tp3d.Flags[kTP3DGood] << tp3d.Flags[kTP3DBad];
3470  myprt << std::setw(7) << std::setprecision(1) << PosSep(tp3d.Pos, pfp.TP3Ds[0].Pos);
3471  myprt << std::setw(7) << std::setprecision(1) << tp3d.along;
3472  myprt << std::setw(6) << std::setprecision(2) << dEdx(clockData, detProp, slc, tp3d);
3473  // print SignalAtTP in each plane
3474  myprt << " ";
3475  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
3476  CTP_t inCTP = EncodeCTP(pfp.TPCID.Cryostat, pfp.TPCID.TPC, plane);
3477  auto tp = MakeBareTP(detProp, slc, tp3d.Pos, inCTP);
3478  myprt << " " << SignalAtTp(tp);
3479  } // plane
3480  if (tp3d.TjID > 0) {
3481  auto& tp = slc.tjs[tp3d.TjID - 1].Pts[tp3d.TPIndex];
3482  myprt << " T" << tp3d.TjID << "_" << tp3d.TPIndex << "_" << PrintPos(slc, tp) << " "
3483  << TPEnvString(tp);
3484  }
3485  else {
3486  myprt << " UNDEFINED";
3487  }
3488  myprt << "\n";
3489  } // ipt
3490  } // PrintTP3Ds
3491 } // namespace
Expect tracks entering from the front face. Don&#39;t create neutrino PFParticles.
Definition: DataStructs.h:532
calo::CalorimetryAlg * caloAlg
Definition: DataStructs.h:577
geo::Length_t WireCoordinate(double YPos, double ZPos, geo::PlaneID const &planeid) const
Returns the index of the nearest wire to the specified position.
Vector2_t Dir
Definition: DataStructs.h:156
Point2_t Pos
Definition: DataStructs.h:155
unsigned short NPts
Definition: DataStructs.h:247
short MCSMom(const TCSlice &slc, const std::vector< int > &tjIDs)
Definition: Utils.cxx:3468
process_name opflash particleana ie ie ie z
std::vector< Trajectory > tjs
vector of all trajectories in each plane
Definition: DataStructs.h:670
void Recover(detinfo::DetectorClocksData const &clockData, detinfo::DetectorPropertiesData const &detProp, TCSlice &slc, PFPStruct &pfp, bool prt)
Definition: PFPUtils.cxx:2089
unsigned short FarEnd(const TCSlice &slc, const PFPStruct &pfp, const Point3_t &pos)
Definition: PFPUtils.cxx:3339
float Length(const PFPStruct &pfp)
Definition: PFPUtils.cxx:3304
float Wire
Definition: DataStructs.h:257
bool dbgStitch
debug PFParticle stitching
Definition: DataStructs.h:603
void Average_dEdX(detinfo::DetectorClocksData const &clockData, detinfo::DetectorPropertiesData const &detProp, const TCSlice &slc, PFPStruct &pfp, float &dEdXAve, float &dEdXRms)
Definition: PFPUtils.cxx:2650
void MakePFParticles(detinfo::DetectorClocksData const &clockData, detinfo::DetectorPropertiesData const &detProp, TCSlice &slc, std::vector< MatchStruct > matVec, unsigned short matVec_Iter)
Definition: PFPUtils.cxx:268
std::array< std::vector< float >, 2 > dEdxErr
Definition: DataStructs.h:289
then if[["$THISISATEST"==1]]
Definition: neoSmazza.sh:95
bool MakeVertexObsolete(std::string fcnLabel, TCSlice &slc, VtxStore &vx2, bool forceKill)
Definition: TCVertex.cxx:2726
int TjID
ID of the trajectory -&gt; TP3D assn.
Definition: DataStructs.h:259
bool ReconcileTPs(TCSlice &slc, PFPStruct &pfp, bool prt)
Definition: PFPUtils.cxx:427
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.
bool InsideFV(const TCSlice &slc, const PFPStruct &pfp, unsigned short end)
Definition: PFPUtils.cxx:3045
unsigned short CloseEnd(const TCSlice &slc, const Trajectory &tj, const Point2_t &pos)
Definition: Utils.cxx:2551
std::vector< int > GetAssns(TCSlice &slc, std::string type1Name, int id, std::string type2Name)
Definition: Utils.cxx:4849
static unsigned int kWire
const std::vector< std::string > AlgBitNames
Definition: DataStructs.cxx:16
bool SetSection(detinfo::DetectorPropertiesData const &detProp, const TCSlice &slc, PFPStruct &pfp, TP3D &tp3d)
Definition: PFPUtils.cxx:2770
CTP_t CTP
Cryostat, TPC, Plane code.
Definition: DataStructs.h:153
process_name opflash particleana ie x
bool InsideTPC(const Point3_t &pos, geo::TPCID &inTPCID)
Definition: PFPUtils.cxx:3072
std::array< double, 3 > Point3_t
Definition: DataStructs.h:41
bool SignalAtTp(TrajPoint &tp)
Definition: Utils.cxx:2004
std::vector< float > vtx3DCuts
2D vtx -&gt; 3D vtx matching cuts
Definition: DataStructs.h:551
bool SortSection(PFPStruct &pfp, unsigned short sfIndex)
Definition: PFPUtils.cxx:2028
TCConfig tcc
Definition: DataStructs.cxx:9
unsigned short id
Definition: DataStructs.h:146
void Reverse(TCSlice &slc, PFPStruct &pfp)
Definition: PFPUtils.cxx:2360
Declaration of signal hit object.
void FindAlongTrans(Point3_t pos1, Vector3_t dir1, Point3_t pos2, Point2_t &alongTrans)
Definition: PFPUtils.cxx:3097
std::bitset< pAlgModSize > AlgMod
Definition: DataStructs.h:302
std::vector< std::vector< std::pair< unsigned int, unsigned int > > > wireHitRange
Definition: DataStructs.h:675
std::vector< int > Vx2ID
Definition: DataStructs.h:114
The data type to uniquely identify a Plane.
Definition: geo_types.h:472
Geometry information for a single TPC.
Definition: TPCGeo.h:38
Point3_t Pos
center position of this section
Definition: DataStructs.h:243
bool StoreTraj(TCSlice &slc, Trajectory &tj)
Definition: Utils.cxx:1089
bool LineLineIntersect(Point3_t p1, Point3_t p2, Point3_t p3, Point3_t p4, Point3_t &intersect, float &doca)
Definition: PFPUtils.cxx:3133
std::string PrintPos(const TCSlice &slc, const TrajPoint &tp)
Definition: Utils.cxx:6526
CryostatID_t Cryostat
Index of cryostat.
Definition: geo_types.h:212
static unsigned int kPlane
void SetAngleCode(TrajPoint &tp)
Definition: Utils.cxx:773
void PrintP(std::string someText, mf::LogVerbatim &myprt, PFPStruct &pfp, bool &printHeader)
Definition: Utils.cxx:5605
PFPStruct CreatePFP(const TCSlice &slc)
Definition: PFPUtils.cxx:2824
process_name E
Point3_t PosAtEnd(const PFPStruct &pfp, unsigned short end)
Definition: PFPUtils.cxx:3293
double DeltaAngle(const Vector3_t v1, const Vector3_t v2)
Definition: PFPUtils.cxx:2540
bool dbgSlc
debug only in the user-defined slice? default is all slices
Definition: DataStructs.h:590
process_name hit
Definition: cheaterreco.fcl:51
void Match2Planes(TCSlice &slc, std::vector< MatchStruct > &matVec)
Definition: PFPUtils.cxx:945
bool CanSection(const TCSlice &slc, const PFPStruct &pfp)
Definition: PFPUtils.cxx:1343
std::array< int, 2 > Vx3ID
Definition: DataStructs.h:290
float TPHitsRMSTime(const TCSlice &slc, const TrajPoint &tp, HitStatus_t hitRequest)
Definition: Utils.cxx:4202
unsigned int MVI
MatchVec Index for detailed 3D matching.
Definition: DebugStruct.h:28
bool IsShowerLike(TCSlice &slc, const std::vector< int > TjIDs)
Definition: TCShower.cxx:1909
void FilldEdx(detinfo::DetectorClocksData const &clockData, detinfo::DetectorPropertiesData const &detProp, const TCSlice &slc, PFPStruct &pfp)
Definition: PFPUtils.cxx:2597
void ReconcileVertices(TCSlice &slc, PFPStruct &pfp, bool prt)
Definition: PFPUtils.cxx:1650
std::string TPEnvString(const TrajPoint &tp)
Definition: Utils.cxx:6353
geo::Length_t WirePitch(geo::PlaneID const &planeid) const
Returns the distance between two consecutive wires.
std::vector< std::array< unsigned int, 3 > > sptHits
SpacePoint -&gt; Hits assns by plane.
Definition: DataStructs.h:633
BEGIN_PROLOG TPC
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.
float MaxTjLen(const TCSlice &slc, std::vector< int > &tjIDs)
Definition: Utils.cxx:2630
void MakePFPTjs(TCSlice &slc)
Definition: PFPUtils.cxx:513
unsigned short Find3DRecoRange(const TCSlice &slc, const PFPStruct &pfp, unsigned short fromPt, unsigned short min2DPts, short dir)
Definition: PFPUtils.cxx:1359
bool MakeSmallAnglePFP(detinfo::DetectorPropertiesData const &detProp, TCSlice &slc, PFPStruct &pfp, bool prt)
Definition: PFPUtils.cxx:2206
Vector3_t DirErr
and direction error
Definition: DataStructs.h:245
IDparameter< geo::PlaneID > PlaneID
Member type of validated geo::PlaneID parameter.
double ThetaZ() const
Angle of the wires from positive z axis; .
Definition: PlaneGeo.cxx:726
unsigned short plane
Definition: DataStructs.h:144
void AddPointsInRange(detinfo::DetectorClocksData const &clockData, detinfo::DetectorPropertiesData const &detProp, TCSlice &slc, PFPStruct &pfp, unsigned short fromPt, unsigned short toPt, CTP_t inCTP, float maxPull, unsigned short &nWires, unsigned short &nAdd, bool prt)
Definition: PFPUtils.cxx:1838
then local
unsigned short npts
Definition: DataStructs.h:149
TrajPoint MakeBareTP(detinfo::DetectorPropertiesData const &detProp, const TCSlice &slc, const Point3_t &pos, CTP_t inCTP)
Definition: Utils.cxx:4027
std::bitset< 8 > Flags
Definition: DataStructs.h:300
float HitsRMSTime(const TCSlice &slc, const std::vector< unsigned int > &hitsInMultiplet, HitStatus_t hitRequest)
Definition: Utils.cxx:4237
Access the description of detector geometry.
std::vector< int > TjUIDs
Definition: DataStructs.h:284
bool ReSection(detinfo::DetectorClocksData const &clockData, detinfo::DetectorPropertiesData const &detProp, const TCSlice &slc, PFPStruct &pfp, bool prt)
Definition: PFPUtils.cxx:1112
unsigned short ipt
Definition: DataStructs.h:147
T abs(T value)
CTP_t CTP
Definition: DataStructs.h:260
struct of temporary 3D vertices
Definition: DataStructs.h:104
Vector3_t Dir
Definition: DataStructs.h:254
geo::TPCID TPCID
Definition: DataStructs.h:295
void PFPVertexCheck(TCSlice &slc)
Definition: PFPUtils.cxx:2845
geo::Length_t DetHalfHeight(geo::TPCID const &tpcid) const
Returns the half height of the active volume of the specified TPC.
Vector3_t Dir
and direction
Definition: DataStructs.h:244
process_name opflash particleana ie ie y
std::vector< Tj2Pt > mallTraj
vector of trajectory points ordered by increasing X
Definition: DataStructs.h:671
void FillGaps3D(detinfo::DetectorClocksData const &clockData, detinfo::DetectorPropertiesData const &detProp, TCSlice &slc, PFPStruct &pfp, bool prt)
Definition: PFPUtils.cxx:1745
std::array< float, 2 > Point2_t
Definition: DataStructs.h:43
int PDGCodeVote(detinfo::DetectorClocksData const &clockData, detinfo::DetectorPropertiesData const &detProp, const TCSlice &slc, PFPStruct &pfp)
Definition: PFPUtils.cxx:3352
float unitsPerTick
scale factor from Tick to WSE equivalent units
Definition: DataStructs.h:571
geo::TPCID TPCID
Definition: DataStructs.h:113
DebugStuff debug
Definition: DebugStruct.cxx:4
Vector3_t PointDirection(const Point3_t p1, const Point3_t p2)
Definition: PFPUtils.cxx:2548
void FillWireIntersections(TCSlice &slc)
Definition: PFPUtils.cxx:612
bool MakeTP3Ds(detinfo::DetectorPropertiesData const &detProp, TCSlice &slc, PFPStruct &pfp, bool prt)
Definition: PFPUtils.cxx:2125
int UID
unique global ID
Definition: DataStructs.h:116
unsigned short TPIndex
and the TP index
Definition: DataStructs.h:261
unsigned int wire
Definition: DataStructs.h:140
void Match3Planes(TCSlice &slc, std::vector< MatchStruct > &matVec)
Definition: PFPUtils.cxx:814
void GetHitMultiplet(const TCSlice &slc, unsigned int theHit, std::vector< unsigned int > &hitsInMultiplet, bool useLongPulseHits)
Definition: StepUtils.cxx:1415
bool TCIntersectionPoint(unsigned int wir1, unsigned int wir2, unsigned int pln1, unsigned int pln2, float &y, float &z)
Definition: PFPUtils.cxx:669
float ElectronLikelihood(const TCSlice &slc, const Trajectory &tj)
Definition: Utils.cxx:3216
std::vector< std::vector< bool > > goodWire
Definition: DataStructs.h:630
Point3_t Pos
position of the trajectory
Definition: DataStructs.h:253
for($it=0;$it< $RaceTrack_number;$it++)
float ChgFracNearPos(const TCSlice &slc, const Point2_t &pos, const std::vector< int > &tjIDs)
Definition: Utils.cxx:3236
std::vector< unsigned int > FindCloseHits(const TCSlice &slc, std::array< int, 2 > const &wireWindow, Point2_t const &timeWindow, const unsigned short plane, HitStatus_t hitRequest, bool usePeakTime, bool &hitsNear)
Definition: Utils.cxx:2843
float ChgFracBetween(detinfo::DetectorPropertiesData const &detProp, const TCSlice &slc, Point3_t pos1, Point3_t pos2)
Definition: PFPUtils.cxx:3197
auto end(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:585
bool FitSection(detinfo::DetectorClocksData const &clockData, detinfo::DetectorPropertiesData const &detProp, const TCSlice &slc, PFPStruct &pfp, unsigned short sfIndex)
Definition: PFPUtils.cxx:1414
unsigned short pln2
Definition: DataStructs.h:125
std::bitset< 8 > Flags
Definition: DataStructs.h:263
std::vector< VtxStore > vtxs
2D vertices
Definition: DataStructs.h:676
tick_as<> tick
Tick number, represented by std::ptrdiff_t.
Definition: electronics.h:75
double ConvertXToTicks(double X, int p, int t, int c) const
void CountBadPoints(const TCSlice &slc, const PFPStruct &pfp, unsigned short fromPt, unsigned short toPt, unsigned short &nBadPts, unsigned short &firstBadPt)
Definition: PFPUtils.cxx:1309
std::vector< float > match3DCuts
3D matching cuts
Definition: DataStructs.h:560
std::vector< SectionFit > SectionFits
Definition: DataStructs.h:286
geo::Length_t DetLength(geo::TPCID const &tpcid) const
Returns the length of the active volume of the specified TPC.
void MakeTrajectoryObsolete(TCSlice &slc, unsigned int itj)
Definition: Utils.cxx:2184
double TPXErr2
(X position error)^2
Definition: DataStructs.h:256
double PosSep2(const Point3_t &pos1, const Point3_t &pos2)
Definition: PFPUtils.cxx:2572
const geo::GeometryCore * geom
Definition: DataStructs.h:576
bool SectionStartEnd(const PFPStruct &pfp, unsigned short sfIndex, unsigned short &startPt, unsigned short &endPt)
Definition: PFPUtils.cxx:3312
bool PointDirIntersect(Point3_t p1, Vector3_t p1Dir, Point3_t p2, Vector3_t p2Dir, Point3_t &intersect, float &doca)
Definition: PFPUtils.cxx:3115
The data type to uniquely identify a TPC.
Definition: geo_types.h:386
PlaneID_t Plane
Index of the plane within its TPC.
Definition: geo_types.h:493
constexpr unsigned int pAlgModSize
Definition: DataStructs.h:280
bool valsIncreasing(const SortEntry &c1, const SortEntry &c2)
Definition: Utils.cxx:39
bool IntersectionPoint(geo::WireID const &wid1, geo::WireID const &wid2, double &y, double &z) const
Returns the intersection point of two wires.
bool SetMag(Vector3_t &v1, double mag)
Definition: PFPUtils.cxx:2583
Definition of data types for geometry description.
float dEdx(detinfo::DetectorClocksData const &clockData, detinfo::DetectorPropertiesData const &detProp, const TCSlice &slc, TP3D &tp3d)
Definition: PFPUtils.cxx:2687
auto norm(Vector const &v)
Return norm of the specified vector.
std::vector< TCSlice > slices
Definition: DataStructs.cxx:13
void GetRange(const PFPStruct &pfp, unsigned short sfIndex, unsigned short &fromPt, unsigned short &npts)
Definition: PFPUtils.cxx:1392
unsigned short InsertTP3D(PFPStruct &pfp, TP3D &tp3d)
Definition: PFPUtils.cxx:1989
tuple dir
Definition: dropbox.py:28
SectionFit FitTP3Ds(detinfo::DetectorPropertiesData const &detProp, const TCSlice &slc, const std::vector< TP3D > &tp3ds, unsigned short fromPt, short fitDir, unsigned short nPtsFit)
Definition: PFPUtils.cxx:1447
double ConvertTicksToX(double ticks, int p, int t, int c) const
std::bitset< 16 > modes
number of points to find AveChg
Definition: DataStructs.h:607
unsigned int NTPC(unsigned int cstat=0) const
Returns the total number of TPCs in the specified cryostat.
std::vector< TCHit > slHits
Definition: DataStructs.h:669
float PointPull(const PFPStruct &pfp, const TP3D &tp3d)
Definition: PFPUtils.cxx:2815
BEGIN_PROLOG Z planes
TP3D CreateTP3D(detinfo::DetectorPropertiesData const &detProp, const TCSlice &slc, int tjID, unsigned short tpIndex)
Definition: PFPUtils.cxx:2723
bool ValidTwoPlaneMatch(detinfo::DetectorPropertiesData const &detProp, const TCSlice &slc, const PFPStruct &pfp)
Definition: PFPUtils.cxx:1797
double DotProd(const Vector3_t &v1, const Vector3_t &v2)
Definition: PFPUtils.h:128
unsigned int CTP_t
Definition: DataStructs.h:47
std::vector< Vtx3Store > vtx3s
3D vertices
Definition: DataStructs.h:677
geo::TPCID TPCID
Definition: DataStructs.h:662
void FillmAllTraj(detinfo::DetectorPropertiesData const &detProp, TCSlice &slc)
Definition: PFPUtils.cxx:2382
std::bitset< 128 > useAlg
Max hit separation for making junk trajectories. &lt; 0 to turn off.
Definition: DataStructs.h:586
Encapsulate the construction of a single detector plane.
Contains all timing reference information for the detector.
std::vector< float > pfpStitchCuts
cuts for stitching between TPCs
Definition: DataStructs.h:564
geo::PlaneID DecodeCTP(CTP_t CTP)
float along
distance from the start Pos of the section
Definition: DataStructs.h:258
double PosSep(const Point3_t &pos1, const Point3_t &pos2)
Definition: PFPUtils.cxx:2565
std::vector< int > TjIDs
Definition: DataStructs.h:283
std::vector< TCWireIntersection > wireIntersections
Definition: DataStructs.h:639
std::vector< recob::Hit > const * allHits
Definition: DataStructs.h:622
bool valsDecreasing(const SortEntry &c1, const SortEntry &c2)
Definition: Utils.cxx:38
CTP_t EncodeCTP(unsigned int cryo, unsigned int tpc, unsigned int plane)
Definition: DataStructs.h:52
double dEdx_AREA(detinfo::DetectorClocksData const &clock_data, detinfo::DetectorPropertiesData const &det_prop, recob::Hit const &hit, double pitch, double T0=0) const
std::pair< unsigned short, unsigned short > GetSliceIndex(std::string typeName, int uID)
Definition: Utils.cxx:5088
unsigned short MVI_Iter
MVI iteration - see FindPFParticles.
Definition: DebugStruct.h:29
std::vector< unsigned int > nWires
Definition: DataStructs.h:653
std::array< double, 3 > Vector3_t
Definition: DataStructs.h:42
std::vector< TP3D > TP3Ds
Definition: DataStructs.h:285
int ID
ID of the recob::Slice (not the sub-slice)
Definition: DataStructs.h:664
std::array< std::vector< float >, 2 > dEdx
Definition: DataStructs.h:288
void Match3PlanesSpt(TCSlice &slc, std::vector< MatchStruct > &matVec)
Definition: PFPUtils.cxx:701
void DefinePFPParents(TCSlice &slc, bool prt)
Definition: PFPUtils.cxx:2884
unsigned short pln1
Definition: DataStructs.h:124
unsigned short nPlanes
Definition: DataStructs.h:663
TPCGeo const & TPC(unsigned int const tpc=0, unsigned int const cstat=0) const
Returns the specified TPC.
void FindPFParticles(detinfo::DetectorClocksData const &clockData, detinfo::DetectorPropertiesData const &detProp, TCSlice &slc)
Definition: PFPUtils.cxx:191
std::vector< PFPStruct > pfps
Definition: DataStructs.h:678
void MoveTPToWire(TrajPoint &tp, float wire)
Definition: Utils.cxx:2831
unsigned short NumPtsWithCharge(const TCSlice &slc, const Trajectory &tj, bool includeDeadWires)
Definition: Utils.cxx:2116
TCEvent evt
Definition: DataStructs.cxx:8
unsigned short MVI
Definition: DataStructs.h:299
double TPX
X position of the TP or the single hit.
Definition: DataStructs.h:255
TPCID_t TPC
Index of the TPC within its cryostat.
Definition: geo_types.h:406
bool StorePFP(TCSlice &slc, PFPStruct &pfp)
Definition: PFPUtils.cxx:3004
Collection of Physical constants used in LArSoft.
float ChgFracNearEnd(detinfo::DetectorPropertiesData const &detProp, const TCSlice &slc, const PFPStruct &pfp, unsigned short end)
Definition: PFPUtils.cxx:3232
void PrintTP3Ds(detinfo::DetectorClocksData const &clockData, detinfo::DetectorPropertiesData const &detProp, std::string someText, const TCSlice &slc, const PFPStruct &pfp, short printPts)
Definition: PFPUtils.cxx:3397
float A
Definition: dedx.py:137
size_t ParentUID
Definition: DataStructs.h:294
Vector3_t DirAtEnd(const PFPStruct &pfp, unsigned short end)
Definition: PFPUtils.cxx:3284
void StitchPFPs()
Definition: PFPUtils.cxx:42
TP3D MakeTP3D(detinfo::DetectorPropertiesData const &detProp, TCSlice &slc, const TrajPoint &itp, const TrajPoint &jtp)
Definition: PFPUtils.cxx:2448
void LocalToWorld(const double *tpc, double *world) const
Transform point from local TPC frame to world frame.
Definition: TPCGeo.h:563
physics pm2 e1
bool AttachToAnyVertex(TCSlice &slc, PFPStruct &pfp, float maxSep, bool prt)
Definition: TCVertex.cxx:1583
physics associatedGroupsWithLeft p1
constexpr TPCID()=default
Default constructor: an invalid TPC ID.
BEGIN_PROLOG could also be cout
auto const detProp
bool Update(detinfo::DetectorClocksData const &clockData, detinfo::DetectorPropertiesData const &detProp, const TCSlice &slc, PFPStruct &pfp, bool prt)
Definition: PFPUtils.cxx:1057
Encapsulate the construction of a single detector plane.
std::array< std::bitset< 8 >, 2 > EndFlag
Definition: DataStructs.h:301
bool SptInTPC(const std::array< unsigned int, 3 > &sptHits, unsigned int tpc)
Definition: PFPUtils.cxx:793
bool HasWire(geo::WireID const &wireid) const
Returns whether we have the specified wire.
unsigned short SFIndex
and the section fit index
Definition: DataStructs.h:262
unsigned int index
Definition: DataStructs.h:36