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