LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
Utils.cxx
Go to the documentation of this file.
2 
3 #include "larsim/MCCheater/ParticleInventoryService.h" // for printing
4 #include <boost/algorithm/string/classification.hpp> // Include boost::for is_any_of
5 #include <boost/algorithm/string/split.hpp> // Include for boost::split
6 
8 
15 #include "lardataobj/RecoBase/Hit.h" // for Hit
16 #include "larevt/CalibrationDBI/Interface/ChannelStatusProvider.h"
17 #include "larevt/CalibrationDBI/Interface/ChannelStatusService.h"
22 #include "larreco/RecoAlg/TCAlg/TCVertex.h" // for tcc
24 
25 #include <algorithm>
26 #include <array>
27 #include <bitset>
28 #include <fstream>
29 #include <iomanip>
30 #include <iostream>
31 #include <limits.h>
32 #include <math.h>
33 #include <stdlib.h>
34 #include <string>
35 #include <utility>
36 #include <vector>
37 
39 {
40  return c1.val > c2.val;
41 }
43 {
44  return c1.val < c2.val;
45 }
46 
47 namespace tca {
48 
49  using namespace detail;
50 
51  // dressed muons
52  void MakeHaloTj(TCSlice& slc, Trajectory& muTj, bool prt)
53  {
54  // Creates a "halo trajectory" around a muon tj consisting of hits and trajectories
55  // that are within MuonTag[4] distance. The halo tj is a virtual clone of muTj in the
56  // sense that it has the same number of points and the same start and end points.
57 
58  if (tcc.muonTag.size() < 5) return;
59  if (tcc.muonTag[4] <= 0) return;
60  if (!tcc.useAlg[kHaloTj]) return;
61 
62  if (muTj.PDGCode != 13) return;
63 
64  // check for daughter delta-rays
65  std::vector<int> dtrs;
66  for (auto& dtj : slc.tjs) {
67  if (dtj.AlgMod[kKilled]) continue;
68  if (dtj.ParentID != muTj.ID) continue;
69  dtrs.push_back(dtj.ID);
70  if (!dtj.AlgMod[kDeltaRay]) continue;
71  if (prt) mf::LogVerbatim("TC") << "MakeHaloTj: Killing delta-ray T" << dtj.ID;
72  // Kill a delta-ray PFParticle?
73  if (dtj.AlgMod[kMat3D]) {
74  unsigned short pfpIndex = GetPFPIndex(slc, dtj.ID);
75  if (pfpIndex == USHRT_MAX) {
76  if (prt) mf::LogVerbatim("TC") << " No PFP found for 3D-matched delta-ray";
77  }
78  else {
79  auto& pfp = slc.pfps[pfpIndex];
80  if (prt) mf::LogVerbatim("TC") << " Killing delta-ray PFParticle P" << pfp.UID;
81  pfp.ID = 0;
82  // correct the parent -> daughter assn
83  if (pfp.ParentUID > 0) {
84  auto parentIndx = GetSliceIndex("P", pfp.ParentUID);
85  if (parentIndx.first != USHRT_MAX) {
86  auto& parent = slices[parentIndx.first].pfps[parentIndx.second];
87  std::vector<int> newDtrUIDs;
88  for (auto uid : parent.DtrUIDs)
89  if (uid != dtj.UID) newDtrUIDs.push_back(uid);
90  parent.DtrUIDs = newDtrUIDs;
91  } // parent found
92  } // correct the parent
93  } // kill PFParticle
94  } // kill
95  MakeTrajectoryObsolete(slc, (unsigned int)(dtj.ID - 1));
96  } // dtj
97 
98  // make a copy
99  Trajectory tj;
100  tj.CTP = muTj.CTP;
101  // We can't use StoreTraj so variables need to be defined here
102  tj.ID = slc.tjs.size() + 1;
103  tj.WorkID = muTj.WorkID;
104  // increment the global ID
105  ++evt.globalT_UID;
106  tj.UID = evt.globalT_UID;
107  tj.PDGCode = 11;
108  tj.Pass = muTj.Pass;
109  tj.StepDir = muTj.StepDir;
110  tj.StartEnd = muTj.StartEnd;
111  tj.TotChg = 0;
112  tj.ChgRMS = 0;
113  tj.EndPt[0] = 0;
114  tj.ParentID = muTj.ID;
115  tj.AlgMod.reset();
116  tj.AlgMod[kHaloTj] = true;
117  // start a list of tjs that have points near the muon
118  std::vector<int> closeTjs;
119  for (unsigned short ipt = muTj.EndPt[0]; ipt <= muTj.EndPt[1]; ++ipt) {
120  auto tp = muTj.Pts[ipt];
121  tp.Hits.resize(0);
122  tp.UseHit.reset();
123  tp.Chg = 0;
124  tp.AveChg = 0;
125  tp.ChgPull = 0;
126  tp.Delta = 0;
127  tp.DeltaRMS = 0;
128  tp.FitChi = 0;
129  tp.NTPsFit = 0;
130  float window = tcc.muonTag[4];
131  if (tp.Dir[0] != 0) window *= std::abs(1 / tp.Dir[0]);
132  if (!FindCloseHits(slc, tp, window, kAllHits)) continue;
133  // add unused hits to the point and look for close tjs
134  bool hitsAdded = false;
135  for (unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
136  unsigned int iht = tp.Hits[ii];
137  auto inTraj = slc.slHits[iht].InTraj;
138  if (inTraj < 0) continue;
139  if (inTraj == 0) {
140  tp.UseHit[ii] = true;
141  slc.slHits[iht].InTraj = tj.ID;
142  hitsAdded = true;
143  }
144  else {
145  // add to the closeTjs list
146  if (inTraj != muTj.ID &&
147  std::find(closeTjs.begin(), closeTjs.end(), inTraj) == closeTjs.end())
148  closeTjs.push_back(inTraj);
149  }
150  } // ii
151  if (hitsAdded) {
152  DefineHitPos(slc, tp);
153  tp.Delta = PointTrajDOCA(tp.HitPos[0], tp.HitPos[1], tp);
154  tj.TotChg += tp.Chg;
155  tj.Pts.push_back(tp);
156  } // hitsAdded
157  } // ipt
158  if (tj.Pts.empty()) return;
159  tj.EndPt[1] = tj.Pts.size() - 1;
160  if (prt) {
161  mf::LogVerbatim myprt("TC");
162  myprt << "MHTj: T" << muTj.ID << " npts " << tj.Pts.size() << " close";
163  for (auto tid : closeTjs)
164  myprt << " T" << tid;
165  myprt << "\n";
166  PrintTrajectory("DM", slc, tj, USHRT_MAX);
167  }
168  slc.tjs.push_back(tj);
169  } // MakeHaloTj
170 
172  void DefineTjParents(TCSlice& slc, bool prt)
173  {
174  /*
175  This function sets the ParentUID of Tjs in this tpcid to create a hierarchy. The highest Score
176  3D vertex in a chain of Tjs and vertices is declared the primary vertex; vx3.Primary = true. Tjs directly attached
177  to that vertex are declared Primary trajectories with ParentUID = 0. All other Tjs in the chain have ParentUID
178  set to the next upstream Tj to which it is attached by a vertex. In the graphical description below, V1 and V4 are
179  2D vertices that are matched to a high-score 3D vertex. The V1 Score is greater than the V2 Score and V3 Score.
180  V1 and V4 are declared to be primary vertices. T1, T2, T6 and T7 are declared to be primary Tjs
181 
182  V1 - T1 - V2 - T3 V4 - T6 / T8
183  \ \ /
184  T2 - V3 - T4 T7
185  \
186  T5
187 
188  This is represented as follows. The NeutrinoPrimaryTjID is defined by a function.
189  Tj ParentUID NeutrinoPrimaryTjID
190  -----------------------------------
191  T1 0 T1
192  T2 0 T2
193  T3 T1 T2
194  T4 T2 T2
195  T5 T2 T2
196  T6 0 -1
197  T7 0 -1
198  T8 -1 -1
199 */
200 
201  // don't do anything if this is test beam data
202  if (tcc.modes[kTestBeam]) return;
203 
204  // clear old information
205  for (auto& tj : slc.tjs) {
206  if (tj.AlgMod[kKilled]) continue;
207  // ignore delta rays
208  if (tj.AlgMod[kDeltaRay] || tj.AlgMod[kHaloTj]) continue;
209  tj.ParentID = 0;
210  } // tj
211 
212  // sort vertice by decreasing score
213  std::vector<int> temp;
214  for (auto& vx3 : slc.vtx3s) {
215  if (vx3.ID == 0) continue;
216  // clear the Primary flag while we are here
217  vx3.Primary = false;
218  temp.push_back(vx3.ID);
219  } // vx3
220  if (temp.empty()) return;
221 
222  // Make a master list of all Tjs that are attached to these vertices
223  std::vector<int> masterlist;
224  for (auto vx3id : temp) {
225  auto& vx3 = slc.vtx3s[vx3id - 1];
226  float score;
227  auto tjlist = GetVtxTjIDs(slc, vx3, score);
228  for (auto tjid : tjlist) {
229  auto& tj = slc.tjs[tjid - 1];
230  if (tj.ParentID != 0) tj.ParentID = 0;
231  if (std::find(masterlist.begin(), masterlist.end(), tjid) == masterlist.end())
232  masterlist.push_back(tjid);
233  } // tjid
234  } // vxid
235  if (prt) {
236  mf::LogVerbatim myprt("TC");
237  myprt << "DTP: masterlist Tjs";
238  for (auto tjid : masterlist)
239  myprt << " " << tjid;
240  }
241 
242  // Do the sort
243  std::vector<SortEntry> sortVec(temp.size());
244  for (unsigned short indx = 0; indx < temp.size(); ++indx) {
245  auto& vx3 = slc.vtx3s[temp[indx] - 1];
246  sortVec[indx].index = indx;
247  sortVec[indx].val = vx3.Score;
248  } // indx
249  if (sortVec.size() > 1) std::sort(sortVec.begin(), sortVec.end(), valsDecreasing);
250  // put them into order
251  auto vlist = temp;
252  for (unsigned short indx = 0; indx < temp.size(); ++indx)
253  vlist[indx] = temp[sortVec[indx].index];
254 
255  // make a neutrino PFParticle to associate with the highest score vertex if it is high enough
256  if (tcc.match3DCuts[0] > 0) {
257  auto& vx3 = slc.vtx3s[vlist[0] - 1];
258  if (vx3.Score > tcc.vtx2DCuts[7]) {
259  auto neutrinoPFP = CreatePFP(slc);
260  // call it the neutrino vertex
261  vx3.Neutrino = true;
262  // put the vertex at the end of the neutrino
263  auto& sf = neutrinoPFP.SectionFits[0];
264  sf.Pos[0] = vx3.X;
265  sf.Pos[1] = vx3.Y;
266  sf.Pos[2] = vx3.Z;
267  sf.Dir[2] = 1;
268  // This may be set to 12 later on if a primary shower is reconstructed
269  neutrinoPFP.PDGCode = 14;
270  neutrinoPFP.Vx3ID[1] = vx3.ID;
271  neutrinoPFP.Vx3ID[0] = vx3.ID;
272  neutrinoPFP.Flags[kNeedsUpdate] = false;
273  // the rest of this will be defined later
274  if (!StorePFP(slc, neutrinoPFP)) return;
275  }
276  } // User wants to make PFParticles
277  // a temp vector to ensure that we only consider a vertex once
278  std::vector<bool> lookedAt3(slc.vtx3s.size() + 1, false);
279  std::vector<bool> lookedAt2(slc.vtxs.size() + 1, false);
280  // vector of parent-daughter pairs
281  std::vector<std::pair<int, int>> pardtr;
282  // Start with the highest score vertex
283  for (unsigned short indx = 0; indx < vlist.size(); ++indx) {
284  auto& vx3 = slc.vtx3s[vlist[indx] - 1];
285  if (lookedAt3[vx3.ID]) continue;
286  vx3.Primary = true;
287  lookedAt3[vx3.ID] = true;
288  // make a list of Tjs attached to this vertex
289  float score;
290  auto primTjList = GetVtxTjIDs(slc, vx3, score);
291  if (primTjList.empty()) continue;
292  pardtr.clear();
293  for (auto primTjID : primTjList) {
294  auto& primTj = slc.tjs[primTjID - 1];
295  // This isn't a primary tj if the parent ID isn't -1
296  if (primTj.ParentID != -1) continue;
297  if (prt) mf::LogVerbatim("TC") << "Vx3 " << vx3.ID << " Primary tj " << primTj.ID;
298  // declare this a primary tj
299  primTj.ParentID = 0;
300  // look for daughter tjs = those that are attached to a 2D vertex
301  // at the other end
302  for (unsigned short end = 0; end < 2; ++end) {
303  if (primTj.VtxID[end] == 0) continue;
304  auto& vx2 = slc.vtxs[primTj.VtxID[end] - 1];
305  if (vx2.Vx3ID == vx3.ID) continue;
306  // found a 2D vertex. Check for daughters
307  auto dtrList = GetVtxTjIDs(slc, vx2);
308  for (auto dtrID : dtrList) {
309  // ignore the primary tj
310  if (dtrID == primTjID) continue;
311  auto& dtj = slc.tjs[dtrID - 1];
312  if (dtj.ParentID != -1) continue;
313  pardtr.push_back(std::make_pair(primTjID, dtrID));
314  if (prt) mf::LogVerbatim("TC") << " primTj " << primTjID << " dtrID " << dtrID;
315  } // tjid
316  } // end
317  // Ensure that end 0 of the trajectory is attached to the primary vertex
318  for (unsigned short end = 0; end < 2; ++end) {
319  if (primTj.VtxID[end] == 0) continue;
320  auto& vx2 = slc.vtxs[primTj.VtxID[end] - 1];
321  if (vx2.Vx3ID == vx3.ID && end != 0) ReverseTraj(primTj);
322  } // end
323  } // tjid
324  if (pardtr.empty()) continue;
325  if (prt) {
326  mf::LogVerbatim myprt("TC");
327  myprt << " par_dtr";
328  for (auto pdtr : pardtr)
329  myprt << " " << pdtr.first << "_" << pdtr.second;
330  }
331  // iterate through the parent - daughter stack, removing the last pair when a
332  // ParentID is updated and adding pairs for new daughters
333  for (unsigned short nit = 0; nit < 100; ++nit) {
334  auto lastPair = pardtr[pardtr.size() - 1];
335  auto& dtj = slc.tjs[lastPair.second - 1];
336  dtj.ParentID = lastPair.first;
337  // reverse the daughter trajectory if necessary so that end 0 is closest to the parent
338  float doca = 100;
339  unsigned short dpt = 0, ppt = 0;
340  auto& ptj = slc.tjs[lastPair.first - 1];
341  // find the point on the daughter tj that is closest to the parent
342  TrajTrajDOCA(slc, dtj, ptj, dpt, ppt, doca);
343  // reverse the daughter if the closest point is near end 1 of the daughter
344  if (prt) mf::LogVerbatim("TC") << "Set parent " << ptj.ID << " dtr " << dtj.ID;
345  // remove that entry
346  pardtr.pop_back();
347  // Add entries for new daughters
348  for (unsigned short end = 0; end < 2; ++end) {
349  if (dtj.VtxID[end] == 0) continue;
350  auto& vx2 = slc.vtxs[dtj.VtxID[end] - 1];
351  if (lookedAt2[vx2.ID]) continue;
352  lookedAt2[vx2.ID] = true;
353  auto tjlist = GetVtxTjIDs(slc, vx2);
354  for (auto tjid : tjlist) {
355  if (tjid == dtj.ID || tjid == ptj.ID) continue;
356  pardtr.push_back(std::make_pair(dtj.ID, tjid));
357  if (prt) {
358  mf::LogVerbatim myprt("TC");
359  myprt << " add par_dtr";
360  for (auto pdtr : pardtr)
361  myprt << " " << pdtr.first << "_" << pdtr.second;
362  }
363  }
364  } // end
365  if (pardtr.empty()) break;
366  } // nit
367  } // indx
368  // check the master list
369  for (auto tjid : masterlist) {
370  auto& tj = slc.tjs[tjid - 1];
371  if (tj.ParentID < 0) tj.ParentID = tj.ID;
372  } // tjid
373 
374  } // DefineTjParents
375 
377  float MaxChargeAsymmetry(TCSlice& slc, std::vector<int> const& tjIDs)
378  {
379  // calculates the maximum charge asymmetry in all planes using the supplied list of Tjs
380  if (tjIDs.size() < 2) return 1;
381  std::vector<float> plnchg(slc.nPlanes);
382  for (auto tjid : tjIDs) {
383  if (tjid <= 0 || tjid > (int)slc.tjs.size()) return 1;
384  auto& tj = slc.tjs[tjid - 1];
385  if (tj.TotChg == 0) UpdateTjChgProperties("MCA", slc, tj, false);
386  unsigned short plane = DecodeCTP(tj.CTP).Plane;
387  plnchg[plane] += tj.TotChg;
388  } // tjid
389  float aveChg = 0;
390  float cnt = 0;
391  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
392  if (plnchg[plane] == 0) continue;
393  aveChg += plnchg[plane];
394  ++cnt;
395  } // plane
396  if (cnt < 2) return 1;
397  aveChg /= cnt;
398  float maxAsym = 0;
399  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
400  // ignore zeros
401  if (plnchg[plane] == 0) continue;
402  float asym = std::abs(plnchg[plane] - aveChg) / (plnchg[plane] + aveChg);
403  if (asym > maxAsym) maxAsym = asym;
404  } // plane
405  return maxAsym;
406  } // MaxChargeAsymmetry
407 
409  int PDGCodeVote(const TCSlice& slc, const std::vector<int>& tjIDs)
410  {
411  // Returns the most likely PDGCode for the set of Tjs provided
412  // The PDG codes are:
413  // 0 = your basic track-like trajectory
414  // 11 = Tagged delta-ray
415  // 13 = Tagged muon
416  // 211 = pion-like. There exists a Bragg peak at an end with a vertex
417  // 2212 = proton-like. There exists a Bragg peak at an end without a vertex
418  std::array<int, 5> codeList = {{0, 11, 13, 111, 211}};
419  unsigned short codeIndex = 0;
420  if (tjIDs.empty()) return codeList[codeIndex];
421 
422  std::array<unsigned short, 5> cnts;
423  cnts.fill(0);
424  float maxLen = 0;
425  for (auto tjid : tjIDs) {
426  if (tjid <= 0 || tjid > (int)slc.tjs.size()) continue;
427  auto& tj = slc.tjs[tjid - 1];
428  for (unsigned short ii = 0; ii < 5; ++ii)
429  if (tj.PDGCode == codeList[ii]) ++cnts[ii];
430  float len = TrajLength(tj);
431  if (len > maxLen) maxLen = len;
432  } // tjid
433  unsigned maxCnt = 0;
434  // ignore the first PDG code in the list (the default)
435  for (unsigned short ii = 1; ii < 5; ++ii) {
436  if (cnts[ii] > maxCnt) {
437  maxCnt = cnts[ii];
438  codeIndex = ii;
439  }
440  } // ii
441  return codeList[codeIndex];
442  } // PDGCodeVote
443 
445  int NeutrinoPrimaryTjID(const TCSlice& slc, const Trajectory& tj)
446  {
447  // Returns the ID of the grandparent of this tj that is a primary tj that is attached
448  // to the neutrino vertex. 0 is returned if this condition is not met.
449  if (tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) return -1;
450  if (tj.ParentID <= 0) return -1;
451  int primID = PrimaryID(slc, tj);
452  if (primID <= 0 || primID > (int)slc.tjs.size()) return -1;
453 
454  // We have the ID of the primary tj. Now see if it is attached to the neutrino vertex
455  auto& ptj = slc.tjs[primID - 1];
456  for (unsigned short end = 0; end < 2; ++end) {
457  if (ptj.VtxID[end] == 0) continue;
458  auto& vx2 = slc.vtxs[ptj.VtxID[end] - 1];
459  if (vx2.Vx3ID == 0) continue;
460  auto& vx3 = slc.vtx3s[vx2.Vx3ID - 1];
461  if (vx3.Neutrino) return primID;
462  } // end
463  return -1;
464  } // NeutrinoPrimaryTjUID
465 
467  int PrimaryID(const TCSlice& slc, const Trajectory& tj)
468  {
469  // Returns the ID of the grandparent trajectory of this trajectory that is a primary
470  // trajectory (i.e. whose ParentID = 0).
471  if (tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) return -1;
472  if (tj.ParentID < 0 || tj.ParentID > (int)slc.tjs.size()) return -1;
473  if (tj.ParentID == 0) return tj.ID;
474  int parid = tj.ParentID;
475  for (unsigned short nit = 0; nit < 10; ++nit) {
476  if (parid < 1 || parid > (int)slc.tjs.size()) break;
477  auto& tj = slc.tjs[parid - 1];
478  if (tj.ParentID < 0 || tj.ParentID > (int)slc.tjs.size()) return -1;
479  if (tj.ParentID == 0) return tj.ID;
480  parid = tj.ParentID;
481  } // nit
482  return -1;
483  } // PrimaryID
484 
486  int PrimaryUID(const PFPStruct& pfp)
487  {
488  // returns the UID of the most upstream PFParticle (that is not a neutrino)
489 
490  if (int(pfp.ParentUID) == pfp.UID || pfp.ParentUID <= 0) return pfp.ID;
491  int paruid = pfp.ParentUID;
492  int dtruid = pfp.UID;
493  unsigned short nit = 0;
494  while (true) {
495  auto slcIndx = GetSliceIndex("P", paruid);
496  auto& parent = slices[slcIndx.first].pfps[slcIndx.second];
497  // found a neutrino
498  if (parent.PDGCode == 14 || parent.PDGCode == 12) return dtruid;
499  // found a primary PFParticle?
500  if (parent.ParentUID == 0) return parent.UID;
501  if (int(parent.ParentUID) == parent.UID) return parent.UID;
502  dtruid = parent.UID;
503  paruid = parent.ParentUID;
504  if (paruid < 0) return 0;
505  ++nit;
506  if (nit == 10) return 0;
507  }
508  } // PrimaryUID
509 
511  bool MergeTjIntoPFP(TCSlice& slc, int mtjid, PFPStruct& pfp, bool prt)
512  {
513  // Tries to merge Tj with ID tjid into PFParticle pfp
514  if (mtjid > (int)slc.tjs.size()) return false;
515  auto const& mtj = slc.tjs[mtjid - 1];
516  // find the Tj in pfp.TjIDs which it should be merged with
517  int otjid = 0;
518  for (auto tjid : pfp.TjIDs) {
519  auto const& otj = slc.tjs[tjid - 1];
520  if (otj.CTP == mtj.CTP) {
521  otjid = tjid;
522  break;
523  }
524  } // tjid
525  if (otjid == 0) return false;
526  if (MergeAndStore(slc, otjid - 1, mtjid - 1, prt)) {
527  int newtjid = slc.tjs.size();
528  if (prt)
529  mf::LogVerbatim("TC") << "MergeTjIntoPFP: merged T" << otjid << " with T" << mtjid
530  << " -> T" << newtjid;
531  std::replace(pfp.TjIDs.begin(), pfp.TjIDs.begin(), otjid, newtjid);
532  return true;
533  }
534  else {
535  if (prt)
536  mf::LogVerbatim("TC") << "MergeTjIntoPFP: merge T" << otjid << " with T" << mtjid
537  << " failed ";
538  return false;
539  }
540  } // MergeTjIntoPFP
541 
543  float PointPull(Point2_t pos, float chg, const Trajectory& tj)
544  {
545  // returns the combined position and charge pull for the charge at pos
546  // relative to the Tj closest to that point using a loose requirement on position separation.
547  if (tj.AlgMod[kKilled]) return 100;
548  if (tj.AveChg <= 0) return 100;
549  // find the closest point on the tj to pos
550  unsigned short closePt = USHRT_MAX;
551  float close = 1000;
552  for (unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
553  auto& tp = tj.Pts[ipt];
554  float sep2 = PosSep2(pos, tp.Pos);
555  if (sep2 > close) continue;
556  close = sep2;
557  closePt = ipt;
558  } // ipt
559  if (closePt == USHRT_MAX) return 100;
560  // find the delta between the projection of the Tj close TP to inTP
561  auto& tp = tj.Pts[closePt];
562  float delta = PointTrajDOCA(pos[0], pos[1], tp);
563  // estimate the proejcted position error (roughly)
564  float posErr = tp.DeltaRMS;
565  if (tp.AngErr > 0 && close > 10) posErr += sqrt(tp.AngErr * sqrt(close));
566  if (posErr < 0.1) posErr = 0.1;
567  float posPull = delta / posErr;
568  float chgErr = tj.ChgRMS;
569  if (chgErr < 0.15) chgErr = 0.15;
570  float chgPull = std::abs(chg / tj.AveChg - 1) / chgErr;
571  // return a simple average
572  return 0.5 * (posPull + chgPull);
573  } // PointPull
574 
576  bool CompatibleMerge(const TCSlice& slc, std::vector<int> const& tjIDs, bool prt)
577  {
578  // Returns true if the last Tj in tjIDs has a topology consistent with it being
579  // merged with other Tjs in the same plane in the list. This is done by requiring that
580  // the closest TP between the last Tj and any other Tj is EndPt[0] or EndPt[1]. This is
581  // shown graphically here where the numbers represent the ID of a Tj that has a TP on a wire.
582  // Assume that TjIDs = {1, 2, 3, 4, 7} where T1 and T3 are in plane 0, T2 is in plane 1 and
583  // T4 is in plane 2. T7, in plane 0, was added to TjIDs with the intent of merging it with
584  // T1 and T3 into a single trajectory. This is a compatible merge if Tj7 has the following
585  // topology:
586  // 111111 333333 7777777
587  // This is an incompatible topology
588  // 111111 333333
589  // 7777777777
590  if (tjIDs.size() < 2) return false;
591  unsigned short lasttj = tjIDs[tjIDs.size() - 1] - 1;
592  auto const& mtj = slc.tjs[lasttj];
593  bool mtjIsShort = (mtj.Pts.size() < 5);
594  // minimum separation from each end of mtj
595  std::array<float, 2> minsep2{{1000, 1000}};
596  // ID of the Tj with the minimum separation
597  std::array<int, 2> minsepTj{{0, 0}};
598  // and the index of the point on that Tj
599  std::array<unsigned short, 2> minsepPt;
600  // determine the end of the closest Tj point. Start by assuming
601  // the closest Tj point is not near an end (end = 0);
602  std::array<unsigned short, 2> minsepEnd;
603  for (auto tjid : tjIDs) {
604  auto& tj = slc.tjs[tjid - 1];
605  if (tj.CTP != mtj.CTP) continue;
606  if (tj.ID == mtj.ID) continue;
607  for (unsigned short mend = 0; mend < 2; ++mend) {
608  Point2_t mendPos = mtj.Pts[mtj.EndPt[mend]].Pos;
609  float sep2 = minsep2[mend];
610  unsigned short closePt = 0;
611  if (!TrajClosestApproach(tj, mendPos[0], mendPos[1], closePt, sep2)) continue;
612  minsep2[mend] = sep2;
613  minsepTj[mend] = tjid;
614  minsepPt[mend] = closePt;
615  // set the end to a bogus value (not near an end)
616  minsepEnd[mend] = 2;
617  short dend0 = abs((short)closePt - tj.EndPt[0]);
618  short dend1 = abs((short)closePt - tj.EndPt[1]);
619  if (dend0 < dend1 && dend0 < 3) minsepEnd[mend] = 0;
620  if (dend1 < dend0 && dend1 < 3) minsepEnd[mend] = 1;
621  } // mend
622  } // tjid
623  // don't require that the minsepTjs be the same. This would reject this topology
624  // 111111 333333 7777777
625  // if mtj.ID = 3
626  bool isCompatible = (minsepEnd[0] != 2 && minsepEnd[1] != 2);
627  // check for large separation between the closest points for short Tjs
628  if (isCompatible && mtjIsShort) {
629  float minminsep = minsep2[0];
630  if (minsep2[1] < minminsep) minminsep = minsep2[1];
631  // require that the separation be less than sqrt(5)
632  isCompatible = minminsep < 5;
633  }
634  if (prt) {
635  mf::LogVerbatim myprt("TC");
636  myprt << "CompatibleMerge: T" << mtj.ID << " end";
637  for (unsigned short end = 0; end < 2; ++end)
638  myprt << " T" << minsepTj[end] << "_I" << minsepPt[end] << "_E" << minsepEnd[end]
639  << " minsep " << sqrt(minsep2[end]);
640  myprt << " Compatible? " << isCompatible;
641  } // prt
642  return isCompatible;
643 
644  } // CompatibleMerge
645 
647  bool CompatibleMerge(const Trajectory& tj1, const Trajectory& tj2, bool prt)
648  {
649  // returns true if the two Tjs are compatible with and end0-end1 merge. This function has many aspects of the
650  // compatibility checks done in EndMerge but with looser cuts.
651  if (tj1.AlgMod[kKilled] || tj2.AlgMod[kKilled]) return false;
652  if (tj1.AlgMod[kHaloTj] || tj2.AlgMod[kHaloTj]) return false;
653  if (tj1.CTP != tj2.CTP) return false;
654  unsigned short end1 = -1, end2 = 0;
655  float minLen = PosSep(tj1.Pts[tj1.EndPt[0]].Pos, tj1.Pts[tj1.EndPt[1]].Pos);
656  float len2 = PosSep(tj2.Pts[tj2.EndPt[0]].Pos, tj2.Pts[tj2.EndPt[1]].Pos);
657  if (len2 < minLen) minLen = len2;
658  minLen *= 1.2;
659  if (minLen > 10) minLen = 10;
660  for (unsigned short e1 = 0; e1 < 2; ++e1) {
661  auto& tp1 = tj1.Pts[tj1.EndPt[e1]];
662  for (unsigned short e2 = 0; e2 < 2; ++e2) {
663  auto& tp2 = tj2.Pts[tj2.EndPt[e2]];
664  float sep = PosSep(tp1.Pos, tp2.Pos);
665  if (sep < minLen) {
666  minLen = sep;
667  end1 = e1;
668  end2 = e2;
669  }
670  } // e2
671  } // e1
672  // require end to end
673  if (end2 != 1 - end1) return false;
674 
675  float overlapFraction = OverlapFraction(tj1, tj2);
676  if (overlapFraction > 0.25) {
677  if (prt)
678  mf::LogVerbatim("TC") << "CM: " << tj1.ID << " " << tj2.ID << " overlapFraction "
679  << overlapFraction << " > 0.25 ";
680  return false;
681  }
682 
683  auto& tp1 = tj1.Pts[tj1.EndPt[end1]];
684  auto& tp2 = tj2.Pts[tj2.EndPt[end2]];
685  float doca1 = PointTrajDOCA(tp1.Pos[0], tp1.Pos[1], tp2);
686  float doca2 = PointTrajDOCA(tp2.Pos[0], tp2.Pos[1], tp1);
687  if (doca1 > 2 && doca2 > 2) {
688  if (prt)
689  mf::LogVerbatim("TC") << "CM: " << tj1.ID << " " << tj2.ID << " Both docas > 2 " << doca1
690  << " " << doca2;
691  return false;
692  }
693 
694  float dang = DeltaAngle(tp1.Ang, tp2.Ang);
695  if (dang > 2 * tcc.kinkCuts[0]) {
696  if (prt)
697  mf::LogVerbatim("TC") << "CM: " << tj1.ID << " " << tj2.ID << " dang " << dang << " > "
698  << 2 * tcc.kinkCuts[0];
699  return false;
700  }
701 
702  return true;
703  } // CompatibleMerge
704 
706  float OverlapFraction(const Trajectory& tj1, const Trajectory& tj2)
707  {
708  // returns the fraction of wires spanned by two trajectories
709  float minWire = 1E6;
710  float maxWire = -1E6;
711 
712  float cnt1 = 0;
713  for (auto& tp : tj1.Pts) {
714  if (tp.Chg == 0) continue;
715  if (tp.Pos[0] < 0) continue;
716  if (tp.Pos[0] < minWire) minWire = tp.Pos[0];
717  if (tp.Pos[0] > maxWire) maxWire = tp.Pos[0];
718  ++cnt1;
719  }
720  if (cnt1 == 0) return 0;
721  float cnt2 = 0;
722  for (auto& tp : tj2.Pts) {
723  if (tp.Chg == 0) continue;
724  if (tp.Pos[0] < 0) continue;
725  if (tp.Pos[0] < minWire) minWire = tp.Pos[0];
726  if (tp.Pos[0] > maxWire) maxWire = tp.Pos[0];
727  ++cnt2;
728  }
729  if (cnt2 == 0) return 0;
730  int span = maxWire - minWire;
731  if (span <= 0) return 0;
732  std::vector<unsigned short> wcnt(span);
733  for (auto& tp : tj1.Pts) {
734  if (tp.Chg == 0) continue;
735  if (tp.Pos[0] < -0.4) continue;
736  int indx = std::nearbyint(tp.Pos[0] - minWire);
737  if (indx < 0 || indx > span - 1) continue;
738  ++wcnt[indx];
739  }
740  for (auto& tp : tj2.Pts) {
741  if (tp.Chg == 0) continue;
742  if (tp.Pos[0] < -0.4) continue;
743  int indx = std::nearbyint(tp.Pos[0] - minWire);
744  if (indx < 0 || indx > span - 1) continue;
745  ++wcnt[indx];
746  }
747  float cntOverlap = 0;
748  for (auto cnt : wcnt)
749  if (cnt > 1) ++cntOverlap;
750  if (cnt1 < cnt2) { return cntOverlap / cnt1; }
751  else {
752  return cntOverlap / cnt2;
753  }
754 
755  } // OverlapFraction
756 
758  unsigned short AngleRange(TrajPoint const& tp)
759  {
760  return AngleRange(tp.Ang);
761  }
762 
765  {
766  unsigned short ar = AngleRange(tp.Ang);
767  if (ar == tcc.angleRanges.size() - 1) {
768  // Very large angle
769  tp.AngleCode = 2;
770  }
771  else if (tcc.angleRanges.size() > 2 && ar == tcc.angleRanges.size() - 2) {
772  // Large angle
773  tp.AngleCode = 1;
774  }
775  else {
776  // Small angle
777  tp.AngleCode = 0;
778  }
779 
780  } // SetAngleCode
781 
783  unsigned short AngleRange(float angle)
784  {
785  // returns the index of the angle range
786  if (angle > M_PI) angle = M_PI;
787  if (angle < -M_PI) angle = M_PI;
788  if (angle < 0) angle = -angle;
789  if (angle > M_PI / 2) angle = M_PI - angle;
790  for (unsigned short ir = 0; ir < tcc.angleRanges.size(); ++ir) {
791  if (angle < tcc.angleRanges[ir]) return ir;
792  }
793  return tcc.angleRanges.size() - 1;
794  } // AngleRange
795 
797  void FitTraj(TCSlice const& slc, Trajectory& tj)
798  {
799  // Jacket around FitTraj to fit the leading edge of the supplied trajectory
800  unsigned short originPt = tj.EndPt[1];
801  unsigned short npts = tj.Pts[originPt].NTPsFit;
802  TrajPoint tpFit;
803  unsigned short fitDir = -1;
804  FitTraj(slc, tj, originPt, npts, fitDir, tpFit);
805  tj.Pts[originPt] = tpFit;
806 
807  } // FitTraj
808 
810  void FitTraj(TCSlice const& slc,
811  Trajectory& tj,
812  unsigned short originPt,
813  unsigned short npts,
814  short fitDir,
815  TrajPoint& tpFit)
816  {
817  // Fit the supplied trajectory using HitPos positions with the origin at originPt.
818  // The npts is interpreted as the number of points on each side of the origin
819  // The allowed modes are as follows, where i denotes a TP that is included, . denotes
820  // a TP with no hits, and x denotes a TP that is not included
821  //TP 012345678 fitDir originPt npts
822  // Oiiixxxxx 1 0 4 << npts in the fit
823  // xi.iiOxxx -1 5 4
824  // xiiiOiiix 0 4 4 << 2 * npts + 1 points in the fit
825  // xxxiO.ixx 0 4 1
826  // 0iiixxxxx 0 0 4
827  // This routine puts the results into tp if the fit is successfull. The
828  // fit "direction" is in increasing order along the trajectory from 0 to tj.Pts.size() - 1.
829 
830  // static const float twoPi = 2 * M_PI;
831 
832  if (originPt > tj.Pts.size() - 1) {
833  mf::LogWarning("TC") << "FitTraj: Requesting fit of invalid TP " << originPt;
834  return;
835  }
836 
837  // copy the origin TP into the fit TP
838  tpFit = tj.Pts[originPt];
839  // Assume that the fit will fail
840  tpFit.FitChi = 999;
841  if (fitDir < -1 || fitDir > 1) return;
842 
843  std::vector<double> x, y;
844  Point2_t origin = tj.Pts[originPt].HitPos;
845  // Use TP position if there aren't any hits on it
846  if (tj.Pts[originPt].Chg == 0) origin = tj.Pts[originPt].Pos;
847 
848  // simple two point case
849  if (NumPtsWithCharge(slc, tj, false) == 2) {
850  for (unsigned short ipt = tj.EndPt[0]; ipt < tj.EndPt[1]; ++ipt) {
851  if (tj.Pts[ipt].Chg <= 0) continue;
852  double xx = tj.Pts[ipt].HitPos[0] - origin[0];
853  double yy = tj.Pts[ipt].HitPos[1] - origin[1];
854  x.push_back(xx);
855  y.push_back(yy);
856  } // ii
857  if (x.size() != 2) return;
858  if (x[0] == x[1]) {
859  // Either + or - pi/2
860  tpFit.Ang = M_PI / 2;
861  if (y[1] < y[0]) tpFit.Ang = -tpFit.Ang;
862  }
863  else {
864  double dx = x[1] - x[0];
865  double dy = y[1] - y[0];
866  tpFit.Ang = atan2(dy, dx);
867  }
868  tpFit.Dir[0] = cos(tpFit.Ang);
869  tpFit.Dir[1] = sin(tpFit.Ang);
870  tpFit.Pos[0] += origin[0];
871  tpFit.Pos[1] += origin[1];
872  tpFit.AngErr = 0.01;
873  tpFit.FitChi = 0.01;
874  SetAngleCode(tpFit);
875  return;
876  } // two points
877 
878  std::vector<double> w, q;
879  std::array<double, 2> dir;
880  double xx, yy, xr, yr;
881  double chgWt;
882 
883  // Rotate the traj hit position into the coordinate system defined by the
884  // originPt traj point, where x = along the trajectory, y = transverse
885  double rotAngle = tj.Pts[originPt].Ang;
886  double cs = cos(-rotAngle);
887  double sn = sin(-rotAngle);
888 
889  // enter the originPT hit info if it exists
890  if (tj.Pts[originPt].Chg > 0) {
891  xx = tj.Pts[originPt].HitPos[0] - origin[0];
892  yy = tj.Pts[originPt].HitPos[1] - origin[1];
893  xr = cs * xx - sn * yy;
894  yr = sn * xx + cs * yy;
895  x.push_back(xr);
896  y.push_back(yr);
897  chgWt = tj.Pts[originPt].ChgPull;
898  if (chgWt < 1) chgWt = 1;
899  chgWt *= chgWt;
900  w.push_back(chgWt * tj.Pts[originPt].HitPosErr2);
901  }
902 
903  // correct npts to account for the origin point
904  if (fitDir != 0) --npts;
905 
906  // step in the + direction first
907  if (fitDir != -1) {
908  unsigned short cnt = 0;
909  for (unsigned short ipt = originPt + 1; ipt < tj.Pts.size(); ++ipt) {
910  if (tj.Pts[ipt].Chg <= 0) continue;
911  xx = tj.Pts[ipt].HitPos[0] - origin[0];
912  yy = tj.Pts[ipt].HitPos[1] - origin[1];
913  xr = cs * xx - sn * yy;
914  yr = sn * xx + cs * yy;
915  x.push_back(xr);
916  y.push_back(yr);
917  chgWt = tj.Pts[ipt].ChgPull;
918  if (chgWt < 1) chgWt = 1;
919  chgWt *= chgWt;
920  w.push_back(chgWt * tj.Pts[ipt].HitPosErr2);
921  ++cnt;
922  if (cnt == npts) break;
923  } // ipt
924  } // fitDir != -1
925 
926  // step in the - direction next
927  if (fitDir != 1 && originPt > 0) {
928  unsigned short cnt = 0;
929  for (unsigned short ii = 1; ii < tj.Pts.size(); ++ii) {
930  unsigned short ipt = originPt - ii;
931  if (ipt > tj.Pts.size() - 1) continue;
932  if (tj.Pts[ipt].Chg == 0) continue;
933  xx = tj.Pts[ipt].HitPos[0] - origin[0];
934  yy = tj.Pts[ipt].HitPos[1] - origin[1];
935  xr = cs * xx - sn * yy;
936  yr = sn * xx + cs * yy;
937  x.push_back(xr);
938  y.push_back(yr);
939  chgWt = tj.Pts[ipt].ChgPull;
940  if (chgWt < 1) chgWt = 1;
941  chgWt *= chgWt;
942  w.push_back(chgWt * tj.Pts[ipt].HitPosErr2);
943  ++cnt;
944  if (cnt == npts) break;
945  if (ipt == 0) break;
946  } // ipt
947  } // fitDir != -1
948 
949  // Not enough points to define a line?
950  if (x.size() < 2) return;
951 
952  double sum = 0.;
953  double sumx = 0.;
954  double sumy = 0.;
955  double sumxy = 0.;
956  double sumx2 = 0.;
957  double sumy2 = 0.;
958 
959  // weight by the charge ratio and accumulate sums
960  double wght;
961  for (unsigned short ipt = 0; ipt < x.size(); ++ipt) {
962  if (w[ipt] < 0.00001) w[ipt] = 0.00001;
963  wght = 1 / w[ipt];
964  sum += wght;
965  sumx += wght * x[ipt];
966  sumy += wght * y[ipt];
967  sumx2 += wght * x[ipt] * x[ipt];
968  sumy2 += wght * y[ipt] * y[ipt];
969  sumxy += wght * x[ipt] * y[ipt];
970  }
971  // calculate coefficients and std dev
972  double delta = sum * sumx2 - sumx * sumx;
973  if (delta == 0) return;
974  // A is the intercept
975  double A = (sumx2 * sumy - sumx * sumxy) / delta;
976  // B is the slope
977  double B = (sumxy * sum - sumx * sumy) / delta;
978 
979  // The chisq will be set below if there are enough points. Don't allow it to be 0
980  // so we can take Chisq ratios later
981  tpFit.FitChi = 0.01;
982  double newang = atan(B);
983  dir[0] = cos(newang);
984  dir[1] = sin(newang);
985  // rotate back into the (w,t) coordinate system
986  cs = cos(rotAngle);
987  sn = sin(rotAngle);
988  tpFit.Dir[0] = cs * dir[0] - sn * dir[1];
989  tpFit.Dir[1] = sn * dir[0] + cs * dir[1];
990  // ensure that the direction is consistent with the originPt direction
991  bool flipDir = false;
992  if (AngleRange(tj.Pts[originPt]) > 0) {
993  flipDir = std::signbit(tpFit.Dir[1]) != std::signbit(tj.Pts[originPt].Dir[1]);
994  }
995  else {
996  flipDir = std::signbit(tpFit.Dir[0]) != std::signbit(tj.Pts[originPt].Dir[0]);
997  }
998  if (flipDir) {
999  tpFit.Dir[0] = -tpFit.Dir[0];
1000  tpFit.Dir[1] = -tpFit.Dir[1];
1001  }
1002  tpFit.Ang = atan2(tpFit.Dir[1], tpFit.Dir[0]);
1003  SetAngleCode(tpFit);
1004 
1005  // rotate (0, intcpt) into (W,T) coordinates
1006  tpFit.Pos[0] = -sn * A + origin[0];
1007  tpFit.Pos[1] = cs * A + origin[1];
1008  // force the origin to be at origin[0]
1009  if (tpFit.AngleCode < 2) MoveTPToWire(tpFit, origin[0]);
1010 
1011  if (x.size() < 3) return;
1012 
1013  // Calculate chisq/DOF
1014  double ndof = x.size() - 2;
1015  double varnce =
1016  (sumy2 + A * A * sum + B * B * sumx2 - 2 * (A * sumy + B * sumxy - A * B * sumx)) / ndof;
1017  if (varnce > 0.) {
1018  // Intercept error is not used
1019  // InterceptError = sqrt(varnce * sumx2 / delta);
1020  double slopeError = sqrt(varnce * sum / delta);
1021  tpFit.AngErr = std::abs(atan(slopeError));
1022  }
1023  else {
1024  tpFit.AngErr = 0.01;
1025  }
1026  sum = 0;
1027  // calculate chisq
1028  double arg;
1029  for (unsigned short ii = 0; ii < y.size(); ++ii) {
1030  arg = y[ii] - A - B * x[ii];
1031  sum += arg * arg / w[ii];
1032  }
1033  tpFit.FitChi = sum / ndof;
1034  } // FitTraj
1035 
1037  unsigned short GetPFPIndex(const TCSlice& slc, int tjID)
1038  {
1039  if (slc.pfps.empty()) return USHRT_MAX;
1040  for (unsigned int ipfp = 0; ipfp < slc.pfps.size(); ++ipfp) {
1041  const auto& pfp = slc.pfps[ipfp];
1042  if (std::find(pfp.TjIDs.begin(), pfp.TjIDs.end(), tjID) != pfp.TjIDs.end()) return ipfp;
1043  } // indx
1044  return USHRT_MAX;
1045  } // GetPFPIndex
1046 
1048  void ReleaseHits(TCSlice& slc, Trajectory const& tj)
1049  {
1050  // Sets InTraj[] = 0 for all TPs in work. Called when abandoning work
1051  for (auto const& tp : tj.Pts) {
1052  for (auto iht : tp.Hits) {
1053  if (slc.slHits[iht].InTraj == tj.ID) slc.slHits[iht].InTraj = 0;
1054  }
1055  } // tp
1056 
1057  } // ReleaseWorkHits
1058 
1061  {
1062  // Sets InTraj = 0 and UseHit false for all used hits in tp
1063  for (unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1064  if (tp.UseHit[ii]) {
1065  slc.slHits[tp.Hits[ii]].InTraj = 0;
1066  tp.UseHit[ii] = false;
1067  } // UseHit
1068  } // ii
1069  tp.Chg = 0;
1070  } // UnsetUsedHits
1071 
1073  bool StoreTraj(TCSlice& slc, Trajectory& tj)
1074  {
1075  // check for errors
1076  for (auto& tp : tj.Pts) {
1077  if (tp.Hits.size() > 16) return false;
1078  } // tp
1079 
1080  if (tj.NeedsUpdate) UpdateTjChgProperties("ST", slc, tj, false);
1081 
1082  // This shouldn't be necessary but do it anyway
1083  SetEndPoints(tj);
1084 
1085  if (slc.tjs.size() >= USHRT_MAX || tj.EndPt[1] <= tj.EndPt[0] || tj.EndPt[1] > tj.Pts.size()) {
1086  ReleaseHits(slc, tj);
1087  return false;
1088  }
1089 
1090  unsigned short npts = tj.EndPt[1] - tj.EndPt[0] + 1;
1091  if (npts < 2) return false;
1092 
1093  auto& endTp0 = tj.Pts[tj.EndPt[0]];
1094  auto& endTp1 = tj.Pts[tj.EndPt[1]];
1095 
1096  // ensure that angle errors are defined at both ends, ignoring junk Tjs
1097  if (!tj.AlgMod[kJunkTj]) {
1098  if (endTp0.AngErr == 0.1 && endTp1.AngErr != 0.1) { endTp0.AngErr = endTp1.AngErr; }
1099  else if (endTp0.AngErr != 0.1 && endTp1.AngErr == 0.1) {
1100  endTp1.AngErr = endTp0.AngErr;
1101  }
1102  } // not a junk Tj
1103 
1104  // Calculate the charge near the end and beginning if necessary. This must be a short
1105  // trajectory. Find the average using 4 points
1106  if (endTp0.AveChg <= 0) {
1107  unsigned short cnt = 0;
1108  float sum = 0;
1109  for (unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
1110  if (tj.Pts[ipt].Chg == 0) continue;
1111  sum += tj.Pts[ipt].Chg;
1112  ++cnt;
1113  if (cnt == 4) break;
1114  }
1115  tj.Pts[tj.EndPt[0]].AveChg = sum / (float)cnt;
1116  }
1117  if (endTp1.AveChg <= 0 && npts < 5) endTp1.AveChg = endTp0.AveChg;
1118  if (endTp1.AveChg <= 0) {
1119  float sum = 0;
1120  unsigned short cnt = 0;
1121  for (unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
1122  short ipt = tj.EndPt[1] - ii;
1123  if (ipt < 0) break;
1124  if (tj.Pts[ipt].Chg == 0) continue;
1125  sum += tj.Pts[ipt].Chg;
1126  ++cnt;
1127  if (cnt == 4) break;
1128  if (ipt == 0) break;
1129  } // ii
1130  tj.Pts[tj.EndPt[1]].AveChg = sum / (float)cnt;
1131  } // begin charge == end charge
1132 
1133  // update the kink significance
1134  if (!tj.AlgMod[kJunkTj]) {
1135  unsigned short nPtsFit = tcc.kinkCuts[0];
1136  bool useChg = (tcc.kinkCuts[2] > 0);
1137  if (npts > 2 * nPtsFit) {
1138  for (unsigned short ipt = tj.EndPt[0] + nPtsFit; ipt < tj.EndPt[1] - nPtsFit; ++ipt) {
1139  auto& tp = tj.Pts[ipt];
1140  if (tp.KinkSig < 0) tp.KinkSig = KinkSignificance(slc, tj, ipt, nPtsFit, useChg, false);
1141  }
1142  } // long trajectory
1143  } // not JunkTj
1144 
1145  UpdateTjChgProperties("ST", slc, tj, false);
1146 
1147  int trID = slc.tjs.size() + 1;
1148 
1149  for (unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
1150  for (unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
1151  if (tj.Pts[ipt].UseHit[ii]) {
1152  unsigned int iht = tj.Pts[ipt].Hits[ii];
1153  if (iht > slc.slHits.size() - 1) {
1154  ReleaseHits(slc, tj);
1155  return false;
1156  }
1157  if (slc.slHits[iht].InTraj > 0) {
1158  ReleaseHits(slc, tj);
1159  return false;
1160  } // error
1161  slc.slHits[iht].InTraj = trID;
1162  }
1163  } // ii
1164  } // ipt
1165 
1166  // ensure that inTraj is clean for the ID
1167  for (unsigned int iht = 0; iht < slc.slHits.size(); ++iht) {
1168  if (slc.slHits[iht].InTraj == tj.ID) {
1169  mf::LogWarning("TC") << "StoreTraj: Hit " << PrintHit(slc.slHits[iht])
1170  << " thinks it belongs to T" << tj.ID << " but it isn't in the Tj\n";
1171  return false;
1172  }
1173  } // iht
1174 
1175  tj.WorkID = tj.ID;
1176  tj.ID = trID;
1177  // increment the global ID
1178  ++evt.globalT_UID;
1179  tj.UID = evt.globalT_UID;
1180  // Don't clobber the ParentID if it was defined by the calling function
1181  if (tj.ParentID == 0) tj.ParentID = trID;
1182  slc.tjs.push_back(tj);
1183  if (tcc.modes[kDebug] && tcc.dbgSlc && debug.Hit != UINT_MAX) {
1184  // print some debug info
1185  for (unsigned short ipt = 0; ipt < tj.Pts.size(); ++ipt) {
1186  for (unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
1187  unsigned int iht = tj.Pts[ipt].Hits[ii];
1188  if (slc.slHits[iht].allHitsIndex == debug.Hit) {
1189  std::cout << "Debug hit appears in trajectory w WorkID " << tj.WorkID << " UseHit "
1190  << tj.Pts[ipt].UseHit[ii] << "\n";
1191  }
1192  } // ii
1193  } // ipt
1194  } // debug.Hit ...
1195 
1196  return true;
1197 
1198  } // StoreTraj
1199 
1201  void FitPar(const Trajectory& tj,
1202  unsigned short originPt,
1203  unsigned short npts,
1204  short fitDir,
1205  ParFit& pFit,
1206  unsigned short usePar)
1207  {
1208  // Fit a TP parameter, like Chg or Delta, to a line using the points starting at originPT.
1209  // Currently supported values of usePar are Chg (1) and Delta (2)
1210 
1211  pFit.ChiDOF = 999;
1212  pFit.AvePar = 0.;
1213  if (originPt > tj.Pts.size() - 1) return;
1214  if (fitDir != 1 && fitDir != -1) return;
1215  Point2_t inPt;
1216  Vector2_t outVec, outVecErr;
1217  float pErr, chiDOF;
1218  Fit2D(0, inPt, pErr, outVec, outVecErr, chiDOF);
1219  unsigned short cnt = 0;
1220  for (unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
1221  unsigned short ipt = originPt + ii * fitDir;
1222  if (ipt < tj.EndPt[0] || ipt > tj.EndPt[1]) break;
1223  auto& tp = tj.Pts[ipt];
1224  if (tp.Chg <= 0) continue;
1225  // Accumulate and save points
1226  inPt[0] = std::abs(tp.Pos[0] - tj.Pts[originPt].Pos[0]);
1227  float parVal = tp.Chg;
1228  // Assume errors are 10% for a charge fit
1229  pErr = 0.1 * parVal;
1230  if (usePar > 1) {
1231  parVal = tp.Delta;
1232  // use the TP hit position error for a Delta Fit
1233  pErr = sqrt(tp.HitPosErr2);
1234  }
1235  inPt[1] = parVal;
1236  pFit.AvePar += parVal;
1237  if (!Fit2D(2, inPt, pErr, outVec, outVecErr, chiDOF)) break;
1238  ++cnt;
1239  if (cnt == npts) break;
1240  } // ii
1241  if (cnt < npts) return;
1242  // do the fit and get the results
1243  if (!Fit2D(-1, inPt, pErr, outVec, outVecErr, chiDOF)) return;
1244  pFit.Pos = tj.Pts[originPt].Pos;
1245  pFit.Par0 = outVec[0];
1246  pFit.AvePar /= (float)cnt;
1247  pFit.ParErr = outVecErr[0];
1248  pFit.Pos = tj.Pts[originPt].Pos;
1249  pFit.ParSlp = outVec[1];
1250  pFit.ParSlpErr = outVecErr[1];
1251  pFit.ChiDOF = chiDOF;
1252  pFit.nPtsFit = cnt;
1253  } // FitPar
1254 
1256  bool InTrajOK(TCSlice& slc, std::string someText)
1257  {
1258  // Check slc.tjs -> InTraj associations
1259 
1260  unsigned short tID;
1261  unsigned int iht;
1262  unsigned short itj = 0;
1263  std::vector<unsigned int> tHits;
1264  std::vector<unsigned int> atHits;
1265  for (auto& tj : slc.tjs) {
1266  // ignore abandoned trajectories
1267  if (tj.AlgMod[kKilled]) continue;
1268  tID = tj.ID;
1269  tHits = PutTrajHitsInVector(tj, kUsedHits);
1270  if (tHits.size() < 2) continue;
1271  std::sort(tHits.begin(), tHits.end());
1272  atHits.clear();
1273  for (iht = 0; iht < slc.slHits.size(); ++iht) {
1274  if (slc.slHits[iht].InTraj == tID) atHits.push_back(iht);
1275  } // iht
1276  if (atHits.size() < 2) continue;
1277  if (!std::equal(tHits.begin(), tHits.end(), atHits.begin())) {
1278  mf::LogVerbatim myprt("TC");
1279  myprt << someText << " ChkInTraj failed: inTraj - UseHit mis-match for T" << tID
1280  << " tj.WorkID " << tj.WorkID << " atHits size " << atHits.size() << " tHits size "
1281  << tHits.size() << " in CTP " << tj.CTP << "\n";
1282  myprt << "AlgMods: ";
1283  for (unsigned short ib = 0; ib < AlgBitNames.size(); ++ib)
1284  if (tj.AlgMod[ib]) myprt << " " << AlgBitNames[ib];
1285  myprt << "\n";
1286  myprt << "index inTraj UseHit \n";
1287  for (iht = 0; iht < atHits.size(); ++iht) {
1288  myprt << "iht " << iht << " " << PrintHit(slc.slHits[atHits[iht]]);
1289  if (iht < tHits.size()) myprt << " " << PrintHit(slc.slHits[tHits[iht]]);
1290  if (atHits[iht] != tHits[iht]) myprt << " <<< " << atHits[iht] << " != " << tHits[iht];
1291  myprt << "\n";
1292  } // iht
1293  if (tHits.size() > atHits.size()) {
1294  for (iht = atHits.size(); iht < atHits.size(); ++iht) {
1295  myprt << "atHits " << iht << " " << PrintHit(slc.slHits[atHits[iht]]) << "\n";
1296  } // iht
1297  PrintTrajectory("CIT", slc, tj, USHRT_MAX);
1298  } // tHit.size > atHits.size()
1299  return false;
1300  }
1301  // check the VtxID
1302  for (unsigned short end = 0; end < 2; ++end) {
1303  if (tj.VtxID[end] > slc.vtxs.size()) {
1304  mf::LogVerbatim("TC") << someText << " ChkInTraj: Bad VtxID " << tj.ID;
1305  tj.AlgMod[kKilled] = true;
1306  return false;
1307  }
1308  } // end
1309  ++itj;
1310  } // tj
1311  return true;
1312 
1313  } // InTrajOK
1314 
1316  void CheckTrajBeginChg(TCSlice& slc, unsigned short itj)
1317  {
1318  // This function is called after the beginning of the tj has been inspected to see if
1319  // reverse propagation was warranted. Trajectory points at the beginning were removed by
1320  // this process.
1321  // A search has been made for a Bragg peak with nothing
1322  // found. Here we look for a charge pattern like the following, where C means large charge
1323  // and c means lower charge:
1324  // CCCCCCccccccc
1325  // The charge in the two regions should be fairly uniform.
1326 
1327  // This function may split the trajectory so it needs to have been stored
1328  if (itj > slc.tjs.size() - 1) return;
1329  auto& tj = slc.tjs[itj];
1330 
1331  if (!tcc.useAlg[kBeginChg]) return;
1332  if (tj.EndFlag[0][kBragg]) return;
1333  if (tj.AlgMod[kFTBRvProp]) return;
1334  if (tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) return;
1335  if (tj.Pts.size() < 20) return;
1336 
1337  bool prt = (tcc.dbgSlc && (tcc.dbgStp || tcc.dbgAlg[kBeginChg]));
1338 
1339  // look for a large drop between the average charge near the beginning
1340  float chg2 = tj.Pts[tj.EndPt[0] + 2].AveChg;
1341  // and the average charge 15 points away
1342  float chg15 = tj.Pts[tj.EndPt[0] + 15].AveChg;
1343  if (chg2 < 3 * chg15) return;
1344 
1345  // find the point where the charge falls below the mid-point
1346  float midChg = 0.5 * (chg2 + chg15);
1347 
1348  unsigned short breakPt = USHRT_MAX;
1349  for (unsigned short ipt = tj.EndPt[0] + 3; ipt < 15; ++ipt) {
1350  float chgm2 = tj.Pts[ipt - 2].Chg;
1351  if (chgm2 == 0) continue;
1352  float chgm1 = tj.Pts[ipt - 1].Chg;
1353  if (chgm1 == 0) continue;
1354  float chgp1 = tj.Pts[ipt + 1].Chg;
1355  if (chgp1 == 0) continue;
1356  float chgp2 = tj.Pts[ipt + 2].Chg;
1357  if (chgp2 == 0) continue;
1358  if (chgm2 > midChg && chgm1 > midChg && chgp1 < midChg && chgp2 < midChg) {
1359  breakPt = ipt;
1360  break;
1361  }
1362  } // breakPt
1363  if (breakPt == USHRT_MAX) return;
1364  // check the charge and rms before and after the split
1365  std::array<double, 2> cnt, sum, sum2;
1366  for (unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
1367  auto& tp = tj.Pts[ipt];
1368  if (tp.Chg <= 0) continue;
1369  unsigned short end = 0;
1370  if (ipt > breakPt) end = 1;
1371  ++cnt[end];
1372  sum[end] += tp.Chg;
1373  sum2[end] += tp.Chg * tp.Chg;
1374  } // ipt
1375  for (unsigned short end = 0; end < 2; ++end) {
1376  if (cnt[end] < 3) return;
1377  double ave = sum[end] / cnt[end];
1378  double arg = sum2[end] - cnt[end] * ave * ave;
1379  if (arg <= 0) return;
1380  sum2[end] = sqrt(arg / (cnt[end] - 1));
1381  sum2[end] /= ave;
1382  sum[end] = ave;
1383  } // region
1384  bool doSplit = true;
1385  // don't split if this looks like an electron - no significant improvement
1386  // in the charge rms before and after
1387  if (tj.ChgRMS > 0.5 && sum2[0] > 0.3 && sum2[1] > 0.3) doSplit = false;
1388  if (prt) {
1389  mf::LogVerbatim myprt("TC");
1390  myprt << "CTBC: T" << tj.ID << " chgRMS " << tj.ChgRMS;
1391  myprt << " AveChg before split point " << (int)sum[0] << " rms " << sum2[0];
1392  myprt << " after " << (int)sum[1] << " rms " << sum2[1] << " doSplit? " << doSplit;
1393  } // prt
1394  if (!doSplit) return;
1395  // Create a vertex at the break point
1396  VtxStore aVtx;
1397  aVtx.Pos = tj.Pts[breakPt].Pos;
1398  aVtx.NTraj = 2;
1399  aVtx.Pass = tj.Pass;
1400  aVtx.Topo = 8;
1401  aVtx.ChiDOF = 0;
1402  aVtx.CTP = tj.CTP;
1403  aVtx.ID = slc.vtxs.size() + 1;
1404  aVtx.Stat[kFixed] = true;
1405  unsigned short ivx = slc.vtxs.size();
1406  if (!StoreVertex(slc, aVtx)) return;
1407  if (!SplitTraj(slc, itj, breakPt, ivx, prt)) {
1408  if (prt) mf::LogVerbatim("TC") << "CTBC: Failed to split trajectory";
1409  MakeVertexObsolete("CTBC", slc, slc.vtxs[ivx], false);
1410  return;
1411  }
1412  SetVx2Score(slc);
1413  slc.tjs[itj].AlgMod[kBeginChg] = true;
1414 
1415  if (prt)
1416  mf::LogVerbatim("TC") << "CTBC: Split T" << tj.ID << " at " << PrintPos(tj.Pts[breakPt].Pos)
1417  << "\n";
1418 
1419  } // CheckTrajBeginChg
1420 
1422  bool BraggSplit(TCSlice& slc, unsigned short itj)
1423  {
1424  // Searches the stored trajectory for a Bragg Peak and kink and splits it
1425  if (!tcc.useAlg[kBraggSplit]) return false;
1426  if (itj > slc.tjs.size() - 1) return false;
1427  if (tcc.chkStopCuts.size() < 4) return false;
1428  if (tcc.chkStopCuts[3] <= 0) return false;
1429  unsigned short nPtsToCheck = tcc.chkStopCuts[1];
1430  auto& tj = slc.tjs[itj];
1431  unsigned short npwc = NumPtsWithCharge(slc, tj, false);
1432  if (npwc < 4) return false;
1433  if (npwc < nPtsToCheck) nPtsToCheck = npwc;
1434  // do a rough ChgPull check first
1435  float maxPull = 2;
1436  unsigned short maxPullPt = USHRT_MAX;
1437  for (unsigned short ipt = tj.EndPt[0]; ipt < tj.EndPt[1]; ++ipt) {
1438  auto& tp = tj.Pts[ipt];
1439  if (tp.ChgPull < maxPull) continue;
1440  maxPull = tp.ChgPull;
1441  maxPullPt = ipt;
1442  } // ipt
1443  if (maxPullPt == USHRT_MAX) return false;
1444  short dpt;
1445  if (maxPullPt < 0.5 * (tj.EndPt[0] + tj.EndPt[1])) { dpt = maxPullPt - tj.EndPt[0]; }
1446  else {
1447  dpt = tj.EndPt[1] - maxPullPt;
1448  }
1449  if (dpt < 3) return false;
1450  bool prt = (tcc.dbgSlc && (tcc.dbgStp || tcc.dbgAlg[kBraggSplit]));
1451  if (prt)
1452  mf::LogVerbatim("TC") << "BS: T" << tj.ID << " maxPull " << maxPull << " at "
1453  << PrintPos(tj.Pts[maxPullPt]) << " dpt " << dpt;
1454  unsigned short breakPt = USHRT_MAX;
1455  float bestFOM = tcc.chkStopCuts[3];
1456  unsigned short bestBragg = 0;
1457  unsigned short nPtsFit = tcc.kinkCuts[0];
1458  TrajPoint tp1, tp2;
1459  ParFit chgFit1, chgFit2;
1460  for (unsigned short ipt = maxPullPt - 2; ipt <= maxPullPt + 2; ++ipt) {
1461  FitTraj(slc, tj, ipt - 1, nPtsFit, -1, tp1);
1462  if (tp1.FitChi > 10) continue;
1463  FitTraj(slc, tj, ipt + 1, nPtsFit, 1, tp2);
1464  if (tp2.FitChi > 10) continue;
1465  float dang = std::abs(tp1.Ang - tp2.Ang);
1466  FitPar(tj, ipt - 1, nPtsToCheck, -1, chgFit1, 1);
1467  if (chgFit1.ChiDOF > 100) continue;
1468  chgFit1.ParSlp = -chgFit1.ParSlp;
1469  FitPar(tj, ipt + 1, nPtsToCheck, 1, chgFit2, 1);
1470  if (chgFit2.ChiDOF > 100) continue;
1471  chgFit2.ParSlp = -chgFit2.ParSlp;
1472  // require a large positive slope on at least one side
1473  if (chgFit1.ParSlp < tcc.chkStopCuts[0] && chgFit2.ParSlp < tcc.chkStopCuts[0]) continue;
1474  // assume it is on side 1
1475  unsigned short bragg = 1;
1476  float bchi = chgFit1.ChiDOF;
1477  if (chgFit2.ParSlp > chgFit1.ParSlp) {
1478  bragg = 2;
1479  bchi = chgFit2.ChiDOF;
1480  }
1481  float chgAsym = std::abs(chgFit1.Par0 - chgFit2.Par0) / (chgFit1.Par0 + chgFit2.Par0);
1482  float slpAsym = std::abs(chgFit1.ParSlp - chgFit2.ParSlp) / (chgFit1.ParSlp + chgFit2.ParSlp);
1483  if (bchi < 1) bchi = 1;
1484  float fom = 10 * dang * chgAsym * slpAsym / bchi;
1485  if (prt) {
1486  mf::LogVerbatim myprt("TC");
1487  myprt << "pt " << PrintPos(tj.Pts[ipt]) << " " << std::setprecision(2) << dang;
1488  myprt << " chg1 " << (int)chgFit1.Par0 << " slp " << chgFit1.ParSlp << " chi "
1489  << chgFit1.ChiDOF;
1490  myprt << " chg2 " << (int)chgFit2.Par0 << " slp " << chgFit2.ParSlp << " chi "
1491  << chgFit2.ChiDOF;
1492  myprt << " chgAsym " << chgAsym;
1493  myprt << " slpAsym " << slpAsym;
1494  myprt << " fom " << fom;
1495  myprt << " bragg " << bragg;
1496  }
1497  if (fom < bestFOM) continue;
1498  bestFOM = fom;
1499  breakPt = ipt;
1500  bestBragg = bragg;
1501  } // ipt
1502  if (breakPt == USHRT_MAX) return false;
1503  if (prt)
1504  mf::LogVerbatim("TC") << " breakPt " << PrintPos(tj.Pts[breakPt]) << " bragg " << bestBragg;
1505  // Create a vertex at the break point
1506  VtxStore aVtx;
1507  aVtx.Pos = tj.Pts[breakPt].Pos;
1508  aVtx.NTraj = 2;
1509  aVtx.Pass = tj.Pass;
1510  aVtx.Topo = 12;
1511  aVtx.ChiDOF = 0;
1512  aVtx.CTP = tj.CTP;
1513  aVtx.ID = slc.vtxs.size() + 1;
1514  aVtx.Stat[kFixed] = true;
1515  unsigned short ivx = slc.vtxs.size();
1516  if (!StoreVertex(slc, aVtx)) return false;
1517  if (!SplitTraj(slc, itj, breakPt, ivx, prt)) {
1518  if (prt) mf::LogVerbatim("TC") << "BS: Failed to split trajectory";
1519  MakeVertexObsolete("BS", slc, slc.vtxs[ivx], false);
1520  return false;
1521  }
1522  SetVx2Score(slc);
1523  slc.tjs[itj].AlgMod[kBraggSplit] = true;
1524  unsigned short otj = slc.tjs.size() - 1;
1525  if (bestBragg == 2) std::swap(itj, otj);
1526  slc.tjs[itj].PDGCode = 211;
1527  slc.tjs[itj].EndFlag[1][kBragg] = true;
1528  slc.tjs[otj].PDGCode = 13;
1529  return true;
1530  } // BraggSplit
1531 
1533  void TrimHiChgEndPts(TCSlice& slc, Trajectory& tj, bool prt)
1534  {
1535  // Trim points at the end if the charge pull is too high
1536  if (!tcc.useAlg[kTHCEP]) return;
1537  // don't consider long electrons
1538  if (tj.PDGCode == 111) return;
1539  unsigned short npwc = NumPtsWithCharge(slc, tj, false);
1540  // only consider long tjs
1541  if (npwc < 50) return;
1542  // that don't have a Bragg peak
1543  if (tj.EndFlag[1][kBragg]) return;
1544 
1545  // only look at the last points that would have not been considered by GottaKink
1546  unsigned short nPtsMax = tcc.kinkCuts[0];
1547  if (nPtsMax > 8) nPtsMax = 8;
1548 
1549  // find the first point with a high charge pull starting at nPtsMax points before the end
1550  // and count the number of high charge pull points
1551  float cntBad = 0;
1552  unsigned short firstBad = USHRT_MAX;
1553  for (unsigned short ii = 0; ii < nPtsMax; ++ii) {
1554  unsigned short ipt = tj.EndPt[1] - nPtsMax + ii;
1555  auto& tp = tj.Pts[ipt];
1556  if (tp.Chg <= 0) continue;
1557  if (tp.ChgPull < 3) continue;
1558  ++cntBad;
1559  if (firstBad == USHRT_MAX) firstBad = ipt;
1560  } // ii
1561  if (firstBad == USHRT_MAX) return;
1562  // total number of points from the first bad point to the end
1563  float cntTot = tj.EndPt[1] - firstBad;
1564  // fraction of those poins that are bad
1565  float fracBad = cntBad / cntTot;
1566  if (fracBad < 0.5) return;
1567  if (prt)
1568  mf::LogVerbatim("TC") << "THCEP: Trim points starting at " << PrintPos(tj.Pts[firstBad]);
1569  for (unsigned short ipt = firstBad; ipt <= tj.EndPt[1]; ++ipt)
1570  UnsetUsedHits(slc, tj.Pts[ipt]);
1571  tj.AlgMod[kTHCEP] = true;
1572  } // TrimHiChgEndPts
1573 
1575  void TrimEndPts(std::string fcnLabel,
1576  TCSlice& slc,
1577  Trajectory& tj,
1578  const std::vector<float>& fQualityCuts,
1579  bool prt)
1580  {
1581  // Trim the hits off the end until there are at least MinPts consecutive hits at the end
1582  // and the fraction of hits on the trajectory exceeds fQualityCuts[0]
1583  // Minimum length requirement accounting for dead wires where - denotes a wire with a point
1584  // and D is a dead wire. Here is an example with minPts = 3
1585  // ---DDDDD--- is OK
1586  // ----DD-DD-- is OK
1587  // ----DDD-D-- is OK
1588  // ----DDDDD-- is not OK
1589 
1590  if (!tcc.useAlg[kTEP]) return;
1591  if (tj.PDGCode == 111) return;
1592  if (tj.EndFlag[1][kAtKink]) return;
1593 
1594  unsigned short npwc = NumPtsWithCharge(slc, tj, false);
1595  short minPts = fQualityCuts[1];
1596  if (minPts < 1) return;
1597  if (npwc < minPts) return;
1598  // don't consider short Tjs
1599  if (npwc < 8) return;
1600 
1601  // handle short tjs
1602  if (npwc == minPts + 1) {
1603  unsigned short endPt1 = tj.EndPt[1];
1604  auto& tp = tj.Pts[endPt1];
1605  auto& ptp = tj.Pts[endPt1 - 1];
1606  // remove the last point if the previous point has no charge or if
1607  // it isn't on the next wire
1608  float dwire = std::abs(ptp.Pos[0] - tp.Pos[0]);
1609  if (ptp.Chg == 0 || dwire > 1.1) {
1610  UnsetUsedHits(slc, tp);
1611  SetEndPoints(tj);
1612  tj.AlgMod[kTEP] = true;
1613  }
1614  return;
1615  } // short tj
1616 
1617  // find the separation between adjacent points, starting at the end
1618  short lastPt = 0;
1619  for (lastPt = tj.EndPt[1]; lastPt >= minPts; --lastPt) {
1620  // check for an error
1621  if (lastPt == 1) break;
1622  if (tj.Pts[lastPt].Chg == 0) continue;
1623  // number of points on adjacent wires
1624  unsigned short nadj = 0;
1625  unsigned short npwc = 0;
1626  for (short ipt = lastPt - minPts; ipt < lastPt; ++ipt) {
1627  if (ipt < 2) break;
1628  // the current point
1629  auto& tp = tj.Pts[ipt];
1630  // the previous point
1631  auto& ptp = tj.Pts[ipt - 1];
1632  if (tp.Chg > 0 && ptp.Chg > 0) {
1633  ++npwc;
1634  if (std::abs(tp.Pos[0] - ptp.Pos[0]) < 1.5) ++nadj;
1635  }
1636  } // ipt
1637  float ntpwc = NumPtsWithCharge(slc, tj, true, tj.EndPt[0], lastPt);
1638  float nwires = std::abs(tj.Pts[tj.EndPt[0]].Pos[0] - tj.Pts[lastPt].Pos[0]) + 1;
1639  float hitFrac = ntpwc / nwires;
1640  if (prt)
1641  mf::LogVerbatim("TC") << fcnLabel << "-TEP: T" << tj.ID << " lastPt " << lastPt << " npwc "
1642  << npwc << " ntpwc " << ntpwc << " nadj " << nadj << " hitFrac "
1643  << hitFrac;
1644  if (hitFrac > fQualityCuts[0] && npwc == minPts && nadj >= minPts - 1) break;
1645  } // lastPt
1646 
1647  if (prt) mf::LogVerbatim("TC") << " lastPt " << lastPt << " " << tj.EndPt[1] << "\n";
1648  // trim the last point if it just after a dead wire.
1649  if (tj.Pts[lastPt].Pos[0] > -0.4) {
1650  unsigned int prevWire = std::nearbyint(tj.Pts[lastPt].Pos[0]);
1651  if (tj.StepDir > 0) { --prevWire; }
1652  else {
1653  ++prevWire;
1654  }
1655  if (prt) {
1656  mf::LogVerbatim("TC") << fcnLabel << "-TEP: is prevWire " << prevWire << " dead? ";
1657  }
1658  unsigned short plane = DecodeCTP(tj.CTP).Plane;
1659  if (prevWire < slc.nWires[plane] && !evt.goodWire[plane][prevWire]) --lastPt;
1660  } // valid Pos[0]
1661 
1662  // Nothing needs to be done
1663  if (lastPt == tj.EndPt[1]) {
1664  if (prt) mf::LogVerbatim("TC") << fcnLabel << "-TEPo: Tj is OK";
1665  return;
1666  }
1667 
1668  // clear the points after lastPt
1669  for (unsigned short ipt = lastPt + 1; ipt <= tj.EndPt[1]; ++ipt)
1670  UnsetUsedHits(slc, tj.Pts[ipt]);
1671  SetEndPoints(tj);
1672  tj.AlgMod[kTEP] = true;
1673  if (prt) {
1674  fcnLabel += "-TEPo";
1675  PrintTrajectory(fcnLabel, slc, tj, USHRT_MAX);
1676  }
1677 
1678  } // TrimEndPts
1679 
1681  void ChkEndKink(TCSlice const& slc, Trajectory& tj, bool prt)
1682  {
1683  // look for large-angle kink near the end
1684  if (!tcc.useAlg[kEndKink]) return;
1685  if (tj.PDGCode == 111) return;
1686  if (tj.EndPt[1] - tj.EndPt[0] < 6) return;
1687 
1688  if (prt) mf::LogVerbatim("TC") << "CEK: Inside ChkEndKinks T" << tj.ID << " ";
1689 
1690  float maxSig = tcc.kinkCuts[1];
1691  unsigned short withNptsFit = 0;
1692  unsigned short nPtsFit = tcc.kinkCuts[0];
1693  bool useChg = (tcc.kinkCuts[2] > 0);
1694  for (unsigned short nptsf = 3; nptsf < nPtsFit; ++nptsf) {
1695  unsigned short ipt = tj.EndPt[1] - nptsf;
1696  float ks = KinkSignificance(slc, tj, ipt, nptsf, useChg, prt);
1697  if (ks > maxSig) {
1698  maxSig = ks;
1699  withNptsFit = nptsf;
1700  }
1701  } // nptsf
1702  if (withNptsFit > 0) {
1703  unsigned short ipt = tj.EndPt[1] - withNptsFit;
1704  std::cout << "CEK: T" << tj.ID << " ipt " << ipt;
1705  float ks = KinkSignificance(slc, tj, ipt, withNptsFit, false, prt);
1706  auto& tp = tj.Pts[ipt];
1707  std::cout << " " << PrintPos(tp) << " withNptsFit " << withNptsFit << " ks " << ks << "\n";
1708  }
1709 
1710  } // ChkEndKink
1711 
1713  void ChkChgAsymmetry(TCSlice& slc, Trajectory& tj, bool prt)
1714  {
1715  // looks for a high-charge point in the trajectory which may be due to the
1716  // trajectory crossing an interaction vertex. The properties of points on the opposite
1717  // sides of the high-charge point are analyzed. If significant differences are found, all points
1718  // near the high-charge point are removed as well as those from that point to the end
1719  if (!tcc.useAlg[kChkChgAsym]) return;
1720  if (tj.PDGCode == 111) return;
1721  unsigned short npts = tj.EndPt[1] - tj.EndPt[0];
1722  if (prt) mf::LogVerbatim("TC") << " Inside ChkChgAsymmetry T" << tj.ID;
1723  // ignore long tjs
1724  if (npts > 50) return;
1725  // ignore short tjs
1726  if (npts < 8) return;
1727  // require the charge pull > 5
1728  float bigPull = 5;
1729  unsigned short atPt = 0;
1730  // Don't consider the first/last few points in case there is a Bragg peak
1731  for (unsigned short ipt = tj.EndPt[0] + 2; ipt <= tj.EndPt[1] - 2; ++ipt) {
1732  auto& tp = tj.Pts[ipt];
1733  if (tp.ChgPull > bigPull) {
1734  bigPull = tp.ChgPull;
1735  atPt = ipt;
1736  }
1737  } // ipt
1738  if (atPt == 0) return;
1739  // require that this point be near the DS end
1740  if ((atPt - tj.EndPt[0]) < 0.5 * npts) return;
1741  if (prt)
1742  mf::LogVerbatim("TC") << "CCA: T" << tj.ID << " Large Chg point at " << atPt
1743  << ". Check charge asymmetry around it.";
1744  unsigned short nchk = 0;
1745  unsigned short npos = 0;
1746  unsigned short nneg = 0;
1747  for (short ii = 1; ii < 5; ++ii) {
1748  short iplu = atPt + ii;
1749  if (iplu > tj.EndPt[1]) break;
1750  short ineg = atPt - ii;
1751  if (ineg < tj.EndPt[0]) break;
1752  if (tj.Pts[iplu].Chg == 0) continue;
1753  if (tj.Pts[ineg].Chg == 0) continue;
1754  float asym = (tj.Pts[iplu].Chg - tj.Pts[ineg].Chg) / (tj.Pts[iplu].Chg + tj.Pts[ineg].Chg);
1755  ++nchk;
1756  if (asym > 0.5) ++npos;
1757  if (asym < -0.5) ++nneg;
1758  if (prt)
1759  mf::LogVerbatim("TC") << " ineg " << ineg << " iplu " << iplu << " asym " << asym
1760  << " nchk " << nchk;
1761  } // ii
1762  if (nchk < 3) return;
1763  // require most of the points be very positive or very negative
1764  nchk -= 2;
1765  bool doTrim = (nneg > nchk) || (npos > nchk);
1766  if (!doTrim) return;
1767  // remove all the points at the end starting at the one just before the peak if the pull is not so good
1768  auto& prevTP = tj.Pts[atPt - 1];
1769  if (std::abs(prevTP.ChgPull) > 2) --atPt;
1770  for (unsigned short ipt = atPt; ipt <= tj.EndPt[1]; ++ipt)
1771  UnsetUsedHits(slc, tj.Pts[ipt]);
1772  SetEndPoints(tj);
1773  tj.AlgMod[kChkChgAsym] = true;
1774  if (prt) PrintTrajectory("CCA", slc, tj, USHRT_MAX);
1775  } // ChkChgAsymmetry
1776 
1778  bool SignalBetween(const TrajPoint& tp1, const TrajPoint& tp2, const float MinWireSignalFraction)
1779  {
1780  // Returns true if there is a signal on > MinWireSignalFraction of the wires between tp1 and tp2.
1781  if (MinWireSignalFraction == 0) return true;
1782 
1783  if (tp1.Pos[0] < -0.4 || tp2.Pos[0] < -0.4) return false;
1784  int fromWire = std::nearbyint(tp1.Pos[0]);
1785  int toWire = std::nearbyint(tp2.Pos[0]);
1786 
1787  if (fromWire == toWire) {
1788  TrajPoint tp = tp1;
1789  // check for a signal midway between
1790  tp.Pos[1] = 0.5 * (tp1.Pos[1] + tp2.Pos[1]);
1791  return SignalAtTp(tp);
1792  }
1793  // define a trajectory point located at tp1 that has a direction towards tp2
1794  TrajPoint tp;
1795  if (!MakeBareTrajPoint(tp1, tp2, tp)) return true;
1796  return SignalBetween(tp, toWire, MinWireSignalFraction);
1797  } // SignalBetween
1798 
1800  bool SignalBetween(TrajPoint tp, float toPos0, const float MinWireSignalFraction)
1801  {
1802  // Returns true if there is a signal on > MinWireSignalFraction of the wires between tp and toPos0.
1803  return ChgFracBetween(tp, toPos0) >= MinWireSignalFraction;
1804  } // SignalBetween
1805 
1807  float ChgFracBetween(TrajPoint tp, float toPos0)
1808  {
1809  // Returns the fraction of wires between tp.Pos[0] and toPos0 that have a hit
1810  // on the line defined by tp.Pos and tp.Dir
1811 
1812  if (tp.Pos[0] < -0.4 || toPos0 < -0.4) return 0;
1813  int fromWire = std::nearbyint(tp.Pos[0]);
1814  int toWire = std::nearbyint(toPos0);
1815 
1816  if (fromWire == toWire) return SignalAtTp(tp);
1817 
1818  int nWires = abs(toWire - fromWire) + 1;
1819 
1820  if (std::abs(tp.Dir[0]) < 0.001) tp.Dir[0] = 0.001;
1821  float stepSize = std::abs(1 / tp.Dir[0]);
1822  // ensure that we step in the right direction
1823  if (toWire > fromWire && tp.Dir[0] < 0) stepSize = -stepSize;
1824  if (toWire < fromWire && tp.Dir[0] > 0) stepSize = -stepSize;
1825  float nsig = 0;
1826  float num = 0;
1827  for (unsigned short cnt = 0; cnt < nWires; ++cnt) {
1828  ++num;
1829  if (SignalAtTp(tp)) ++nsig;
1830  tp.Pos[0] += tp.Dir[0] * stepSize;
1831  tp.Pos[1] += tp.Dir[1] * stepSize;
1832  } // cnt
1833  float sigFrac = nsig / num;
1834  return sigFrac;
1835  } // ChgFracBetween
1836 
1838  bool TrajHitsOK(TCSlice const& slc,
1839  const std::vector<unsigned int>& iHitsInMultiplet,
1840  const std::vector<unsigned int>& jHitsInMultiplet)
1841  {
1842  // Hits (assume to be on adjacent wires have an acceptable signal overlap
1843 
1844  if (iHitsInMultiplet.empty() || jHitsInMultiplet.empty()) return false;
1845 
1846  float sum;
1847  float cvI = HitsPosTick(slc, iHitsInMultiplet, sum, kAllHits);
1848  if (cvI < 0) return false;
1849  float minI = 1E6;
1850  float maxI = 0;
1851  for (auto& iht : iHitsInMultiplet) {
1852  auto const& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
1853  float cv = hit.PeakTime();
1854  float rms = hit.RMS();
1855  float arg = cv - 3.1 * rms;
1856  if (arg < minI) minI = arg;
1857  arg = cv + 3.1 * rms;
1858  if (arg > maxI) maxI = arg;
1859  }
1860 
1861  float cvJ = HitsPosTick(slc, jHitsInMultiplet, sum, kAllHits);
1862  if (cvJ < 0) return false;
1863  float minJ = 1E6;
1864  float maxJ = 0;
1865  for (auto& jht : jHitsInMultiplet) {
1866  auto& hit = (*evt.allHits)[slc.slHits[jht].allHitsIndex];
1867  float cv = hit.PeakTime();
1868  float rms = hit.RMS();
1869  float arg = cv - 3.1 * rms;
1870  if (arg < minJ) minJ = arg;
1871  arg = cv + 3.1 * rms;
1872  if (arg > maxJ) maxJ = arg;
1873  }
1874 
1875  if (cvI < cvJ) {
1876  if (maxI > minJ) return true;
1877  }
1878  else {
1879  if (minI < maxJ) return true;
1880  }
1881  return false;
1882  } // TrajHitsOK
1883 
1885  bool TrajHitsOK(TCSlice const& slc, const unsigned int iht, const unsigned int jht)
1886  {
1887  // ensure that two adjacent hits have an acceptable overlap
1888  if (iht > slc.slHits.size() - 1) return false;
1889  if (jht > slc.slHits.size() - 1) return false;
1890  // require that they be on adjacent wires
1891  auto& ihit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
1892  auto& jhit = (*evt.allHits)[slc.slHits[jht].allHitsIndex];
1893  int iwire = ihit.WireID().Wire;
1894  int jwire = jhit.WireID().Wire;
1895  if (std::abs(iwire - jwire) > 1) return false;
1896  if (ihit.PeakTime() > jhit.PeakTime()) {
1897  float minISignal = ihit.PeakTime() - 3 * ihit.RMS();
1898  float maxJSignal = jhit.PeakTime() + 3 * ihit.RMS();
1899  if (maxJSignal > minISignal) return true;
1900  }
1901  else {
1902  float maxISignal = ihit.PeakTime() + 3 * ihit.RMS();
1903  float minJSignal = jhit.PeakTime() - 3 * ihit.RMS();
1904  if (minJSignal > maxISignal) return true;
1905  }
1906  return false;
1907  } // TrajHitsOK
1908 
1910  float ExpectedHitsRMS(const TrajPoint& tp)
1911  {
1912  // returns the expected RMS of hits for the trajectory point in ticks
1913  if (std::abs(tp.Dir[0]) > 0.001) {
1914  geo::PlaneID planeID = DecodeCTP(tp.CTP);
1915  return 1.5 * evt.aveHitRMS[planeID.Plane] +
1916  2 * std::abs(tp.Dir[1] / tp.Dir[0]) / tcc.unitsPerTick;
1917  }
1918  return 500;
1919  } // ExpectedHitsRMS
1920 
1922  bool SignalAtTpInSlc(const TCSlice& slc, const TrajPoint& tp)
1923  {
1924  // Version of SignalAtTP that only checks the hit collection in the current slice
1925 
1926  if (tp.Pos[0] < -0.4) return false;
1927  geo::PlaneID planeID = DecodeCTP(tp.CTP);
1928  unsigned short pln = planeID.Plane;
1929  unsigned int wire = std::nearbyint(tp.Pos[0]);
1930  if (wire > evt.goodWire[pln].size() - 1) return false;
1931  // assume there is a signal on a dead wire
1932  if (!evt.goodWire[pln][wire]) return true;
1933  // no signal here if there are no hits on this wire
1934  if (slc.wireHitRange[pln][wire].first == UINT_MAX) return false;
1935  // check the proximity of all of the hits in the range
1936  float projTick = (float)(tp.Pos[1] / tcc.unitsPerTick);
1937  float tickRange = 0;
1938  if (std::abs(tp.Dir[1]) != 0) {
1939  tickRange = std::abs(0.5 / tp.Dir[1]) / tcc.unitsPerTick;
1940  // don't let it get too large
1941  if (tickRange > 40) tickRange = 40;
1942  }
1943  float loTpTick = projTick - tickRange;
1944  float hiTpTick = projTick + tickRange;
1945  for (unsigned int iht = slc.wireHitRange[pln][wire].first;
1946  iht <= slc.wireHitRange[pln][wire].second;
1947  ++iht) {
1948  unsigned int ahi = slc.slHits[iht].allHitsIndex;
1949  auto& hit = (*evt.allHits)[ahi];
1950  if (projTick < hit.PeakTime()) {
1951  float loHitTick = hit.PeakTime() - 3 * hit.RMS();
1952  if (hiTpTick > loHitTick) return true;
1953  }
1954  else {
1955  float hiHitTick = hit.PeakTime() + 3 * hit.RMS();
1956  if (loTpTick < hiHitTick) return true;
1957  }
1958  } // iht
1959  return false;
1960  } // SignalAtTpInSlc
1961 
1964  {
1965  // returns true if there is a hit near tp.Pos by searching through the full hit collection (if there
1966  // are multiple slices) or through the last slice (if there is only one slice)
1967 
1968  tp.Environment[kEnvNearSrcHit] = false;
1969 
1970  // just check the hits in the last slice
1971  if (evt.wireHitRange.empty()) {
1972  const auto& slc = slices[slices.size() - 1];
1973  return SignalAtTpInSlc(slc, tp);
1974  }
1975 
1976  if (tp.Pos[0] < -0.4) return false;
1977  geo::PlaneID planeID = DecodeCTP(tp.CTP);
1978  unsigned short pln = planeID.Plane;
1979  unsigned int wire = std::nearbyint(tp.Pos[0]);
1980  if (wire > evt.goodWire[pln].size() - 1) return false;
1981  // assume there is a signal on a dead wire
1982  if (!evt.goodWire[pln][wire]) return true;
1983 
1984  // check the proximity of all of the hits in the range
1985  float projTick = (float)(tp.Pos[1] / tcc.unitsPerTick);
1986  float tickRange = 0;
1987  if (std::abs(tp.Dir[1]) != 0) {
1988  tickRange = std::abs(0.5 / tp.Dir[1]) / tcc.unitsPerTick;
1989  // don't let it get too large
1990  if (tickRange > 40) tickRange = 40;
1991  }
1992  float loTpTick = projTick - tickRange;
1993  float hiTpTick = projTick + tickRange;
1994 
1995  // no signal here if there are no hits on this wire
1996  if (evt.wireHitRange[pln][wire].first == UINT_MAX) return false;
1997 
1998  for (unsigned int iht = evt.wireHitRange[pln][wire].first;
1999  iht <= evt.wireHitRange[pln][wire].second;
2000  ++iht) {
2001  auto& hit = (*evt.allHits)[iht];
2002  // We wouldn't need to make this check if hits were sorted
2003  const auto& wid = hit.WireID();
2004  if (wid.Cryostat != planeID.Cryostat) continue;
2005  if (wid.TPC != planeID.TPC) continue;
2006  if (wid.Plane != planeID.Plane) continue;
2007  if (projTick < hit.PeakTime()) {
2008  float loHitTick = hit.PeakTime() - 3 * hit.RMS();
2009  if (hiTpTick > loHitTick) return true;
2010  }
2011  else {
2012  float hiHitTick = hit.PeakTime() + 3 * hit.RMS();
2013  if (loTpTick < hiHitTick) return true;
2014  }
2015  } // iht
2016  // No hit was found near projTick. Search through the source hits collection
2017  // (if it is defined) for a hit that may have been removed by disambiguation
2018  // Use the srcHit collection if it is available
2019  if (evt.srcHits != NULL) {
2020  if (NearbySrcHit(planeID, wire, loTpTick, hiTpTick)) {
2021  tp.Environment[kEnvNearSrcHit] = true;
2022  return true;
2023  } // NearbySrcHit
2024  } // evt.srcHits != NULL
2025  return false;
2026  } // SignalAtTp
2027 
2029  bool NearbySrcHit(geo::PlaneID plnID, unsigned int wire, float loTick, float hiTick)
2030  {
2031  // Look for a hit on wid in the srcHits collection that has a tick in the range. This
2032  // is a DUNE-specific function in which hit disambiguation is done in the U and V planes
2033  if (evt.srcHits == NULL) return false;
2034  unsigned int pln = plnID.Plane;
2035  if (pln == 2) return false;
2036 
2037  unsigned int tpc = plnID.TPC;
2038  // get a valid range of hits to search
2039  if (evt.tpcSrcHitRange[tpc].first >= (*evt.srcHits).size()) return false;
2040  if (evt.tpcSrcHitRange[tpc].second >= (*evt.srcHits).size()) return false;
2041  raw::ChannelID_t chan = tcc.geom->PlaneWireToChannel(geo::WireID(plnID, wire));
2042  float atTick = 0.5 * (loTick + hiTick);
2043  for (unsigned int iht = evt.tpcSrcHitRange[tpc].first; iht <= evt.tpcSrcHitRange[tpc].second;
2044  ++iht) {
2045  auto& hit = (*evt.srcHits)[iht];
2046  if (hit.Channel() != chan) continue;
2047  if (atTick < hit.PeakTime()) {
2048  float loHitTick = hit.PeakTime() - 3 * hit.RMS();
2049  if (hiTick > loHitTick) return true;
2050  }
2051  else {
2052  float hiHitTick = hit.PeakTime() + 3 * hit.RMS();
2053  if (loTick < hiHitTick) return true;
2054  }
2055  } // iht
2056  return false;
2057  } // NearbySrcHit
2058 
2060  float TpSumHitChg(const TCSlice& slc, TrajPoint const& tp)
2061  {
2062  float totchg = 0;
2063  for (size_t i = 0; i < tp.Hits.size(); ++i) {
2064  if (!tp.UseHit[i]) continue;
2065  totchg += (*evt.allHits)[slc.slHits[tp.Hits[i]].allHitsIndex].Integral();
2066  }
2067  return totchg;
2068  } // TpSumHitChg
2069 
2071  unsigned short NumPtsWithCharge(const TCSlice& slc, const Trajectory& tj, bool includeDeadWires)
2072  {
2073  unsigned short firstPt = tj.EndPt[0];
2074  unsigned short lastPt = tj.EndPt[1];
2075  return NumPtsWithCharge(slc, tj, includeDeadWires, firstPt, lastPt);
2076  }
2077 
2079  unsigned short NumPtsWithCharge(const TCSlice& slc,
2080  const Trajectory& tj,
2081  bool includeDeadWires,
2082  unsigned short firstPt,
2083  unsigned short lastPt)
2084  {
2085  unsigned short ntp = 0;
2086  for (unsigned short ipt = firstPt; ipt <= lastPt; ++ipt)
2087  if (tj.Pts[ipt].Chg > 0) ++ntp;
2088  // Add the count of deadwires
2089  if (includeDeadWires) ntp += DeadWireCount(slc, tj.Pts[firstPt], tj.Pts[lastPt]);
2090  return ntp;
2091  } // NumPtsWithCharge
2092 
2094  float DeadWireCount(const TCSlice& slc, const TrajPoint& tp1, const TrajPoint& tp2)
2095  {
2096  return DeadWireCount(slc, tp1.Pos[0], tp2.Pos[0], tp1.CTP);
2097  } // DeadWireCount
2098 
2100  float DeadWireCount(const TCSlice& slc,
2101  const float& inWirePos1,
2102  const float& inWirePos2,
2103  CTP_t tCTP)
2104  {
2105  if (inWirePos1 < -0.4 || inWirePos2 < -0.4) return 0;
2106  unsigned int inWire1 = std::nearbyint(inWirePos1);
2107  unsigned int inWire2 = std::nearbyint(inWirePos2);
2108  geo::PlaneID planeID = DecodeCTP(tCTP);
2109  unsigned short plane = planeID.Plane;
2110  if (inWire1 > slc.nWires[plane] || inWire2 > slc.nWires[plane]) return 0;
2111  if (inWire1 > inWire2) {
2112  // put in increasing order
2113  unsigned int tmp = inWire1;
2114  inWire1 = inWire2;
2115  inWire2 = tmp;
2116  } // inWire1 > inWire2
2117  ++inWire2;
2118  unsigned int wire, ndead = 0;
2119  for (wire = inWire1; wire < inWire2; ++wire)
2120  if (!evt.goodWire[plane][wire]) ++ndead;
2121  return ndead;
2122  } // DeadWireCount
2123 
2125  unsigned short PDGCodeIndex(int PDGCode)
2126  {
2127  unsigned short pdg = abs(PDGCode);
2128  if (pdg == 11) return 0; // electron
2129  if (pdg == 13) return 1; // muon
2130  if (pdg == 211) return 2; // pion
2131  if (pdg == 321) return 3; // kaon
2132  if (pdg == 2212) return 4; // proton
2133  return USHRT_MAX;
2134  } // PDGCodeIndex
2135 
2137  void MakeTrajectoryObsolete(TCSlice& slc, unsigned int itj)
2138  {
2139  // Note that this does not change the state of UseHit to allow
2140  // resurrecting the trajectory later (RestoreObsoleteTrajectory)
2141  if (itj > slc.tjs.size() - 1) return;
2142  int killTjID = slc.tjs[itj].ID;
2143  for (auto& hit : slc.slHits)
2144  if (hit.InTraj == killTjID) hit.InTraj = 0;
2145  slc.tjs[itj].AlgMod[kKilled] = true;
2146  } // MakeTrajectoryObsolete
2147 
2149  void RestoreObsoleteTrajectory(TCSlice& slc, unsigned int itj)
2150  {
2151  if (itj > slc.tjs.size() - 1) return;
2152  if (!slc.tjs[itj].AlgMod[kKilled]) {
2153  mf::LogWarning("TC")
2154  << "RestoreObsoleteTrajectory: Trying to restore not-obsolete trajectory "
2155  << slc.tjs[itj].ID;
2156  return;
2157  }
2158  unsigned int iht;
2159  for (auto& tp : slc.tjs[itj].Pts) {
2160  for (unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
2161  if (tp.UseHit[ii]) {
2162  iht = tp.Hits[ii];
2163  if (slc.slHits[iht].InTraj == 0) { slc.slHits[iht].InTraj = slc.tjs[itj].ID; }
2164  }
2165  } // ii
2166  } // tp
2167  slc.tjs[itj].AlgMod[kKilled] = false;
2168  } // RestoreObsoleteTrajectory
2169 
2171  void MergeGhostTjs(TCSlice& slc, CTP_t inCTP)
2172  {
2173  // Merges short Tjs that share many hits with a longer Tj
2174  if (!tcc.useAlg[kMrgGhost]) return;
2175 
2176  for (auto& shortTj : slc.tjs) {
2177  if (shortTj.AlgMod[kKilled] || shortTj.AlgMod[kHaloTj]) continue;
2178  if (shortTj.CTP != inCTP) continue;
2179  unsigned short spts = shortTj.EndPt[1] - shortTj.EndPt[0];
2180  if (spts > 20) continue;
2181  // ignore delta rays
2182  if (shortTj.PDGCode == 11) continue;
2183  // ignore InShower Tjs
2184  if (shortTj.SSID > 0) continue;
2185  auto tjhits = PutTrajHitsInVector(shortTj, kAllHits);
2186  if (tjhits.empty()) continue;
2187  std::vector<int> tids;
2188  std::vector<unsigned short> tcnt;
2189  for (auto iht : tjhits) {
2190  auto& hit = slc.slHits[iht];
2191  if (hit.InTraj <= 0) continue;
2192  if ((unsigned int)hit.InTraj > slc.tjs.size()) continue;
2193  if (hit.InTraj == shortTj.ID) continue;
2194  unsigned short indx = 0;
2195  for (indx = 0; indx < tids.size(); ++indx)
2196  if (hit.InTraj == tids[indx]) break;
2197  if (indx == tids.size()) {
2198  tids.push_back(hit.InTraj);
2199  tcnt.push_back(1);
2200  }
2201  else {
2202  ++tcnt[indx];
2203  }
2204  } // iht
2205  if (tids.empty()) continue;
2206  // find the max count for Tjs that are longer than this one
2207  unsigned short maxcnt = 0;
2208  for (unsigned short indx = 0; indx < tids.size(); ++indx) {
2209  if (tcnt[indx] > maxcnt) {
2210  auto& ltj = slc.tjs[tids[indx] - 1];
2211  unsigned short lpts = ltj.EndPt[1] - ltj.EndPt[0];
2212  if (lpts < spts) continue;
2213  maxcnt = tcnt[indx];
2214  }
2215  } // indx
2216  float hitFrac = (float)maxcnt / (float)tjhits.size();
2217  if (hitFrac < 0.1) continue;
2218  } // shortTj
2219  } // MergeGhostTjs
2220 
2223  TCSlice& slc,
2224  unsigned short itj,
2225  float XPos,
2226  bool makeVx2,
2227  bool prt)
2228  {
2229  // Splits the trajectory at an X position and optionally creates a 2D vertex
2230  // at the split point
2231  if (itj > slc.tjs.size() - 1) return false;
2232 
2233  auto& tj = slc.tjs[itj];
2234  geo::PlaneID planeID = DecodeCTP(tj.CTP);
2235  float atPos1 = detProp.ConvertXToTicks(XPos, planeID) * tcc.unitsPerTick;
2236  unsigned short atPt = USHRT_MAX;
2237  for (unsigned short ipt = tj.EndPt[0] + 1; ipt <= tj.EndPt[1]; ++ipt) {
2238  if (tj.Pts[ipt].Pos[1] > tj.Pts[ipt - 1].Pos[1]) {
2239  // positive slope
2240  if (tj.Pts[ipt - 1].Pos[1] < atPos1 && tj.Pts[ipt].Pos[1] >= atPos1) {
2241  atPt = ipt;
2242  break;
2243  }
2244  }
2245  else {
2246  // negative slope
2247  if (tj.Pts[ipt - 1].Pos[1] >= atPos1 && tj.Pts[ipt].Pos[1] < atPos1) {
2248  atPt = ipt;
2249  break;
2250  }
2251  } // negative slope
2252  } // ipt
2253  if (atPt == USHRT_MAX) return false;
2254  unsigned short vx2Index = USHRT_MAX;
2255  if (makeVx2) {
2256  VtxStore newVx2;
2257  newVx2.CTP = tj.CTP;
2258  newVx2.Pos[0] = 0.5 * (tj.Pts[atPt - 1].Pos[0] + tj.Pts[atPt].Pos[0]);
2259  newVx2.Pos[1] = 0.5 * (tj.Pts[atPt - 1].Pos[1] + tj.Pts[atPt].Pos[1]);
2260  newVx2.Topo = 10;
2261  newVx2.NTraj = 2;
2262  if (StoreVertex(slc, newVx2)) vx2Index = slc.vtxs.size() - 1;
2263  } // makeVx2
2264  return SplitTraj(slc, itj, atPt, vx2Index, prt);
2265  } // SplitTraj
2266 
2268  bool SplitTraj(TCSlice& slc, unsigned short itj, unsigned short pos, unsigned short ivx, bool prt)
2269  {
2270  // Splits the trajectory itj in the slc.tjs vector into two trajectories at position pos. Splits
2271  // the trajectory and associates the ends to the supplied vertex.
2272  // Here is an example where itj has 9 points and we will split at pos = 4
2273  // itj (0 1 2 3 4 5 6 7 8) -> new traj (0 1 2 3) + new traj (4 5 6 7 8)
2274 
2275  if (itj > slc.tjs.size() - 1) return false;
2276  if (pos < slc.tjs[itj].EndPt[0] + 1 || pos > slc.tjs[itj].EndPt[1] - 1) return false;
2277  if (ivx != USHRT_MAX && ivx > slc.vtxs.size() - 1) return false;
2278 
2279  Trajectory& tj = slc.tjs[itj];
2280 
2281  // Reset the PDG Code if we are splitting a tagged muon
2282  bool splittingMuon = (tj.PDGCode == 13);
2283  if (splittingMuon) tj.PDGCode = 0;
2284 
2285  if (prt) {
2286  mf::LogVerbatim myprt("TC");
2287  myprt << "SplitTraj: Split T" << tj.ID << " at point " << PrintPos(tj.Pts[pos]);
2288  if (ivx < slc.vtxs.size()) myprt << " with Vtx 2V" << slc.vtxs[ivx].ID;
2289  }
2290 
2291  // ensure that there will be at least 3 TPs on each trajectory
2292  unsigned short ntp = 0;
2293  for (unsigned short ipt = 0; ipt <= pos; ++ipt) {
2294  if (tj.Pts[ipt].Chg > 0) ++ntp;
2295  if (ntp > 2) break;
2296  } // ipt
2297  if (ntp < 3) {
2298  if (prt) mf::LogVerbatim("TC") << " Split point to small at begin " << ntp << " pos " << pos;
2299  return false;
2300  }
2301  ntp = 0;
2302  for (unsigned short ipt = pos + 1; ipt <= tj.EndPt[1]; ++ipt) {
2303  if (tj.Pts[ipt].Chg > 0) ++ntp;
2304  if (ntp > 2) break;
2305  } // ipt
2306  if (ntp < 3) {
2307  if (prt)
2308  mf::LogVerbatim("TC") << " Split point too small at end " << ntp << " pos " << pos
2309  << " EndPt " << tj.EndPt[1];
2310  return false;
2311  }
2312 
2313  // make a copy that will become the Tj after the split point
2314  Trajectory newTj = tj;
2315  newTj.ID = slc.tjs.size() + 1;
2316  ++evt.globalT_UID;
2317  newTj.UID = evt.globalT_UID;
2318  // make another copy in case something goes wrong
2319  Trajectory oldTj = tj;
2320 
2321  // Leave the first section of tj in place. Re-assign the hits
2322  // to the new trajectory
2323  unsigned int iht;
2324  for (unsigned short ipt = pos + 1; ipt <= tj.EndPt[1]; ++ipt) {
2325  tj.Pts[ipt].Chg = 0;
2326  for (unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
2327  if (!tj.Pts[ipt].UseHit[ii]) continue;
2328  iht = tj.Pts[ipt].Hits[ii];
2329  // This shouldn't happen but check anyway
2330  if (slc.slHits[iht].InTraj != tj.ID) continue;
2331  slc.slHits[iht].InTraj = newTj.ID;
2332  tj.Pts[ipt].UseHit[ii] = false;
2333  } // ii
2334  } // ipt
2335  SetEndPoints(tj);
2336  // Update MCSMom and charge properties
2337  tj.MCSMom = MCSMom(slc, tj);
2338  UpdateTjChgProperties("ST", slc, tj, prt);
2339  if (splittingMuon) SetPDGCode(slc, tj);
2340 
2341  // Append 3 points from the end of tj onto the
2342  // beginning of newTj so that hits can be swapped between
2343  // them later
2344  unsigned short eraseSize = pos - 2;
2345  if (eraseSize > newTj.Pts.size() - 1) {
2346  tj = oldTj;
2347  return false;
2348  }
2349 
2350  if (ivx < slc.vtxs.size()) tj.VtxID[1] = slc.vtxs[ivx].ID;
2351  tj.AlgMod[kSplit] = true;
2352  if (prt) {
2353  mf::LogVerbatim("TC") << " Splitting T" << tj.ID << " new EndPts " << tj.EndPt[0] << " to "
2354  << tj.EndPt[1];
2355  }
2356 
2357  // erase the TPs at the beginning of the new trajectory
2358  newTj.Pts.erase(newTj.Pts.begin(), newTj.Pts.begin() + eraseSize);
2359  // unset the first 3 TP hits
2360  for (unsigned short ipt = 0; ipt < 3; ++ipt) {
2361  for (unsigned short ii = 0; ii < newTj.Pts[ipt].Hits.size(); ++ii)
2362  newTj.Pts[ipt].UseHit[ii] = false;
2363  newTj.Pts[ipt].Chg = 0;
2364  } // ipt
2365  SetEndPoints(newTj);
2366  newTj.MCSMom = MCSMom(slc, newTj);
2367  UpdateTjChgProperties("ST", slc, newTj, prt);
2368  if (splittingMuon) SetPDGCode(slc, newTj);
2369  if (ivx < slc.vtxs.size()) newTj.VtxID[0] = slc.vtxs[ivx].ID;
2370  newTj.AlgMod[kSplit] = true;
2371  newTj.ParentID = 0;
2372  slc.tjs.push_back(newTj);
2373 
2374  if (prt) {
2375  mf::LogVerbatim("TC") << " newTj T" << newTj.ID << " EndPts " << newTj.EndPt[0] << " to "
2376  << newTj.EndPt[1];
2377  }
2378  return true;
2379 
2380  } // SplitTraj
2381 
2384  Trajectory const& tj,
2385  unsigned short& closePt,
2386  float& minSep)
2387  {
2388  // Finds the point, ipt, on trajectory tj that is closest to trajpoint tp
2389  float best = minSep * minSep;
2390  closePt = USHRT_MAX;
2391  float dw, dt, dp2;
2392  unsigned short ipt;
2393  for (ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
2394  dw = tj.Pts[ipt].Pos[0] - tp.Pos[0];
2395  dt = tj.Pts[ipt].Pos[1] - tp.Pos[1];
2396  dp2 = dw * dw + dt * dt;
2397  if (dp2 < best) {
2398  best = dp2;
2399  closePt = ipt;
2400  }
2401  } // ipt
2402  minSep = sqrt(best);
2403  } // TrajPointTrajDOCA
2404 
2406  bool TrajTrajDOCA(const TCSlice& slc,
2407  const Trajectory& tj1,
2408  const Trajectory& tj2,
2409  unsigned short& ipt1,
2410  unsigned short& ipt2,
2411  float& minSep)
2412  {
2413  return TrajTrajDOCA(slc, tj1, tj2, ipt1, ipt2, minSep, false);
2414  } // TrajTrajDOCA
2415 
2417  bool TrajTrajDOCA(const TCSlice& slc,
2418  const Trajectory& tj1,
2419  const Trajectory& tj2,
2420  unsigned short& ipt1,
2421  unsigned short& ipt2,
2422  float& minSep,
2423  bool considerDeadWires)
2424  {
2425  // Find the Distance Of Closest Approach between two trajectories less than minSep
2426  // start with some rough cuts to minimize the use of the more expensive checking. This
2427  // function returns true if the DOCA is less than minSep
2428  for (unsigned short iwt = 0; iwt < 2; ++iwt) {
2429  // Apply box cuts on the ends of the trajectories
2430  // The Lo/Hi wire(time) at each end of tj1
2431  float wt0 = tj1.Pts[tj1.EndPt[0]].Pos[iwt];
2432  float wt1 = tj1.Pts[tj1.EndPt[1]].Pos[iwt];
2433  float lowt1 = wt0;
2434  float hiwt1 = wt1;
2435  if (wt1 < lowt1) {
2436  lowt1 = wt1;
2437  hiwt1 = wt0;
2438  }
2439  // The Lo/Hi wire(time) at each end of tj2
2440  wt0 = tj2.Pts[tj2.EndPt[0]].Pos[iwt];
2441  wt1 = tj2.Pts[tj2.EndPt[1]].Pos[iwt];
2442  float lowt2 = wt0;
2443  float hiwt2 = wt1;
2444  if (wt1 < lowt2) {
2445  lowt2 = wt1;
2446  hiwt2 = wt0;
2447  }
2448  // Check for this configuration
2449  // loWire1.......hiWire1 minSep loWire2....hiWire2
2450  // loTime1.......hiTime1 minSep loTime2....hiTime2
2451  if (lowt2 > hiwt1 + minSep) return false;
2452  // and the other
2453  if (lowt1 > hiwt2 + minSep) return false;
2454  } // iwt
2455 
2456  float best = minSep * minSep;
2457  ipt1 = 0;
2458  ipt2 = 0;
2459  float dwc = 0;
2460  bool isClose = false;
2461  for (unsigned short i1 = tj1.EndPt[0]; i1 < tj1.EndPt[1] + 1; ++i1) {
2462  for (unsigned short i2 = tj2.EndPt[0]; i2 < tj2.EndPt[1] + 1; ++i2) {
2463  if (considerDeadWires) dwc = DeadWireCount(slc, tj1.Pts[i1], tj2.Pts[i2]);
2464  float dw = tj1.Pts[i1].Pos[0] - tj2.Pts[i2].Pos[0] - dwc;
2465  if (std::abs(dw) > minSep) continue;
2466  float dt = tj1.Pts[i1].Pos[1] - tj2.Pts[i2].Pos[1];
2467  if (std::abs(dt) > minSep) continue;
2468  float dp2 = dw * dw + dt * dt;
2469  if (dp2 < best) {
2470  best = dp2;
2471  ipt1 = i1;
2472  ipt2 = i2;
2473  isClose = true;
2474  }
2475  } // i2
2476  } // i1
2477  minSep = sqrt(best);
2478  return isClose;
2479  } // TrajTrajDOCA
2480 
2482  float HitSep2(const TCSlice& slc, unsigned int iht, unsigned int jht)
2483  {
2484  // returns the separation^2 between two hits in WSE units
2485  if (iht > slc.slHits.size() - 1 || jht > slc.slHits.size() - 1) return 1E6;
2486  auto& ihit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
2487  auto& jhit = (*evt.allHits)[slc.slHits[jht].allHitsIndex];
2488  float dw = (float)ihit.WireID().Wire - (float)jhit.WireID().Wire;
2489  float dt = (ihit.PeakTime() - jhit.PeakTime()) * tcc.unitsPerTick;
2490  return dw * dw + dt * dt;
2491  } // HitSep2
2492 
2494  unsigned short CloseEnd(const Trajectory& tj, const Point2_t& pos)
2495  {
2496  unsigned short endPt = tj.EndPt[0];
2497  auto& tp0 = tj.Pts[endPt];
2498  endPt = tj.EndPt[1];
2499  auto& tp1 = tj.Pts[endPt];
2500  if (PosSep2(tp0.Pos, pos) < PosSep2(tp1.Pos, pos)) return 0;
2501  return 1;
2502  } // CloseEnd
2503 
2505  float PointTrajSep2(float wire, float time, TrajPoint const& tp)
2506  {
2507  float dw = wire - tp.Pos[0];
2508  float dt = time - tp.Pos[1];
2509  return dw * dw + dt * dt;
2510  }
2511 
2513  float PointTrajDOCA(const TCSlice& slc, unsigned int iht, TrajPoint const& tp)
2514  {
2515  if (iht > slc.slHits.size() - 1) return 1E6;
2516  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
2517  float wire = hit.WireID().Wire;
2518  float time = hit.PeakTime() * tcc.unitsPerTick;
2519  return sqrt(PointTrajDOCA2(wire, time, tp));
2520  } // PointTrajDOCA
2521 
2523  float PointTrajDOCA(float wire, float time, TrajPoint const& tp)
2524  {
2525  return sqrt(PointTrajDOCA2(wire, time, tp));
2526  } // PointTrajDOCA
2527 
2529  float PointTrajDOCA2(float wire, float time, TrajPoint const& tp)
2530  {
2531  // returns the distance of closest approach squared between a (wire, time(WSE)) point
2532  // and a trajectory point
2533 
2534  double t = (double)(wire - tp.Pos[0]) * tp.Dir[0] + (double)(time - tp.Pos[1]) * tp.Dir[1];
2535  double dw = tp.Pos[0] + t * tp.Dir[0] - wire;
2536  double dt = tp.Pos[1] + t * tp.Dir[1] - time;
2537  return (float)(dw * dw + dt * dt);
2538 
2539  } // PointTrajDOCA2
2540 
2542  void TrajIntersection(TrajPoint const& tp1, TrajPoint const& tp2, Point2_t& pos)
2543  {
2544  TrajIntersection(tp1, tp2, pos[0], pos[1]);
2545  } // TrajIntersection
2547  void TrajIntersection(TrajPoint const& tp1, TrajPoint const& tp2, float& x, float& y)
2548  {
2549  // returns the intersection position, (x,y), of two trajectory points
2550 
2551  x = -9999;
2552  y = -9999;
2553 
2554  double arg1 = tp1.Pos[0] * tp1.Dir[1] - tp1.Pos[1] * tp1.Dir[0];
2555  double arg2 = tp2.Pos[0] * tp1.Dir[1] - tp2.Pos[1] * tp1.Dir[0];
2556  double arg3 = tp2.Dir[0] * tp1.Dir[1] - tp2.Dir[1] * tp1.Dir[0];
2557  if (arg3 == 0) return;
2558  double s = (arg1 - arg2) / arg3;
2559 
2560  x = (float)(tp2.Pos[0] + s * tp2.Dir[0]);
2561  y = (float)(tp2.Pos[1] + s * tp2.Dir[1]);
2562 
2563  } // TrajIntersection
2564 
2566  float MaxTjLen(const TCSlice& slc, std::vector<int>& tjIDs)
2567  {
2568  // returns the length of the longest Tj in the supplied list
2569  if (tjIDs.empty()) return 0;
2570  float maxLen = 0;
2571  for (auto tjid : tjIDs) {
2572  if (tjid < 1 || tjid > (int)slc.tjs.size()) continue;
2573  auto& tj = slc.tjs[tjid - 1];
2574  float sep2 = PosSep2(tj.Pts[tj.EndPt[0]].Pos, tj.Pts[tj.EndPt[1]].Pos);
2575  if (sep2 > maxLen) maxLen = sep2;
2576  } // tj
2577  return sqrt(maxLen);
2578  } // MaxTjLen
2579 
2581  float TrajLength(const Trajectory& tj)
2582  {
2583  float len = 0, dx, dy;
2584  unsigned short ipt;
2585  unsigned short prevPt = tj.EndPt[0];
2586  for (ipt = tj.EndPt[0] + 1; ipt < tj.EndPt[1] + 1; ++ipt) {
2587  if (tj.Pts[ipt].Chg == 0) continue;
2588  dx = tj.Pts[ipt].Pos[0] - tj.Pts[prevPt].Pos[0];
2589  dy = tj.Pts[ipt].Pos[1] - tj.Pts[prevPt].Pos[1];
2590  len += sqrt(dx * dx + dy * dy);
2591  prevPt = ipt;
2592  }
2593  return len;
2594  } // TrajLength
2595 
2597  float PosSep(const Point2_t& pos1, const Point2_t& pos2)
2598  {
2599  return sqrt(PosSep2(pos1, pos2));
2600  } // PosSep
2601 
2603  float PosSep2(const Point2_t& pos1, const Point2_t& pos2)
2604  {
2605  // returns the separation distance^2 between two positions
2606  float d0 = pos1[0] - pos2[0];
2607  float d1 = pos1[1] - pos2[1];
2608  return d0 * d0 + d1 * d1;
2609  } // PosSep2
2610 
2612  float TrajPointSeparation(const TrajPoint& tp1, const TrajPoint& tp2)
2613  {
2614  // Returns the separation distance between two trajectory points
2615  float dx = tp1.Pos[0] - tp2.Pos[0];
2616  float dy = tp1.Pos[1] - tp2.Pos[1];
2617  return sqrt(dx * dx + dy * dy);
2618  } // TrajPointSeparation
2619 
2622  float x,
2623  float y,
2624  unsigned short& closePt,
2625  float& DOCA)
2626  {
2627  // find the closest approach between a trajectory tj and a point (x,y). Returns
2628  // the index of the closest trajectory point and the distance. Returns false if none
2629  // of the points on the tj are within DOCA
2630 
2631  float close2 = DOCA * DOCA;
2632  closePt = 0;
2633  bool foundClose = false;
2634 
2635  for (unsigned short ipt = tj.EndPt[0]; ipt < tj.EndPt[1] + 1; ++ipt) {
2636  if (tj.Pts[ipt].Chg == 0) continue;
2637  float dx = tj.Pts[ipt].Pos[0] - x;
2638  if (std::abs(dx) > DOCA) continue;
2639  float dy = tj.Pts[ipt].Pos[1] - y;
2640  if (std::abs(dy) > DOCA) continue;
2641  float sep2 = dx * dx + dy * dy;
2642  if (sep2 < close2) {
2643  close2 = sep2;
2644  closePt = ipt;
2645  foundClose = true;
2646  }
2647  } // ipt
2648 
2649  DOCA = sqrt(close2);
2650  return foundClose;
2651 
2652  } // TrajClosestApproach
2653 
2655  float TwoTPAngle(const TrajPoint& tp1, const TrajPoint& tp2)
2656  {
2657  // Calculates the angle of a line between two TPs
2658  float dw = tp2.Pos[0] - tp1.Pos[0];
2659  float dt = tp2.Pos[1] - tp1.Pos[1];
2660  return atan2(dw, dt);
2661  } // TwoTPAngle
2662 
2664  std::vector<unsigned int> PutHitsInVector(const TCSlice& slc,
2665  PFPStruct const& pfp,
2666  HitStatus_t hitRequest)
2667  {
2668  // Put hits with the assn P -> TP3D -> TP -> Hit into a vector
2669  std::vector<unsigned int> hitVec;
2670  if (pfp.TP3Ds.empty()) return hitVec;
2671 
2672  for (auto& tp3d : pfp.TP3Ds) {
2673  if (tp3d.Flags[kTP3DBad]) continue;
2674  if (tp3d.TjID <= 0) continue;
2675  auto& tp = slc.tjs[tp3d.TjID - 1].Pts[tp3d.TPIndex];
2676  for (unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
2677  unsigned int iht = tp.Hits[ii];
2678  bool useit = (hitRequest == kAllHits);
2679  if (tp.UseHit[ii] && hitRequest == kUsedHits) useit = true;
2680  if (!tp.UseHit[ii] && hitRequest == kUnusedHits) useit = true;
2681  if (useit) hitVec.push_back(iht);
2682  }
2683  } // tp3d
2684  return hitVec;
2685  } // PutHitsInVector
2686 
2688  std::vector<unsigned int> PutTrajHitsInVector(const Trajectory& tj, HitStatus_t hitRequest)
2689  {
2690  // Put hits (which are indexed into slHits) in each trajectory point into a flat vector
2691  std::vector<unsigned int> hitVec;
2692 
2693  // special handling for shower trajectories. UseHit isn't valid
2694  if (tj.AlgMod[kShowerTj]) {
2695  for (auto& tp : tj.Pts)
2696  hitVec.insert(hitVec.end(), tp.Hits.begin(), tp.Hits.end());
2697  return hitVec;
2698  } // shower Tj
2699 
2700  // reserve under the assumption that there will be one hit per point
2701  hitVec.reserve(tj.Pts.size());
2702  for (unsigned short ipt = 0; ipt < tj.Pts.size(); ++ipt) {
2703  for (unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
2704  unsigned int iht = tj.Pts[ipt].Hits[ii];
2705  bool useit = (hitRequest == kAllHits);
2706  if (tj.Pts[ipt].UseHit[ii] && hitRequest == kUsedHits) useit = true;
2707  if (!tj.Pts[ipt].UseHit[ii] && hitRequest == kUnusedHits) useit = true;
2708  if (useit) hitVec.push_back(iht);
2709  } // iht
2710  } // ipt
2711  return hitVec;
2712  } // PutTrajHitsInVector
2713 
2715  void TagJunkTj(Trajectory& tj, bool prt)
2716  {
2717  // Characterizes the trajectory as a junk tj even though it may not
2718  // have been reconstructed in FindJunkTraj. The distinguishing feature is
2719  // that it is short and has many used hits in each trajectory point.
2720 
2721  // Don't bother if it is too long
2722  if (tj.Pts.size() > 10) return;
2723  if (tj.PDGCode == 111) return;
2724  // count the number of points that have many used hits
2725  unsigned short nhm = 0;
2726  unsigned short npwc = 0;
2727  for (auto& tp : tj.Pts) {
2728  if (tp.Chg == 0) continue;
2729  ++npwc;
2730  unsigned short nused = 0;
2731  for (unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
2732  if (tp.UseHit[ii]) ++nused;
2733  } // ii
2734  if (nused > 3) ++nhm;
2735  } // tp
2736  // Set the junkTj bit if most of the hits are used in most of the tps
2737  if (nhm > 0.5 * npwc) tj.AlgMod[kJunkTj] = true;
2738  if (prt)
2739  mf::LogVerbatim("TC") << "TGT: T" << tj.ID << " npwc " << npwc << " nhm " << nhm << " junk? "
2740  << tj.AlgMod[kJunkTj];
2741  } // TagJunkTj
2742 
2744  bool HasDuplicateHits(const TCSlice& slc, Trajectory const& tj, bool prt)
2745  {
2746  // returns true if a hit is associated with more than one TP
2747  auto tjHits = PutTrajHitsInVector(tj, kAllHits);
2748  for (unsigned short ii = 0; ii < tjHits.size() - 1; ++ii) {
2749  for (unsigned short jj = ii + 1; jj < tjHits.size(); ++jj) {
2750  if (tjHits[ii] == tjHits[jj]) {
2751  if (prt)
2752  mf::LogVerbatim("TC") << "HDH: Hit " << PrintHit(slc.slHits[ii]) << " is a duplicate "
2753  << ii << " " << jj;
2754  return true;
2755  }
2756  } // jj
2757  } // ii
2758  return false;
2759  } // HasDuplicateHits
2760 
2762  void MoveTPToWire(TrajPoint& tp, float wire)
2763  {
2764  // Project TP to a "wire position" Pos[0] and update Pos[1]
2765  if (tp.Dir[0] == 0) return;
2766  float dw = wire - tp.Pos[0];
2767  if (std::abs(dw) < 0.01) return;
2768  tp.Pos[0] = wire;
2769  tp.Pos[1] += dw * tp.Dir[1] / tp.Dir[0];
2770  } // MoveTPToWire
2771 
2773  std::vector<unsigned int> FindCloseHits(const TCSlice& slc,
2774  std::array<int, 2> const& wireWindow,
2775  Point2_t const& timeWindow,
2776  const unsigned short plane,
2777  HitStatus_t hitRequest,
2778  bool usePeakTime,
2779  bool& hitsNear)
2780  {
2781  // returns a vector of hits that are within the Window[Pos0][Pos1] in plane.
2782  // Note that hits on wire wireWindow[1] are returned as well. The definition of close
2783  // depends on setting of usePeakTime. If UsePeakTime is true, a hit is considered nearby if
2784  // the PeakTime is within the window. This is shown schematically here where
2785  // the time is on the horizontal axis and a "-" denotes a valid entry
2786  // timeWindow -----------------
2787  // hit PeakTime + close
2788  // hit PeakTime + not close
2789  // If usePeakTime is false, a hit is considered nearby if the hit StartTick and EndTick overlap with the timeWindow
2790  // Time window ---------
2791  // Hit StartTick-EndTick -------- close
2792  // Hit StartTick - EndTick -------- not close
2793 
2794  hitsNear = false;
2795  std::vector<unsigned int> closeHits;
2796  if (plane > slc.firstWire.size() - 1) return closeHits;
2797  // window in the wire coordinate
2798  int loWire = wireWindow[0];
2799  if (loWire < (int)slc.firstWire[plane]) loWire = slc.firstWire[plane];
2800  int hiWire = wireWindow[1];
2801  if (hiWire > (int)slc.lastWire[plane] - 1) hiWire = slc.lastWire[plane] - 1;
2802  // window in the time coordinate
2803  float minTick = timeWindow[0] / tcc.unitsPerTick;
2804  float maxTick = timeWindow[1] / tcc.unitsPerTick;
2805  for (int wire = loWire; wire <= hiWire; ++wire) {
2806  // Set hitsNear if the wire is dead
2807  if (!evt.goodWire[plane][wire]) hitsNear = true;
2808  if (slc.wireHitRange[plane][wire].first == UINT_MAX) continue;
2809  unsigned int firstHit = slc.wireHitRange[plane][wire].first;
2810  unsigned int lastHit = slc.wireHitRange[plane][wire].second;
2811  for (unsigned int iht = firstHit; iht <= lastHit; ++iht) {
2812  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
2813  if (usePeakTime) {
2814  if (hit.PeakTime() < minTick) continue;
2815  if (hit.PeakTime() > maxTick) break;
2816  }
2817  else {
2818  int hiLo = minTick;
2819  if (hit.StartTick() > hiLo) hiLo = hit.StartTick();
2820  int loHi = maxTick;
2821  if (hit.EndTick() < loHi) loHi = hit.EndTick();
2822  if (loHi < hiLo) continue;
2823  if (hiLo > loHi) break;
2824  }
2825  hitsNear = true;
2826  bool takeit = (hitRequest == kAllHits);
2827  if (hitRequest == kUsedHits && slc.slHits[iht].InTraj > 0) takeit = true;
2828  if (hitRequest == kUnusedHits && slc.slHits[iht].InTraj == 0) takeit = true;
2829  if (takeit) closeHits.push_back(iht);
2830  } // iht
2831  } // wire
2832  return closeHits;
2833  } // FindCloseHits
2834 
2836  bool FindCloseHits(TCSlice const& slc,
2837  TrajPoint& tp,
2838  float const maxDelta,
2839  HitStatus_t hitRequest)
2840  {
2841  // Fills tp.Hits sets tp.UseHit true for hits that are close to tp.Pos. Returns true if there are
2842  // close hits OR if the wire at this position is dead
2843 
2844  tp.Hits.clear();
2845  tp.UseHit.reset();
2846  tp.Environment.reset();
2847  if (!WireHitRangeOK(slc, tp.CTP)) { return false; }
2848 
2849  if (tp.Pos[0] < -0.4) return false;
2850  unsigned short plane = DecodeCTP(tp.CTP).Plane;
2851  unsigned int wire = std::nearbyint(tp.Pos[0]);
2852  if (wire < slc.firstWire[plane]) return false;
2853  if (wire > slc.lastWire[plane] - 1) return false;
2854 
2855  // dead wire
2856  if (!evt.goodWire[plane][wire]) {
2857  tp.Environment[kEnvNotGoodWire] = true;
2858  return true;
2859  }
2860  tp.Environment[kEnvNotGoodWire] = false;
2861  // live wire with no hits
2862  if (slc.wireHitRange[plane][wire].first == UINT_MAX) return false;
2863 
2864  unsigned int firstHit = slc.wireHitRange[plane][wire].first;
2865  unsigned int lastHit = slc.wireHitRange[plane][wire].second;
2866 
2867  float fwire = wire;
2868  for (unsigned int iht = firstHit; iht <= lastHit; ++iht) {
2869  if ((unsigned int)slc.slHits[iht].InTraj > slc.tjs.size()) continue;
2870  bool useit = (hitRequest == kAllHits);
2871  if (hitRequest == kUsedHits && slc.slHits[iht].InTraj > 0) useit = true;
2872  if (hitRequest == kUnusedHits && slc.slHits[iht].InTraj == 0) useit = true;
2873  if (!useit) continue;
2874  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
2875  float ftime = tcc.unitsPerTick * hit.PeakTime();
2876  float delta = PointTrajDOCA(fwire, ftime, tp);
2877  if (delta < maxDelta) tp.Hits.push_back(iht);
2878  } // iht
2879  if (tp.Hits.size() > 16) { tp.Hits.resize(16); }
2880  // Set UseHit false. The calling routine should decide if these hits should be used
2881  tp.UseHit.reset();
2882  return (!tp.Hits.empty());
2883 
2884  } // FindCloseHits
2885 
2887  unsigned short NearbyCleanPt(const Trajectory& tj, unsigned short end)
2888  {
2889  // Searches for a TP near the end (or beginnin) that doesn't have the kEnvOverlap bit set
2890  // with the intent that a fit of a vertex position using this tj will be minimally
2891  // biased if there are no nearby hits from other tjs. A search is done from the
2892  // supplied nearPt moving in the + direction if nearPt == tj.EndPt[0] and moving in
2893  // the - direction if nearPt == tj.EndPt[1]
2894  if (end > 1) return USHRT_MAX;
2895  short dir = 1;
2896  if (end == 1) dir = -1;
2897  for (short ii = 0; ii < (short)tj.Pts.size(); ++ii) {
2898  short ipt = tj.EndPt[end] + dir * ii;
2899  if (ipt < 0 || ipt >= (short)tj.Pts.size()) return USHRT_MAX;
2900  auto& tp = tj.Pts[ipt];
2901  if (!tp.Environment[kEnvOverlap]) return ipt;
2902  } // ii
2903  return tj.EndPt[end];
2904  } // FindCleanPt
2905 
2907  std::vector<int> FindCloseTjs(const TCSlice& slc,
2908  const TrajPoint& fromTp,
2909  const TrajPoint& toTp,
2910  const float& maxDelta)
2911  {
2912  // Returns a list of Tj IDs that have hits within distance maxDelta on a line drawn between the two Tps as shown
2913  // graphically here, where a "*" is a Tp and "|" and "-" are the boundaries of the region that is checked
2914  //
2915  // ---------------
2916  // | |
2917  // * *
2918  // | |
2919  // ---------------
2920  // If the wire positions of fromTp and toTp are the same, a different region is checked as shown here
2921  //
2922  // -----------
2923  // | |
2924  // | * |
2925  // | |
2926  // -----------
2927 
2928  std::vector<int> tmp;
2929  if (fromTp.Pos[0] < -0.4 || toTp.Pos[0] < -0.4) return tmp;
2930 
2931  TrajPoint tp;
2932  // Make the tp so that stepping is positive
2933  unsigned int firstWire, lastWire;
2934  if (toTp.Pos[0] > fromTp.Pos[0]) {
2935  if (!MakeBareTrajPoint(fromTp, toTp, tp)) return tmp;
2936  firstWire = std::nearbyint(fromTp.Pos[0]);
2937  lastWire = std::nearbyint(toTp.Pos[0]);
2938  }
2939  else if (toTp.Pos[0] < fromTp.Pos[0]) {
2940  if (!MakeBareTrajPoint(toTp, fromTp, tp)) return tmp;
2941  firstWire = std::nearbyint(toTp.Pos[0]);
2942  lastWire = std::nearbyint(fromTp.Pos[0]);
2943  }
2944  else {
2945  tp.Pos = fromTp.Pos;
2946  float tmp = fromTp.Pos[0] - maxDelta;
2947  if (tmp < 0) tmp = 0;
2948  firstWire = std::nearbyint(tmp);
2949  tmp = fromTp.Pos[0] + maxDelta;
2950  lastWire = std::nearbyint(tmp);
2951  }
2952 
2953  unsigned short plane = DecodeCTP(tp.CTP).Plane;
2954 
2955  if (firstWire < slc.firstWire[plane]) firstWire = slc.firstWire[plane];
2956  if (firstWire > slc.lastWire[plane] - 1) return tmp;
2957  if (lastWire < slc.firstWire[plane]) return tmp;
2958  if (lastWire > slc.lastWire[plane] - 1) lastWire = slc.lastWire[plane] - 1;
2959 
2960  for (unsigned int wire = firstWire; wire <= lastWire; ++wire) {
2961  if (slc.wireHitRange[plane][wire].first == UINT_MAX) continue;
2962  MoveTPToWire(tp, (float)wire);
2963  // Find the tick range at this position
2964  float minTick = (tp.Pos[1] - maxDelta) / tcc.unitsPerTick;
2965  float maxTick = (tp.Pos[1] + maxDelta) / tcc.unitsPerTick;
2966  unsigned int firstHit = slc.wireHitRange[plane][wire].first;
2967  unsigned int lastHit = slc.wireHitRange[plane][wire].second;
2968  for (unsigned int iht = firstHit; iht <= lastHit; ++iht) {
2969  if (slc.slHits[iht].InTraj <= 0) continue;
2970  if ((unsigned int)slc.slHits[iht].InTraj > slc.tjs.size()) continue;
2971  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
2972  if (hit.PeakTime() < minTick) continue;
2973  // Hits are sorted by increasing time so we can break when maxTick is reached
2974  if (hit.PeakTime() > maxTick) break;
2975  if (std::find(tmp.begin(), tmp.end(), slc.slHits[iht].InTraj) != tmp.end()) continue;
2976  tmp.push_back(slc.slHits[iht].InTraj);
2977  } // iht
2978  } // wire
2979 
2980  return tmp;
2981 
2982  } // FindCloseTjs
2983 
2985  float KinkSignificance(TCSlice const& slc,
2986  Trajectory const& tj1,
2987  unsigned short end1,
2988  Trajectory const& tj2,
2989  unsigned short end2,
2990  unsigned short nPtsFit,
2991  bool useChg,
2992  bool prt)
2993  {
2994  // returns the significance of a potential kink between the ends of two trajectories. This
2995  // is used when deciding to either merge trajectories or make a vertex between them
2996 
2997  if (tj1.CTP != tj2.CTP) return -1;
2998  if (end1 > 1 || end2 > 1) return -1;
2999 
3000  // construct a temporary trajectory to allow using the standard KinkSignificance function.
3001  // The first nPtsFit points are comprised of TPs from tj1 and the last nPtsFits points are from tj2
3002  Trajectory tj;
3003  tj.ID = 666;
3004  tj.CTP = tj1.CTP;
3005  short dir = 1;
3006  if (end1 == 1) dir = -1;
3007  unsigned short cnt = 0;
3008  // add tj1 points to the trajectory
3009  for (short ii = 0; ii < (short)tj1.Pts.size(); ++ii) {
3010  short ipt = tj1.EndPt[end1] + dir * ii;
3011  if (ipt < 0) break;
3012  if (ipt >= (short)tj1.Pts.size()) break;
3013  auto& tp = tj1.Pts[ipt];
3014  if (tp.Chg <= 0) continue;
3015  tj.Pts.push_back(tp);
3016  ++cnt;
3017  if (cnt == nPtsFit + 1) break;
3018  } // ipt
3019  if (cnt < nPtsFit) return -1;
3020  // add tj2 points to the trajectory
3021  dir = 1;
3022  if (end2 == 1) dir = -1;
3023  cnt = 0;
3024  for (short ii = 0; ii < (short)tj2.Pts.size(); ++ii) {
3025  short ipt = tj2.EndPt[end2] + dir * ii;
3026  if (ipt < 0) break;
3027  if (ipt >= (short)tj2.Pts.size()) break;
3028  auto& tp = tj2.Pts[ipt];
3029  if (tp.Chg <= 0) continue;
3030  tj.Pts.push_back(tp);
3031  ++cnt;
3032  if (cnt == nPtsFit + 1) break;
3033  } // ipt
3034  tj.EndPt[0] = 0;
3035  tj.EndPt[1] = tj.Pts.size() - 1;
3036  return KinkSignificance(slc, tj, nPtsFit, nPtsFit, useChg, prt);
3037  } // KinkSignificance
3038 
3040  float KinkSignificance(TCSlice const& slc,
3041  Trajectory& tj,
3042  unsigned short kinkPt,
3043  unsigned short nPtsFit,
3044  bool useChg,
3045  bool prt)
3046  {
3047  // returns a kink significance in the trajectory at the presumed kink point kinkPt
3048  // using angle and (optional) charge asymmetry. The returned value is negative if there is insufficient
3049  // information.
3050  //
3051  // Check the limits
3052  if (kinkPt < tj.EndPt[0] + 2) return -1;
3053  if (kinkPt > tj.EndPt[1] - 2) return -1;
3054 
3055  // This function requires knowledge of the DOF of the line fit
3056  if (nPtsFit < 3) return -1;
3057  unsigned short npwc = NumPtsWithCharge(slc, tj, false);
3058  // need enough points to do a fit on each sideof the presumed kink point
3059  if (npwc < 2 * nPtsFit + 1) return -1;
3060 
3061  // The hit charge uncertainty is 0.12 - 0.15 (neglecting 2ndry interactions) for hadrons.
3062  // This translates into an error on the charge
3063  // asymmetry of about 0.07, or about 0.6 * the charge uncertainty
3064  double chgRMS = 0.07;
3065  // An additional contribution to the rms is the dependence on the DOF of the fit.
3066  // Apply a factor to the significance similar to (and simpler than) the Students t-distribution
3067  // This will increase the angle and charge error rms by 1.3 (1.05) when nPtsFit = 3 (8)
3068  double tFactor = 1 + 0.3 / double(nPtsFit - 2);
3069  chgRMS *= tFactor;
3070 
3071  // Fit the trajectory direction on the + side
3072  short fitDir = 1;
3073  TrajPoint tpPos;
3074  FitTraj(slc, tj, kinkPt, nPtsFit, fitDir, tpPos);
3075  if (tpPos.FitChi > 900) return -1;
3076  // repeat the trajectory fit on the - side
3077  fitDir = -1;
3078  TrajPoint tpNeg;
3079  FitTraj(slc, tj, kinkPt, nPtsFit, fitDir, tpNeg);
3080  if (tpNeg.FitChi > 900) return -1;
3081  double angErr = tpNeg.AngErr;
3082  if (tpPos.AngErr > angErr) angErr = tpPos.AngErr;
3083  angErr *= tFactor;
3084  double dang = DeltaAngle(tpPos.Ang, tpNeg.Ang);
3085  double dangSig = dang / angErr;
3086 
3087  double chgAsym = 0;
3088  double chgSig = 0;
3089  if (useChg) {
3090  // Sum the charge Neg and Pos, excluding the kinkPt
3091  double chgNeg = 0;
3092  unsigned short cntNeg = 0;
3093  for (unsigned short ipt = kinkPt - 1; ipt >= tj.EndPt[0]; --ipt) {
3094  auto& tp = tj.Pts[ipt];
3095  if (tp.Chg <= 0) continue;
3096  chgNeg += tp.Chg;
3097  ++cntNeg;
3098  if (cntNeg == nPtsFit) break;
3099  if (ipt == 0) break;
3100  } // ipt
3101  if (cntNeg != nPtsFit) {
3102  if (prt) mf::LogVerbatim("TC") << " KL: Bad cntNeg " << cntNeg << " != " << nPtsFit;
3103  return -1;
3104  }
3105  // now Pos
3106  double chgPos = 0;
3107  unsigned short cntPos = 0;
3108  for (unsigned short ipt = kinkPt + 1; ipt <= tj.EndPt[1]; ++ipt) {
3109  auto& tp = tj.Pts[ipt];
3110  if (tp.Chg <= 0) continue;
3111  chgPos += tp.Chg;
3112  ++cntPos;
3113  if (cntPos == nPtsFit) break;
3114  } // ipt
3115  if (cntPos != nPtsFit) {
3116  if (prt) mf::LogVerbatim("TC") << " KL: Bad cntPos " << cntPos << " != " << nPtsFit;
3117  return -1;
3118  }
3119  chgNeg /= (float)nPtsFit;
3120  chgPos /= (float)nPtsFit;
3121  // The charge asymmetry varies between 0 and 1;
3122  chgAsym = std::abs(chgPos - chgNeg) / (chgPos + chgNeg);
3123  // calculate the charge asymmetry significance
3124  chgSig = chgAsym / chgRMS;
3125  } // useChg
3126  double kinkSig = sqrt(dangSig * dangSig + chgSig * chgSig);
3127 
3128  if (prt) {
3129  mf::LogVerbatim myprt("TC");
3130  myprt << "KL: T" << tj.ID << " kinkPt " << PrintPos(tj.Pts[kinkPt]);
3131  myprt << " nPtsFit " << nPtsFit;
3132  myprt << " dang " << std::fixed << std::setprecision(3) << dang;
3133  myprt << std::fixed << std::setprecision(3) << " angErr " << angErr;
3134  myprt << std::setprecision(2) << " sig " << dangSig;
3135  myprt << " chgAsym " << chgAsym;
3136  myprt << " chgSig " << chgSig;
3137  myprt << " kinkSig " << kinkSig;
3138  }
3139  return (float)kinkSig;
3140  } // KinkSignificance
3141 
3143  float ElectronLikelihood(const TCSlice& slc, const Trajectory& tj)
3144  {
3145  // returns a number between 0 (not electron-like) and 1 (electron-like)
3146  if (NumPtsWithCharge(slc, tj, false) < 8) return -1;
3147  if (tj.EndFlag[0][kBragg] || tj.EndFlag[1][kBragg]) return 0;
3148 
3149  unsigned short midPt = 0.5 * (tj.EndPt[0] + tj.EndPt[1]);
3150  double rms0 = 0, rms1 = 0;
3151  unsigned short cnt;
3152  TjDeltaRMS(tj, tj.EndPt[0], midPt, rms0, cnt);
3153  TjDeltaRMS(tj, midPt, tj.EndPt[1], rms1, cnt);
3154  float asym = std::abs(rms0 - rms1) / (rms0 + rms1);
3155  float chgFact = (tj.ChgRMS - 0.1) * 5;
3156  float elh = 5 * asym * chgFact;
3157  if (elh > 1) elh = 1;
3158  return elh;
3159  } // ElectronLikelihood
3160 
3162  float ChgFracNearPos(const TCSlice& slc, const Point2_t& pos, const std::vector<int>& tjIDs)
3163  {
3164  // returns the fraction of the charge in the region around pos that is associated with
3165  // the list of Tj IDs
3166  if (tjIDs.empty()) return 0;
3167  std::array<int, 2> wireWindow;
3168  Point2_t timeWindow;
3169  // 1/2 size of the region
3170  constexpr float NNDelta = 5;
3171  wireWindow[0] = pos[0] - NNDelta;
3172  wireWindow[1] = pos[0] + NNDelta;
3173  timeWindow[0] = pos[1] - NNDelta;
3174  timeWindow[1] = pos[1] + NNDelta;
3175  // do some checking
3176  for (auto& tjID : tjIDs)
3177  if (tjID <= 0 || tjID > (int)slc.tjs.size()) return 0;
3178  // Determine which plane we are in
3179  geo::PlaneID planeID = DecodeCTP(slc.tjs[tjIDs[0] - 1].CTP);
3180  // get a list of all hits in this region
3181  bool hitsNear;
3182  std::vector<unsigned int> closeHits =
3183  FindCloseHits(slc, wireWindow, timeWindow, planeID.Plane, kAllHits, true, hitsNear);
3184  if (closeHits.empty()) return 0;
3185  float chg = 0;
3186  float tchg = 0;
3187  // Add the hit charge in the box
3188  // All hits in the box, and all hits associated with the Tjs
3189  for (auto& iht : closeHits) {
3190  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
3191  chg += hit.Integral();
3192  if (slc.slHits[iht].InTraj == 0) continue;
3193  if (std::find(tjIDs.begin(), tjIDs.end(), slc.slHits[iht].InTraj) != tjIDs.end())
3194  tchg += hit.Integral();
3195  } // iht
3196  if (chg == 0) return 0;
3197  return tchg / chg;
3198  } // ChgFracNearPos
3199 
3201  float MaxHitDelta(TCSlice& slc, Trajectory& tj)
3202  {
3203  float delta, md = 0;
3204  unsigned short ii;
3205  unsigned int iht;
3206  for (auto& tp : tj.Pts) {
3207  for (ii = 0; ii < tp.Hits.size(); ++ii) {
3208  if (!tp.UseHit[ii]) continue;
3209  iht = tp.Hits[ii];
3210  delta = PointTrajDOCA(slc, iht, tp);
3211  if (delta > md) md = delta;
3212  } // ii
3213  } // pts
3214  return md;
3215  } // MaxHitDelta
3216 
3219  {
3220  // reverse the trajectory
3221  if (tj.Pts.empty()) return;
3222  // reverse the crawling direction flag
3223  tj.StepDir = -tj.StepDir;
3224  // Vertices
3225  std::swap(tj.VtxID[0], tj.VtxID[1]);
3226  // trajectory points
3227  std::reverse(tj.Pts.begin(), tj.Pts.end());
3228  // reverse the stop flag
3229  std::reverse(tj.EndFlag.begin(), tj.EndFlag.end());
3230  std::swap(tj.dEdx[0], tj.dEdx[1]);
3231  // reverse the direction vector on all points
3232  for (unsigned short ipt = 0; ipt < tj.Pts.size(); ++ipt) {
3233  if (tj.Pts[ipt].Dir[0] != 0) tj.Pts[ipt].Dir[0] = -tj.Pts[ipt].Dir[0];
3234  if (tj.Pts[ipt].Dir[1] != 0) tj.Pts[ipt].Dir[1] = -tj.Pts[ipt].Dir[1];
3235  if (tj.Pts[ipt].Ang > 0) { tj.Pts[ipt].Ang -= M_PI; }
3236  else {
3237  tj.Pts[ipt].Ang += M_PI;
3238  }
3239  } // ipt
3240  if (tj.StartEnd == 0 || tj.StartEnd == 1) tj.StartEnd = 1 - tj.StartEnd;
3241  SetEndPoints(tj);
3242  } // ReverseTraj
3243 
3245  bool PointInsideEnvelope(const Point2_t& Point, const std::vector<Point2_t>& Envelope)
3246  {
3247  // returns true if the Point is within the Envelope polygon. Entries in Envelope are the
3248  // Pos[0], Pos[1] locations of the polygon vertices. This is based on the algorithm that the
3249  // sum of the angles of a vector between a point and the vertices will be 2 * pi for an interior
3250  // point and 0 for an exterior point
3251 
3252  Point2_t p1, p2;
3253  unsigned short nvx = Envelope.size();
3254  double angleSum = 0;
3255  for (unsigned short ii = 0; ii < Envelope.size(); ++ii) {
3256  p1[0] = Envelope[ii][0] - Point[0];
3257  p1[1] = Envelope[ii][1] - Point[1];
3258  p2[0] = Envelope[(ii + 1) % nvx][0] - Point[0];
3259  p2[1] = Envelope[(ii + 1) % nvx][1] - Point[1];
3260  angleSum += DeltaAngle(p1, p2);
3261  }
3262  if (abs(angleSum) < M_PI) return false;
3263  return true;
3264 
3265  } // InsideEnvelope
3266 
3268  bool SetMag(Vector2_t& v1, double mag)
3269  {
3270  double den = v1[0] * v1[0] + v1[1] * v1[1];
3271  if (den == 0) return false;
3272  den = sqrt(den);
3273 
3274  v1[0] *= mag / den;
3275  v1[1] *= mag / den;
3276  return true;
3277  } // SetMag
3278 
3280  void FindAlongTrans(Point2_t pos1, Vector2_t dir1, Point2_t pos2, Point2_t& alongTrans)
3281  {
3282  // Calculate the distance along and transverse to the direction vector dir1 from pos1 to pos2
3283  alongTrans[0] = 0;
3284  alongTrans[1] = 0;
3285  if (pos1[0] == pos2[0] && pos1[1] == pos2[1]) return;
3286  pos1[0] = pos2[0] - pos1[0];
3287  pos1[1] = pos2[1] - pos1[1];
3288  double sep = sqrt(pos1[0] * pos1[0] + pos1[1] * pos1[1]);
3289  if (sep < 1E-6) return;
3290  Vector2_t ptDir;
3291  ptDir[0] = pos1[0] / sep;
3292  ptDir[1] = pos1[1] / sep;
3293  SetMag(dir1, 1.0);
3294  double costh = DotProd(dir1, ptDir);
3295  if (costh > 1.0 || costh < -1.0) return;
3296  alongTrans[0] = costh * sep;
3297  double sinth = sqrt(1 - costh * costh);
3298  alongTrans[1] = sinth * sep;
3299  } // FindAlongTrans
3300 
3302  double DeltaAngle(const Point2_t& p1, const Point2_t& p2)
3303  {
3304  // angle between two points
3305  double ang1 = atan2(p1[1], p1[0]);
3306  double ang2 = atan2(p2[1], p2[0]);
3307  return DeltaAngle2(ang1, ang2);
3308  } // DeltaAngle
3309 
3311  double DeltaAngle2(double Ang1, double Ang2)
3312  {
3313  constexpr double twopi = 2 * M_PI;
3314  double dang = Ang1 - Ang2;
3315  while (dang > M_PI)
3316  dang -= twopi;
3317  while (dang < -M_PI)
3318  dang += twopi;
3319  return dang;
3320  }
3321 
3323  double DeltaAngle(double Ang1, double Ang2)
3324  {
3325  return std::abs(std::remainder(Ang1 - Ang2, M_PI));
3326  }
3327 
3330  {
3331  // Find the first (last) TPs, EndPt[0] (EndPt[1], that have charge
3332 
3333  // don't mess with showerTjs or halo tjs
3334  if (tj.AlgMod[kShowerTj] || tj.AlgMod[kHaloTj]) return;
3335 
3336  tj.EndPt[0] = 0;
3337  tj.EndPt[1] = 0;
3338  if (tj.Pts.size() == 0) return;
3339 
3340  // check the end point pointers
3341  for (unsigned short ipt = 0; ipt < tj.Pts.size(); ++ipt) {
3342  if (tj.Pts[ipt].Chg != 0) {
3343  tj.EndPt[0] = ipt;
3344  break;
3345  }
3346  }
3347  for (unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
3348  unsigned short ipt = tj.Pts.size() - 1 - ii;
3349  if (tj.Pts[ipt].Chg != 0) {
3350  tj.EndPt[1] = ipt;
3351  break;
3352  }
3353  }
3354  } // SetEndPoints
3355 
3357  bool TrajIsClean(Trajectory const& tj, bool prt)
3358  {
3359  // Returns true if the trajectory has low hit multiplicity and is in a
3360  // clean environment
3361  unsigned short nUsed = 0;
3362  unsigned short nTotHits = 0;
3363  for (unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
3364  TrajPoint const& tp = tj.Pts[ipt];
3365  nTotHits += tp.Hits.size();
3366  for (unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
3367  if (tp.UseHit[ii]) ++nUsed;
3368  } // ii
3369  } // ipt
3370  if (nTotHits == 0) return false;
3371  float fracUsed = (float)nUsed / (float)nTotHits;
3372  if (prt)
3373  mf::LogVerbatim("TC") << "TrajIsClean: nTotHits " << nTotHits << " nUsed " << nUsed
3374  << " fracUsed " << fracUsed;
3375 
3376  return fracUsed > 0.9;
3377  } // TrajIsClean
3378 
3380  short MCSMom(const TCSlice& slc, const std::vector<int>& tjIDs)
3381  {
3382  // Find the average MCSMom of the trajectories
3383  if (tjIDs.empty()) return 0;
3384  float summ = 0;
3385  float suml = 0;
3386  for (auto tjid : tjIDs) {
3387  auto& tj = slc.tjs[tjid - 1];
3388  float npts = tj.EndPt[1] - tj.EndPt[0] + 1;
3389  summ += npts * tj.MCSMom;
3390  suml += npts;
3391  } // tjid
3392  return (short)(summ / suml);
3393  } // MCSMom
3394 
3396  short MCSMom(const TCSlice& slc, const Trajectory& tj)
3397  {
3398  return MCSMom(slc, tj, tj.EndPt[0], tj.EndPt[1]);
3399  } // MCSMom
3400 
3402  short MCSMom(const TCSlice& slc,
3403  const Trajectory& tj,
3404  unsigned short firstPt,
3405  unsigned short lastPt)
3406  {
3407  // Estimate the trajectory momentum using Multiple Coulomb Scattering ala PDG RPP
3408 
3409  if (firstPt == lastPt) return 0;
3410  if (firstPt > lastPt) std::swap(firstPt, lastPt);
3411 
3412  firstPt = NearestPtWithChg(tj, firstPt);
3413  lastPt = NearestPtWithChg(tj, lastPt);
3414  if (firstPt >= lastPt) return 0;
3415 
3416  if (firstPt < tj.EndPt[0]) return 0;
3417  if (lastPt > tj.EndPt[1]) return 0;
3418  // Can't do this with only 2 points
3419  if (NumPtsWithCharge(slc, tj, false, firstPt, lastPt) < 3) return 0;
3420  // Ignore junk Tjs
3421  if (tj.AlgMod[kJunkTj]) return 0;
3422 
3423  double tjLen = TrajPointSeparation(tj.Pts[firstPt], tj.Pts[lastPt]);
3424  if (tjLen < 1) return 0;
3425  // mom calculated in MeV
3426  double thetaRMS = MCSThetaRMS(tj, firstPt, lastPt);
3427  if (thetaRMS < 0.001) return 999;
3428  double mom = 13.8 * sqrt(tjLen / 14) / thetaRMS;
3429  if (mom > 999) mom = 999;
3430  return (short)mom;
3431  } // MCSMom
3432 
3434  unsigned short NearestPtWithChg(const Trajectory& tj, unsigned short thePt)
3435  {
3436  // returns a point near thePt which has charge
3437  if (thePt > tj.EndPt[1]) return thePt;
3438  if (tj.Pts[thePt].Chg > 0) return thePt;
3439 
3440  short endPt0 = tj.EndPt[0];
3441  short endPt1 = tj.EndPt[1];
3442  for (short off = 1; off < 10; ++off) {
3443  short ipt = thePt + off;
3444  if (ipt <= endPt1 && tj.Pts[ipt].Chg > 0) return (unsigned short)ipt;
3445  ipt = thePt - off;
3446  if (ipt >= endPt0 && tj.Pts[ipt].Chg > 0) return (unsigned short)ipt;
3447  } // off
3448  return thePt;
3449  } // NearestPtWithChg
3450 
3452  float MCSThetaRMS(const Trajectory& tj)
3453  {
3454  // This returns the MCS scattering angle expected for one WSE unit of travel along the trajectory.
3455  // It is used to define kink and vertex cuts. This should probably be named something different to
3456  // prevent confusion
3457 
3458  float tps = TrajPointSeparation(tj.Pts[tj.EndPt[0]], tj.Pts[tj.EndPt[1]]);
3459  if (tps < 1) return 1;
3460 
3461  return MCSThetaRMS(tj, tj.EndPt[0], tj.EndPt[1]) / sqrt(tps);
3462 
3463  } // MCSThetaRMS
3464 
3466  double MCSThetaRMS(const Trajectory& tj, unsigned short firstPt, unsigned short lastPt)
3467  {
3468  // This returns the MCS scattering angle expected for the length of the trajectory
3469  // spanned by firstPt to lastPt. It is used primarily to calculate MCSMom
3470 
3471  if (firstPt < tj.EndPt[0]) return 1;
3472  if (lastPt > tj.EndPt[1]) return 1;
3473 
3474  firstPt = NearestPtWithChg(tj, firstPt);
3475  lastPt = NearestPtWithChg(tj, lastPt);
3476  if (firstPt >= lastPt) return 1;
3477 
3478  double sigmaS;
3479  unsigned short cnt;
3480  TjDeltaRMS(tj, firstPt, lastPt, sigmaS, cnt);
3481  if (sigmaS < 0) return 1;
3482  double tjLen = TrajPointSeparation(tj.Pts[firstPt], tj.Pts[lastPt]);
3483  if (tjLen < 1) return 1;
3484  // Theta_o = 4 * sqrt(3) * sigmaS / path
3485  return (6.8 * sigmaS / tjLen);
3486 
3487  } // MCSThetaRMS
3488 
3490  void TjDeltaRMS(const Trajectory& tj,
3491  unsigned short firstPt,
3492  unsigned short lastPt,
3493  double& rms,
3494  unsigned short& cnt)
3495  {
3496  // returns the rms scatter of points around a line formed by the firstPt and lastPt of the trajectory
3497 
3498  rms = -1;
3499  if (firstPt < tj.EndPt[0]) return;
3500  if (lastPt > tj.EndPt[1]) return;
3501 
3502  firstPt = NearestPtWithChg(tj, firstPt);
3503  lastPt = NearestPtWithChg(tj, lastPt);
3504  if (firstPt >= lastPt) return;
3505 
3506  TrajPoint tmp;
3507  // make a bare trajectory point to define a line between firstPt and lastPt.
3508  // Use the position of the hits at these points
3509  TrajPoint firstTP = tj.Pts[firstPt];
3510  firstTP.Pos = firstTP.HitPos;
3511  TrajPoint lastTP = tj.Pts[lastPt];
3512  lastTP.Pos = lastTP.HitPos;
3513  if (!MakeBareTrajPoint(firstTP, lastTP, tmp)) return;
3514  // sum up the deviations^2
3515  double dsum = 0;
3516  cnt = 0;
3517  for (unsigned short ipt = firstPt + 1; ipt < lastPt; ++ipt) {
3518  if (tj.Pts[ipt].Chg == 0) continue;
3519  // ignore points with large error
3520  if (tj.Pts[ipt].HitPosErr2 > 4) continue;
3521  dsum += PointTrajDOCA2(tj.Pts[ipt].HitPos[0], tj.Pts[ipt].HitPos[1], tmp);
3522  ++cnt;
3523  } // ipt
3524  if (cnt < 2) return;
3525  rms = sqrt(dsum / (double)cnt);
3526 
3527  } // TjDeltaRMS
3528 
3530  void SetTPEnvironment(TCSlice& slc, CTP_t inCTP)
3531  {
3532  // This function is called after tj reconstruction is completed to set TP Environment
3533  // bits that are dependent on reconstruction, just kEnvNearMuon for now. This bit is
3534  // set for all TPs that are within 5 wire-equivalents of a muon
3535 
3536  std::array<int, 2> wireWindow;
3537  Point2_t timeWindow;
3538  unsigned short plane = DecodeCTP(inCTP).Plane;
3539  //
3540  float delta = 5;
3541 
3542  for (auto& mutj : slc.tjs) {
3543  if (mutj.AlgMod[kKilled]) continue;
3544  if (mutj.CTP != inCTP) continue;
3545  if (mutj.PDGCode != 13) continue;
3546  unsigned short nnear = 0;
3547  for (unsigned short ipt = mutj.EndPt[0]; ipt <= mutj.EndPt[1]; ++ipt) {
3548  auto& tp = mutj.Pts[ipt];
3549  wireWindow[0] = tp.Pos[0];
3550  wireWindow[1] = tp.Pos[0];
3551  timeWindow[0] = tp.Pos[1] - delta;
3552  timeWindow[1] = tp.Pos[1] + delta;
3553  // get a list of all hits in this region
3554  bool hitsNear;
3555  auto closeHits =
3556  FindCloseHits(slc, wireWindow, timeWindow, plane, kAllHits, true, hitsNear);
3557  if (closeHits.empty()) continue;
3558  for (auto iht : closeHits) {
3559  auto inTraj = slc.slHits[iht].InTraj;
3560  if (inTraj <= 0) continue;
3561  if (inTraj == mutj.ID) continue;
3562  auto& dtj = slc.tjs[inTraj - 1];
3563  if (dtj.PDGCode == 13) continue;
3564  for (unsigned short jpt = dtj.EndPt[0]; jpt <= dtj.EndPt[1]; ++jpt) {
3565  auto& dtp = dtj.Pts[jpt];
3566  if (std::find(dtp.Hits.begin(), dtp.Hits.end(), iht) == dtp.Hits.end()) continue;
3567  dtp.Environment[kEnvNearMuon] = true;
3568  ++nnear;
3569  } // jpt
3570  } // iht
3571  } // ipt
3572  } // mutj
3573  } // SetTPEnvironment
3574 
3576  void UpdateTjChgProperties(std::string inFcnLabel, TCSlice const& slc, Trajectory& tj, bool prt)
3577  {
3578  // Updates properties of the tj that are affected when the TP environment
3579  // is changed. The most likely reason for a change is when the tj is attached to a
3580  // vertex in which case the Environment kEnvOverlap bit may be set by the UpdateVxEnvironment
3581  // function in which case this function is called.
3582  if (tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) return;
3583 
3584  // first (un)set some bits
3585  for (auto& tp : tj.Pts) {
3586  if (tp.Chg <= 0) continue;
3587  tp.Environment[kEnvUnusedHits] = false;
3588  for (unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
3589  if (tp.UseHit[ii]) continue;
3590  unsigned int iht = tp.Hits[ii];
3591  if (slc.slHits[iht].InTraj == 0) tp.Environment[kEnvUnusedHits] = true;
3592  } // ii
3593  } // tp
3594 
3595  // Update the tj charge variables. The concept is explained by this graphic where
3596  // each column is a wire, Q = a TP with charge, q = a TP with charge that is an
3597  // EnvOverlap region, x = a wire that has a TP with Chg = 0 or a wire that has no TP
3598  // because the wire is dead, o = an EnvOverlap region, V = vertex attached to end. You should
3599  // imagine that all 3 tjs come from the same vertex
3600  // 01234567890123456789 npwc cnt range
3601  // VooooQQQQxxxQQQ 7 7 0 - 14
3602  // VqqqqQQQQxxxQQQQQQQQ 16 12 0 - 19
3603  // VooQQQ 3 3 0 - 5
3604  // The average is first calculated using Ave = sum(Q) / npwc
3605  // TotChg is calculated using
3606  tj.TotChg = 0;
3607  tj.AveChg = 0;
3608  tj.ChgRMS = 0.5;
3609 
3610  // These variables are used to calculate the average and rms using valid points with charge
3611  double vcnt = 0;
3612  double vsum = 0;
3613  double vsum2 = 0;
3614  // Reject a single large charge TP
3615  float bigChg = 0;
3616  for (unsigned short ipt = tj.EndPt[0] + 1; ipt < tj.EndPt[1]; ++ipt) {
3617  auto& tp = tj.Pts[ipt];
3618  if (tp.Chg > bigChg) bigChg = tp.Chg;
3619  } // ipt
3620  // variables for calculating the backup quanties. These are only used if npwc < 3
3621  double bcnt = 0;
3622  double bsum = 0;
3623  double bsum2 = 0;
3624  // don't include the end points
3625  for (unsigned short ipt = tj.EndPt[0] + 1; ipt < tj.EndPt[1]; ++ipt) {
3626  auto& tp = tj.Pts[ipt];
3627  if (tp.Chg <= 0) continue;
3628  // ignore the single large charge TP
3629  if (tp.Chg == bigChg) continue;
3630  // accumulate a backup sum in case most of the points are overlapped. Note that
3631  // tp.Chg has an angle correction, which is why the hit integral is summed
3632  // below. We don't care about this detail for the backup sum
3633  bsum += tp.Chg;
3634  bsum2 += tp.Chg * tp.Chg;
3635  if (tp.Chg > bigChg) bigChg = tp.Chg;
3636  ++bcnt;
3637  // Skip TPs that overlap with TPs on other Tjs. A correction will be made below
3638  if (tj.Pts[ipt].Environment[kEnvOverlap]) continue;
3639  ++vcnt;
3640  double tpchg = 0;
3641  for (unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
3642  if (!tp.UseHit[ii]) continue;
3643  unsigned int iht = tp.Hits[ii];
3644  tpchg += (*evt.allHits)[slc.slHits[iht].allHitsIndex].Integral();
3645  } // ii
3646  vsum += tpchg;
3647  vsum2 += tpchg * tpchg;
3648  } // ipt
3649 
3650  if (bcnt == 0) return;
3651 
3652  if (vcnt < 3) {
3653  // use the backup sum
3654  tj.TotChg = bsum;
3655  tj.AveChg = bsum / bcnt;
3656  if (vcnt > 2) {
3657  double arg = bsum2 - bcnt * tj.AveChg * tj.AveChg;
3658  if (arg > 0) tj.ChgRMS = sqrt(arg / (bcnt - 1));
3659  }
3660  for (auto& tp : tj.Pts)
3661  tp.AveChg = tj.AveChg;
3662  if (prt)
3663  mf::LogVerbatim("TC") << inFcnLabel << ".UpdateTjChgProperties: backup sum Set tj.AveChg "
3664  << (int)tj.AveChg << " ChgRMS " << tj.ChgRMS;
3665  return;
3666  } // low npwc
3667 
3668  double nWires = tj.EndPt[1] - tj.EndPt[0] + 1;
3669  if (nWires < 2) return;
3670  // correct for wires missing near vertices.
3671  // Count the number of wires between vertices at the ends and the first wire
3672  // that has charge. This code assumes that there should be one TP on each wire
3673  if (!tj.AlgMod[kPhoton]) {
3674  for (unsigned short end = 0; end < 2; ++end) {
3675  if (tj.VtxID[end] == 0) continue;
3676  auto& tp = tj.Pts[tj.EndPt[end]];
3677  auto& vx2 = slc.vtxs[tj.VtxID[end] - 1];
3678  int dw = std::abs(tp.Pos[0] - vx2.Pos[0]);
3679  // This assumes that the vertex is not inside the wire boundaries of the tj
3680  nWires += dw;
3681  } // end
3682  } // not a photon Tj
3683 
3684  tj.AveChg = vsum / vcnt;
3685  // calculate the total charge using the tj wire range
3686  tj.TotChg = nWires * tj.AveChg;
3687  // calculate the rms
3688  double arg = vsum2 - vcnt * tj.AveChg * tj.AveChg;
3689  double rms = 0.5;
3690  if (arg > 0) rms = sqrt(arg / (vcnt - 1));
3691  rms /= tj.AveChg;
3692  // don't let it be an unrealistically low value. It could be crazy large however.
3693  if (rms < 0.1) rms = 0.1;
3694  // Don't let the calculated charge RMS dominate until it is well known; after there are 5 - 10 valid TPs.
3695  // Set the starting charge rms = 0.5
3696  if (vcnt < 10) {
3697  double defFrac = 1 / vcnt;
3698  rms = defFrac * 0.5 + (1 - defFrac) * rms;
3699  }
3700  tj.ChgRMS = rms;
3701  if (prt)
3702  mf::LogVerbatim("TC") << inFcnLabel << ".UpdateTjChgProperties: Set tj.AveChg "
3703  << (int)tj.AveChg << " ChgRMS " << tj.ChgRMS;
3704 
3705  // Update the TP charge pulls.
3706  // Don't let the calculated charge RMS dominate the default
3707  // RMS until it is well known. Start with 50% error on the
3708  // charge RMS
3709  for (unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
3710  auto& tp = tj.Pts[ipt];
3711  if (tp.Chg <= 0) continue;
3712  tp.ChgPull = (tp.Chg / tj.AveChg - 1) / tj.ChgRMS;
3713  } // ipt
3714 
3715  // update the local charge average using NPtsAve of the preceding points.
3716  // Handle short Tjs first.
3717  if (vcnt < tcc.nPtsAve) {
3718  for (auto& tp : tj.Pts)
3719  tp.AveChg = tj.AveChg;
3720  return;
3721  }
3722 
3723  // Set the local average to 0 first
3724  for (auto& tp : tj.Pts)
3725  tp.AveChg = 0;
3726  // Enter the local average on the points where an average can be calculated
3727  unsigned short nptsave = tcc.nPtsAve;
3728  unsigned short minPt = tj.EndPt[0] + nptsave;
3729  float lastAve = 0;
3730  for (unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
3731  unsigned short ipt = tj.EndPt[1] - ii;
3732  if (ipt < minPt) break;
3733  float cnt = 0;
3734  float sum = 0;
3735  for (unsigned short iii = 0; iii < nptsave; ++iii) {
3736  unsigned short iipt = ipt - iii;
3737  // Don't include the charge of the first point
3738  if (iipt == tj.EndPt[0]) break;
3739  auto& tp = tj.Pts[iipt];
3740  if (tp.Chg <= 0) continue;
3741  sum += tp.Chg;
3742  ++cnt;
3743  } // iii
3744  if (cnt > 2) {
3745  tj.Pts[ipt].AveChg = sum / cnt;
3746  lastAve = tj.Pts[ipt].AveChg;
3747  }
3748  } // ii
3749  // Fill in the points where no average was calculated
3750  for (unsigned short ii = tj.EndPt[0]; ii <= tj.EndPt[1]; ++ii) {
3751  unsigned short ipt = tj.EndPt[1] - ii;
3752  auto& tp = tj.Pts[ipt];
3753  if (tp.AveChg == 0) { tp.AveChg = lastAve; }
3754  else {
3755  lastAve = tp.AveChg;
3756  }
3757  } // ii
3758 
3759  tj.NeedsUpdate = false;
3760 
3761  } // UpdateTjChgProperties
3762 
3765  {
3766  // Set the kEnvOverlap bit true for all TPs that are close to other
3767  // trajectories that are close to vertices. The positions of TPs that
3768  // overlap are biased and shouldn't be used in a vertex fit. Also, these
3769  // TPs shouldn't be used to calculate dE/dx. The kEnvOverlap bit is first cleared
3770  // for ALL TPs and then set for ALL 2D vertices
3771 
3772  for (auto& tj : slc.tjs) {
3773  if (tj.AlgMod[kKilled]) continue;
3774  for (auto& tp : tj.Pts)
3775  tp.Environment[kEnvOverlap] = false;
3776  } // tj
3777 
3778  for (auto& vx : slc.vtxs) {
3779  if (vx.ID <= 0) continue;
3780  UpdateVxEnvironment(slc, vx, false);
3781  } // vx
3782 
3783  } // UpdateVxEnvironment
3784 
3786  void UpdateVxEnvironment(TCSlice& slc, VtxStore& vx2, bool prt)
3787  {
3788  // Update the Environment each TP on trajectories near the vertex
3789 
3790  if (vx2.ID == 0) return;
3791  if (vx2.Stat[kOnDeadWire]) return;
3792 
3793  if (prt) mf::LogVerbatim("TC") << "UpdateVxEnvironment check Tjs attached to vx2 " << vx2.ID;
3794 
3795  std::vector<int> tjlist;
3796  std::vector<unsigned short> tjends;
3797  if (vx2.Pos[0] < -0.4) return;
3798  unsigned int vxWire = std::nearbyint(vx2.Pos[0]);
3799  unsigned int loWire = vxWire;
3800  unsigned int hiWire = vxWire;
3801  for (auto& tj : slc.tjs) {
3802  if (tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) continue;
3803  if (tj.CTP != vx2.CTP) continue;
3804  // ignore photon Tjs
3805  if (tj.AlgMod[kPhoton]) continue;
3806  for (unsigned short end = 0; end < 2; ++end) {
3807  if (tj.VtxID[end] != vx2.ID) continue;
3808  tjlist.push_back(tj.ID);
3809  tjends.push_back(end);
3810  if (tj.Pts[tj.EndPt[end]].Pos[0] < -0.4) return;
3811  unsigned int endWire = std::nearbyint(tj.Pts[tj.EndPt[end]].Pos[0]);
3812  if (endWire < loWire) loWire = endWire;
3813  if (endWire > hiWire) hiWire = endWire;
3814  } // end
3815  } // tj
3816  if (tjlist.size() < 2) return;
3817  if (hiWire < loWire + 1) return;
3818  if (prt)
3819  mf::LogVerbatim("TC") << " check Tjs on wires in the range " << loWire << " to " << hiWire;
3820 
3821  // create a vector of TPs between loWire and hiWire for every tj in the list
3822  // wire TP
3823  std::vector<std::vector<TrajPoint>> wire_tjpt;
3824  // companion vector of IDs
3825  std::vector<int> tjids;
3826  // populate this vector with TPs on Tjs that are in this range
3827  unsigned short nwires = hiWire - loWire + 1;
3828  for (unsigned short itj = 0; itj < tjlist.size(); ++itj) {
3829  auto& tj = slc.tjs[tjlist[itj] - 1];
3830  unsigned short end = tjends[itj];
3831  std::vector<TrajPoint> tjpt(nwires);
3832  // first enter valid TPs in the range
3833  for (unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
3834  unsigned short ipt;
3835  if (end == 0) { ipt = tj.EndPt[0] + ii; }
3836  else {
3837  ipt = tj.EndPt[1] - ii;
3838  }
3839  if (ipt > tj.Pts.size() - 1) break;
3840  // Make a copy of the TP so we can alter it
3841  auto tp = tj.Pts[ipt];
3842  if (tp.Chg <= 0) continue;
3843  tp.Chg = 1;
3844  tp.Hits.clear();
3845  if (tp.Pos[0] < -0.4) continue;
3846  unsigned int wire = std::nearbyint(tp.Pos[0]);
3847  unsigned short indx = wire - loWire;
3848  if (indx > nwires - 1) break;
3849  tp.Step = ipt;
3850  // We will use NTPsFit to count the number of neighboring TPs
3851  tp.NTPsFit = 0;
3852  tjpt[indx] = tp;
3853  } // ii
3854  // next make TPs on the wires that don't have real TPs
3855  TrajPoint ltp;
3856  // put ltp at the vertex position with direction towards the end point
3857  MakeBareTrajPoint(vx2.Pos, tj.Pts[tj.EndPt[end]].Pos, ltp);
3858  if (ltp.Dir[0] == 0) continue;
3859  if (ltp.Pos[0] < -0.4) continue;
3860  unsigned int wire = std::nearbyint(ltp.Pos[0]);
3861  ltp.Chg = 0;
3862  unsigned short indx = wire - loWire;
3863  // Break if we found a real TP
3864  if (tjpt[indx].Chg == 0) tjpt[indx] = ltp;
3865  double stepSize = std::abs(1 / ltp.Dir[0]);
3866  for (unsigned short ii = 0; ii < nwires; ++ii) {
3867  // move the local TP position by one step in the right direction
3868  for (unsigned short iwt = 0; iwt < 2; ++iwt)
3869  ltp.Pos[iwt] += ltp.Dir[iwt] * stepSize;
3870  if (ltp.Pos[0] < -0.4) break;
3871  wire = std::nearbyint(ltp.Pos[0]);
3872  if (wire < loWire || wire > hiWire) break;
3873  indx = wire - loWire;
3874  if (tjpt[indx].Chg > 0) continue;
3875  tjpt[indx] = ltp;
3876  } // ii
3877  if (prt) {
3878  mf::LogVerbatim myprt("TC");
3879  myprt << " T" << tj.ID;
3880  for (auto& tp : tjpt)
3881  myprt << " " << PrintPos(tp.Pos) << "_" << tp.Step << "_" << (int)tp.Chg;
3882  }
3883  wire_tjpt.push_back(tjpt);
3884  tjids.push_back(tj.ID);
3885  } // itj
3886 
3887  // iterate over the wires in the range
3888  for (unsigned short indx = 0; indx < nwires; ++indx) {
3889  // count the number of valid points on this wire
3890  unsigned short npts = 0;
3891  // count the number of points on this wire that have charge
3892  unsigned short npwc = 0;
3893  for (unsigned short itj = 0; itj < wire_tjpt.size(); ++itj) {
3894  if (wire_tjpt[itj][indx].Pos[0] == 0) continue;
3895  // found a valid point
3896  ++npts;
3897  if (wire_tjpt[itj][indx].Chg > 0) ++npwc;
3898  } // itj
3899  // no valid points
3900  if (npts == 0) continue;
3901  // all valid points have charge
3902  if (npwc == npts) continue;
3903  // re-find the valid points with charge and set the kEnvOverlap bit
3904  for (unsigned short itj = 0; itj < wire_tjpt.size(); ++itj) {
3905  if (wire_tjpt[itj][indx].Pos[0] == 0) continue;
3906  if (wire_tjpt[itj][indx].Chg == 0) continue;
3907  auto& tj = slc.tjs[tjids[itj] - 1];
3908  unsigned short ipt = wire_tjpt[itj][indx].Step;
3909  tj.Pts[ipt].Environment[kEnvOverlap] = true;
3910  tj.NeedsUpdate = true;
3911  if (prt) mf::LogVerbatim("TC") << " Set kEnvOverlap bit on T" << tj.ID << " ipt " << ipt;
3912  } // itj
3913  } // indx
3914 
3915  // update the charge rms for those tjs whose environment was changed above
3916  // (or elsewhere)
3917  for (auto tjid : tjids) {
3918  auto& tj = slc.tjs[tjid - 1];
3919  if (!tj.NeedsUpdate) continue;
3920  if (tj.CTP != vx2.CTP) continue;
3921  UpdateTjChgProperties("UVxE", slc, tj, prt);
3922  } // tjid
3923 
3924  } // UpdateVxEnvironment
3925 
3928  const Point3_t& pos,
3929  CTP_t inCTP)
3930  {
3931  // A version to use when the 2D direction isn't required
3932  TrajPoint tp;
3933  tp.Pos = {{0, 0}};
3934  tp.Dir = {{0, 1}};
3935  tp.CTP = inCTP;
3936  geo::PlaneID planeID = DecodeCTP(inCTP);
3937 
3938  tp.Pos[0] = tcc.geom->WireCoordinate(geo::Point_t{0, pos[1], pos[2]}, planeID);
3939  tp.Pos[1] = detProp.ConvertXToTicks(pos[0], planeID) * tcc.unitsPerTick;
3940  return tp;
3941  } // MakeBareTP
3942 
3945  const Point3_t& pos,
3946  const Vector3_t& dir,
3947  CTP_t inCTP)
3948  {
3949  // Projects the space point defined by pos and dir into the CTP and returns
3950  // it in the form of a trajectory point. The TP Pos[0] is set to a negative
3951  // number if the point has an invalid wire position but doesn't return an
3952  // error if the position is on a dead wire. The projection of the direction
3953  // vector in CTP is stored in tp.Delta.
3954  TrajPoint tp;
3955  tp.Pos = {{-1, 0}};
3956  tp.Dir = {{0, 1}};
3957  tp.CTP = inCTP;
3958  geo::PlaneID planeID = DecodeCTP(inCTP);
3959 
3960  tp.Pos[0] = tcc.geom->WireCoordinate(geo::Point_t{0, pos[1], pos[2]}, planeID);
3961  tp.Pos[1] = detProp.ConvertXToTicks(pos[0], planeID) * tcc.unitsPerTick;
3962 
3963  // now find the direction if dir is defined
3964  if (dir[0] == 0 && dir[1] == 0 && dir[2] == 0) return tp;
3965 
3966  // Make a point at the origin and one 100 units away
3967  Point3_t ori3 = {{0.0, 0.0, 0.0}};
3968  Point3_t pos3 = {{100 * dir[0], 100 * dir[1], 100 * dir[2]}};
3969  // 2D position of ori3 and the pos3 projection
3970  std::array<double, 2> ori2;
3971  std::array<double, 2> pos2;
3972  std::array<double, 2> dir2;
3973  // the wire coordinates
3974  ori2[0] = tcc.geom->WireCoordinate(geo::Point_t{0, ori3[1], ori3[2]}, planeID);
3975  pos2[0] = tcc.geom->WireCoordinate(geo::Point_t{0, pos3[1], pos3[2]}, planeID);
3976  // the time coordinates
3977  ori2[1] = detProp.ConvertXToTicks(ori3[0], planeID) * tcc.unitsPerTick;
3978  pos2[1] = detProp.ConvertXToTicks(pos3[0], planeID) * tcc.unitsPerTick;
3979 
3980  dir2[0] = pos2[0] - ori2[0];
3981  dir2[1] = pos2[1] - ori2[1];
3982 
3983  double norm = sqrt(dir2[0] * dir2[0] + dir2[1] * dir2[1]);
3984  tp.Dir[0] = dir2[0] / norm;
3985  tp.Dir[1] = dir2[1] / norm;
3986  tp.Ang = atan2(dir2[1], dir2[0]);
3987  tp.Delta = norm / 100;
3988 
3989  // The Orth vectors are not unit normalized so we need to correct for this
3990  double w0 = tcc.geom->WireCoordinate(geo::Point_t{0, 0, 0}, planeID);
3991  // cosine-like component
3992  double cs = tcc.geom->WireCoordinate(geo::Point_t{0, 1, 0}, planeID) - w0;
3993  // sine-like component
3994  double sn = tcc.geom->WireCoordinate(geo::Point_t{0, 0, 1}, planeID) - w0;
3995  norm = sqrt(cs * cs + sn * sn);
3996  tp.Delta /= norm;
3997 
3998  // Stasb dt/dWire in DeltaRMS. This is used in PFPUtils/FitSection to find the
3999  // distance along a 3D line given the wire number in a plane
4000  tp.DeltaRMS = 100 / (pos2[0] - ori2[0]);
4001  return tp;
4002 
4003  } // MakeBareTP
4004 
4006  bool MakeBareTrajPoint(const TCSlice& slc,
4007  unsigned int fromHit,
4008  unsigned int toHit,
4009  TrajPoint& tp)
4010  {
4011  if (fromHit > slc.slHits.size() - 1) return false;
4012  if (toHit > slc.slHits.size() - 1) return false;
4013  auto& fhit = (*evt.allHits)[slc.slHits[fromHit].allHitsIndex];
4014  auto& thit = (*evt.allHits)[slc.slHits[toHit].allHitsIndex];
4015  CTP_t tCTP = EncodeCTP(fhit.WireID());
4016  return MakeBareTrajPoint((float)fhit.WireID().Wire,
4017  fhit.PeakTime(),
4018  (float)thit.WireID().Wire,
4019  thit.PeakTime(),
4020  tCTP,
4021  tp);
4022 
4023  } // MakeBareTrajPoint
4024 
4026  bool MakeBareTrajPoint(float fromWire,
4027  float fromTick,
4028  float toWire,
4029  float toTick,
4030  CTP_t tCTP,
4031  TrajPoint& tp)
4032  {
4033  tp.CTP = tCTP;
4034  tp.Pos[0] = fromWire;
4035  tp.Pos[1] = tcc.unitsPerTick * fromTick;
4036  tp.Dir[0] = toWire - fromWire;
4037  tp.Dir[1] = tcc.unitsPerTick * (toTick - fromTick);
4038  double norm = sqrt(tp.Dir[0] * tp.Dir[0] + tp.Dir[1] * tp.Dir[1]);
4039  if (norm == 0) return false;
4040  tp.Dir[0] /= norm;
4041  tp.Dir[1] /= norm;
4042  tp.Ang = atan2(tp.Dir[1], tp.Dir[0]);
4043  return true;
4044  } // MakeBareTrajPoint
4045 
4047  bool MakeBareTrajPoint(const Point2_t& fromPos, const Point2_t& toPos, TrajPoint& tpOut)
4048  {
4049  tpOut.Pos = fromPos;
4050  tpOut.Dir = PointDirection(fromPos, toPos);
4051  tpOut.Ang = atan2(tpOut.Dir[1], tpOut.Dir[0]);
4052  return true;
4053 
4054  } // MakeBareTrajPoint
4055 
4057  bool MakeBareTrajPoint(const TrajPoint& tpIn1, const TrajPoint& tpIn2, TrajPoint& tpOut)
4058  {
4059  tpOut.CTP = tpIn1.CTP;
4060  tpOut.Pos = tpIn1.Pos;
4061  tpOut.Dir = PointDirection(tpIn1.Pos, tpIn2.Pos);
4062  tpOut.Ang = atan2(tpOut.Dir[1], tpOut.Dir[0]);
4063  return true;
4064  } // MakeBareTrajPoint
4065 
4067  unsigned short FarEnd(const Trajectory& tj, const Point2_t& pos)
4068  {
4069  // Returns the end (0 or 1) of the Tj that is furthest away from the position pos
4070  if (tj.ID == 0) return 0;
4071  if (PosSep2(tj.Pts[tj.EndPt[1]].Pos, pos) > PosSep2(tj.Pts[tj.EndPt[0]].Pos, pos)) return 1;
4072  return 0;
4073  } // FarEnd
4074 
4077  {
4078  // Finds the direction vector between the two points from p1 to p2
4079  Vector2_t dir;
4080  for (unsigned short xyz = 0; xyz < 2; ++xyz)
4081  dir[xyz] = p2[xyz] - p1[xyz];
4082  if (dir[0] == 0 && dir[1] == 0) return dir;
4083  double norm = sqrt(dir[0] * dir[0] + dir[1] * dir[1]);
4084  dir[0] /= norm;
4085  dir[1] /= norm;
4086  return dir;
4087  } // PointDirection
4088 
4090  float TPHitsRMSTime(const TCSlice& slc, const TrajPoint& tp, HitStatus_t hitRequest)
4091  {
4092  return tcc.unitsPerTick * TPHitsRMSTick(slc, tp, hitRequest);
4093  } // TPHitsRMSTime
4094 
4096  float TPHitsRMSTick(const TCSlice& slc, const TrajPoint& tp, HitStatus_t hitRequest)
4097  {
4098  // Estimate the RMS of all hits associated with a trajectory point
4099  // without a lot of calculation. Note that this returns a value that is
4100  // closer to a FWHM, not the RMS
4101  if (tp.Hits.empty()) return 0;
4102  float minVal = 9999;
4103  float maxVal = 0;
4104  for (unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
4105  bool useit = (hitRequest == kAllHits);
4106  if (hitRequest == kUsedHits && tp.UseHit[ii]) useit = true;
4107  if (hitRequest == kUnusedHits && !tp.UseHit[ii]) useit = true;
4108  if (!useit) continue;
4109  unsigned int iht = tp.Hits[ii];
4110  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
4111  float cv = hit.PeakTime();
4112  float rms = hit.RMS();
4113  float arg = cv - rms;
4114  if (arg < minVal) minVal = arg;
4115  arg = cv + rms;
4116  if (arg > maxVal) maxVal = arg;
4117  } // ii
4118  if (maxVal == 0) return 0;
4119  return (maxVal - minVal) / 2;
4120  } // TPHitsRMSTick
4121 
4123  float HitsRMSTime(const TCSlice& slc,
4124  const std::vector<unsigned int>& hitsInMultiplet,
4125  HitStatus_t hitRequest)
4126  {
4127  return tcc.unitsPerTick * HitsRMSTick(slc, hitsInMultiplet, hitRequest);
4128  } // HitsRMSTick
4129 
4131  float HitsRMSTick(const TCSlice& slc,
4132  const std::vector<unsigned int>& hitsInMultiplet,
4133  HitStatus_t hitRequest)
4134  {
4135  if (hitsInMultiplet.empty()) return 0;
4136 
4137  if (hitsInMultiplet.size() == 1) {
4138  auto& hit = (*evt.allHits)[slc.slHits[hitsInMultiplet[0]].allHitsIndex];
4139  return hit.RMS();
4140  }
4141 
4142  float minVal = 9999;
4143  float maxVal = 0;
4144  for (unsigned short ii = 0; ii < hitsInMultiplet.size(); ++ii) {
4145  unsigned int iht = hitsInMultiplet[ii];
4146  bool useit = (hitRequest == kAllHits);
4147  if (hitRequest == kUsedHits && slc.slHits[iht].InTraj > 0) useit = true;
4148  if (hitRequest == kUnusedHits && slc.slHits[iht].InTraj == 0) useit = true;
4149  if (!useit) continue;
4150  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
4151  float cv = hit.PeakTime();
4152  float rms = hit.RMS();
4153  float arg = cv - rms;
4154  if (arg < minVal) minVal = arg;
4155  arg = cv + rms;
4156  if (arg > maxVal) maxVal = arg;
4157  } // ii
4158  if (maxVal == 0) return 0;
4159  return (maxVal - minVal) / 2;
4160  } // HitsRMSTick
4161 
4163  float HitsPosTime(const TCSlice& slc,
4164  const std::vector<unsigned int>& hitsInMultiplet,
4165  float& sum,
4166  HitStatus_t hitRequest)
4167  {
4168  return tcc.unitsPerTick * HitsPosTick(slc, hitsInMultiplet, sum, hitRequest);
4169  } // HitsPosTime
4170 
4172  float HitsPosTick(const TCSlice& slc,
4173  const std::vector<unsigned int>& hitsInMultiplet,
4174  float& sum,
4175  HitStatus_t hitRequest)
4176  {
4177  // returns the position and the charge
4178  float pos = 0;
4179  sum = 0;
4180  for (unsigned short ii = 0; ii < hitsInMultiplet.size(); ++ii) {
4181  unsigned int iht = hitsInMultiplet[ii];
4182  bool useit = (hitRequest == kAllHits);
4183  if (hitRequest == kUsedHits && slc.slHits[iht].InTraj > 0) useit = true;
4184  if (hitRequest == kUnusedHits && slc.slHits[iht].InTraj == 0) useit = true;
4185  if (!useit) continue;
4186  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
4187  float chg = hit.Integral();
4188  pos += chg * hit.PeakTime();
4189  sum += chg;
4190  } // ii
4191  if (sum <= 0) return -1;
4192  return pos / sum;
4193  } // HitsPosTick
4194 
4196  unsigned short NumHitsInTP(const TrajPoint& tp, HitStatus_t hitRequest)
4197  {
4198  // Counts the number of hits of the specified type in tp
4199  if (tp.Hits.empty()) return 0;
4200 
4201  if (hitRequest == kAllHits) return tp.Hits.size();
4202 
4203  unsigned short nhits = 0;
4204  for (unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
4205  if (hitRequest == kUsedHits) {
4206  if (tp.UseHit[ii]) ++nhits;
4207  }
4208  else {
4209  // looking for unused hits
4210  if (!tp.UseHit[ii]) ++nhits;
4211  }
4212  } // ii
4213  return nhits;
4214  } // NumHitsInTP
4215 
4217  void SetPDGCode(TCSlice& slc, unsigned short itj)
4218  {
4219  if (itj > slc.tjs.size() - 1) return;
4220  SetPDGCode(slc, slc.tjs[itj]);
4221  }
4222 
4224  void SetPDGCode(TCSlice const& slc, Trajectory& tj)
4225  {
4226  // Sets the PDG code for the supplied trajectory. Note that the existing
4227  // PDG code is left unchanged if these cuts are not met
4228 
4229  short npwc = NumPtsWithCharge(slc, tj, false);
4230  if (npwc < 6) {
4231  tj.PDGCode = 0;
4232  return;
4233  }
4234 
4235  if (tj.Strategy[kStiffEl] && ElectronLikelihood(slc, tj) > tcc.showerTag[6]) {
4236  tj.PDGCode = 111;
4237  return;
4238  }
4239  if (tj.Strategy[kStiffMu]) {
4240  tj.PDGCode = 13;
4241  return;
4242  }
4243 
4244  if (tcc.showerTag[6] > 0 && ElectronLikelihood(slc, tj) > tcc.showerTag[6]) {
4245  tj.PDGCode = 11;
4246  return;
4247  }
4248 
4249  if (tcc.muonTag[0] <= 0) return;
4250  // Special handling of very long straight trajectories, e.g. uB cosmic rays
4251  bool isAMuon = (npwc > (unsigned short)tcc.muonTag[0] && tj.MCSMom > tcc.muonTag[1]);
4252  // anything really really long must be a muon
4253  if (npwc > 500) isAMuon = true;
4254  if (isAMuon) tj.PDGCode = 13;
4255 
4256  } // SetPDGCode
4257 
4260  {
4261  // Find the average hit rms by analyzing the full hit collection. This
4262  // only needs to be done once per job.
4263 
4264  if ((*evt.allHits).empty()) return true;
4265  // no sense re-calculating it if it's been done
4266  if (evt.aveHitRMSValid) return true;
4267 
4268  auto const& wireid = (*evt.allHits)[0].WireID();
4269 
4270  unsigned short nplanes = tcc.geom->Nplanes(wireid.asPlaneID());
4271  evt.aveHitRMS.resize(nplanes);
4272  std::vector<float> cnt(nplanes, 0);
4273  for (unsigned short iht = 0; iht < (*evt.allHits).size(); ++iht) {
4274  auto& hit = (*evt.allHits)[iht];
4275  unsigned short plane = hit.WireID().Plane;
4276  if (plane > nplanes - 1) return false;
4277  if (cnt[plane] > 200) continue;
4278  // require multiplicity one
4279  if (hit.Multiplicity() != 1) continue;
4280  // not-crazy Chisq/DOF
4281  if (hit.GoodnessOfFit() < 0 || hit.GoodnessOfFit() > 500) continue;
4282  // don't let a lot of runt hits screw up the calculation
4283  if (hit.PeakAmplitude() < 1) continue;
4284  evt.aveHitRMS[plane] += hit.RMS();
4285  ++cnt[plane];
4286  // quit if enough hits are found
4287  bool allDone = true;
4288  for (unsigned short plane = 0; plane < nplanes; ++plane)
4289  if (cnt[plane] < 200) allDone = false;
4290  if (allDone) break;
4291  } // iht
4292 
4293  // assume there are enough hits in each plane
4294  evt.aveHitRMSValid = true;
4295  for (unsigned short plane = 0; plane < nplanes; ++plane) {
4296  if (cnt[plane] > 4) { evt.aveHitRMS[plane] /= cnt[plane]; }
4297  else {
4298  evt.aveHitRMS[plane] = 10;
4299  evt.aveHitRMSValid = false;
4300  } // cnt too low
4301  } // plane
4302 
4303  if (tcc.modes[kDebug]) {
4304  std::cout << "Analyze hits aveHitRMS";
4305  std::cout << std::fixed << std::setprecision(1);
4306  for (auto rms : evt.aveHitRMS)
4307  std::cout << " " << rms;
4308  std::cout << " aveHitRMSValid? " << evt.aveHitRMSValid << "\n";
4309  }
4310 
4311  return true;
4312  } // Analyze hits
4313 
4316  {
4317  // return true if the hit is in a long pulse indicating that it's position
4318  // and charge are not well known
4319  return ((hit.GoodnessOfFit() < 0 || hit.GoodnessOfFit() > 50) && hit.Multiplicity() > 5);
4320  }
4321 
4324  {
4325  // Defines the local vector of dead wires and the low-high range of hits in each wire in
4326  // the TPCID in TCEvent. Note that there is no requirement that the allHits collection is sorted. Care should
4327  // be taken when looping over hits using this range - see SignalAtTp
4328 
4329  // see if this function was called in the current TPCID. There is nothing that needs to
4330  // be done if that is the case
4331  if (inTPCID == evt.TPCID) return;
4332 
4333  evt.TPCID = inTPCID;
4334  unsigned short nplanes = tcc.geom->Nplanes(inTPCID);
4335  if (tcc.useChannelStatus) {
4336  lariov::ChannelStatusProvider const& channelStatus =
4338  evt.goodWire.resize(nplanes);
4339  for (auto const& id : tcc.geom->Iterate<geo::PlaneID>(inTPCID)) {
4340  unsigned int nwires = tcc.geom->Nwires(id);
4341  // set all wires dead
4342  evt.goodWire[id.Plane].resize(nwires, false);
4343  for (unsigned int wire = 0; wire < nwires; ++wire) {
4345  evt.goodWire[id.Plane][wire] = channelStatus.IsGood(chan);
4346  } // wire
4347  } // pln
4348  }
4349  else {
4350  // resize and set every channel good
4351  evt.goodWire.resize(nplanes);
4352  for (auto const& id : tcc.geom->Iterate<geo::PlaneID>(inTPCID)) {
4353  unsigned int nwires = tcc.geom->Nwires(id);
4354  evt.goodWire[id.Plane].resize(nwires, true);
4355  } // pln
4356  } // don't use channelStatus
4357 
4358  // there is no need to define evt.wireHitRange if the hit collection is not sliced. The function
4359  // SignalAtTP will then use the (smaller) slc.WireHitRange instead of evt.wireHitRange
4360  if (!evt.expectSlicedHits) return;
4361 
4362  // define the size of evt.wireHitRange
4363  evt.wireHitRange.resize(nplanes);
4364  for (auto const& id : tcc.geom->Iterate<geo::PlaneID>(inTPCID)) {
4365  unsigned int nwires = tcc.geom->Nwires(id);
4366  evt.wireHitRange[id.Plane].resize(nwires);
4367  for (unsigned int wire = 0; wire < nwires; ++wire)
4368  evt.wireHitRange[id.Plane][wire] = {UINT_MAX, UINT_MAX};
4369  } // pln
4370 
4371  // next define the wireHitRange values. Make one loop through the allHits collection
4372  unsigned int nBadWireFix = 0;
4373  for (unsigned int iht = 0; iht < (*evt.allHits).size(); ++iht) {
4374  auto& hit = (*evt.allHits)[iht];
4375  auto const& wid = hit.WireID();
4376 
4377  if (static_cast<geo::TPCID const&>(wid) != inTPCID) continue;
4378  unsigned short pln = wid.Plane;
4379  unsigned int wire = wid.Wire;
4380  // Check the goodWire status and correct it if it's wrong
4381  if (!evt.goodWire[pln][wire]) {
4382  evt.goodWire[pln][wire] = true;
4383  ++nBadWireFix;
4384  } // not goodWire
4385  if (evt.wireHitRange[pln][wire].first == UINT_MAX) evt.wireHitRange[pln][wire].first = iht;
4386  evt.wireHitRange[pln][wire].second = iht;
4387  } // iht
4388  if (nBadWireFix > 0 && tcc.modes[kDebug]) {
4389  std::cout << "FillWireHitRange found hits on " << nBadWireFix
4390  << " wires that were declared not-good by the ChannelStatus service. Fixed it...\n";
4391  }
4392  } // FillWireHitRange
4393 
4396  detinfo::DetectorPropertiesData const& detProp,
4397  TCSlice& slc)
4398  {
4399  // fills the WireHitRange vector. Slightly modified version of the one in ClusterCrawlerAlg.
4400  // Returns false if there was a serious error
4401 
4402  // determine the number of planes
4403  unsigned int cstat = slc.TPCID.Cryostat;
4404  unsigned int tpc = slc.TPCID.TPC;
4405  auto const& tpcgeom = tcc.geom->TPC(slc.TPCID);
4406  unsigned short nplanes = tpcgeom.Nplanes();
4407  slc.nPlanes = nplanes;
4408  if (nplanes > 3) return false;
4409 
4410  // Y,Z limits of the detector
4411  auto const world = tpcgeom.GetCenter();
4412 
4413  // reduce the active area of the TPC by 1 cm to prevent wire boundary issues
4414  slc.xLo = world.X() - tpcgeom.HalfWidth() + 1;
4415  slc.xHi = world.X() + tpcgeom.HalfWidth() - 1;
4416  slc.yLo = world.Y() - tpcgeom.HalfHeight() + 1;
4417  slc.yHi = world.Y() + tpcgeom.HalfHeight() - 1;
4418  slc.zLo = world.Z() - tpcgeom.Length() / 2 + 1;
4419  slc.zHi = world.Z() + tpcgeom.Length() / 2 - 1;
4420 
4421  // initialize everything
4422  slc.wireHitRange.resize(nplanes);
4423  slc.firstWire.resize(nplanes);
4424  slc.lastWire.resize(nplanes);
4425  slc.nWires.resize(nplanes);
4426  tcc.maxPos0.resize(nplanes);
4427  tcc.maxPos1.resize(nplanes);
4428  evt.aveHitRMS.resize(nplanes, nplanes);
4429 
4430  std::pair<unsigned int, unsigned int> flag{UINT_MAX, UINT_MAX};
4431 
4432  // Calculate tcc.unitsPerTick, the scale factor to convert a tick into
4433  // Wire Spacing Equivalent (WSE) units where the wire spacing in this plane = 1.
4434  // Strictly speaking this factor should be calculated for each plane to handle the
4435  // case where the wire spacing is different in each plane. Deal with this later if
4436  // the approximation used here fails.
4437 
4438  geo::PlaneID const plane_0{tpcgeom.ID(), 0};
4439  raw::ChannelID_t channel = tcc.geom->PlaneWireToChannel(geo::WireID{plane_0, 0});
4440  tcc.wirePitch = tcc.geom->WirePitch(tcc.geom->View(channel));
4441  float tickToDist = detProp.DriftVelocity(detProp.Efield(), detProp.Temperature());
4442  tickToDist *= 1.e-3 * sampling_rate(clockData); // 1e-3 is conversion of 1/us to 1/ns
4443  tcc.unitsPerTick = tickToDist / tcc.wirePitch;
4444  for (unsigned short plane = 0; plane < nplanes; ++plane) {
4445  slc.firstWire[plane] = UINT_MAX;
4446  slc.lastWire[plane] = 0;
4447  slc.nWires[plane] = tcc.geom->Nwires(geo::PlaneID{tpcgeom.ID(), plane});
4448  slc.wireHitRange[plane].resize(slc.nWires[plane], flag);
4449  tcc.maxPos0[plane] = (float)slc.nWires[plane] - 0.5;
4450  tcc.maxPos1[plane] = (float)detProp.NumberTimeSamples() * tcc.unitsPerTick;
4451  }
4452 
4453  unsigned int lastWire = 0, lastPlane = 0;
4454  for (unsigned int iht = 0; iht < slc.slHits.size(); ++iht) {
4455  unsigned int ahi = slc.slHits[iht].allHitsIndex;
4456  if (ahi > (*evt.allHits).size() - 1) return false;
4457  auto& hit = (*evt.allHits)[ahi];
4458  if (hit.WireID().Cryostat != cstat) continue;
4459  if (hit.WireID().TPC != tpc) continue;
4460  unsigned short plane = hit.WireID().Plane;
4461  unsigned int wire = hit.WireID().Wire;
4462  if (wire > slc.nWires[plane] - 1) {
4463  mf::LogWarning("TC") << "FillWireHitRange: Invalid wire number " << wire << " > "
4464  << slc.nWires[plane] - 1 << " in plane " << plane << " Quitting";
4465  return false;
4466  } // too large wire number
4467  if (plane == lastPlane && wire < lastWire) {
4468  mf::LogWarning("TC")
4469  << "FillWireHitRange: Hits are not in increasing wire order. Quitting ";
4470  return false;
4471  } // hits out of order
4472  lastWire = wire;
4473  lastPlane = plane;
4474  if (slc.firstWire[plane] == UINT_MAX) slc.firstWire[plane] = wire;
4475  if (slc.wireHitRange[plane][wire].first == UINT_MAX)
4476  slc.wireHitRange[plane][wire].first = iht;
4477  slc.wireHitRange[plane][wire].second = iht;
4478  slc.lastWire[plane] = wire + 1;
4479  } // iht
4480  // check
4481  unsigned int slhitsSize = slc.slHits.size();
4482  for (unsigned short plane = 0; plane < nplanes; ++plane) {
4483  for (unsigned int wire = slc.firstWire[plane]; wire < slc.lastWire[plane]; ++wire) {
4484  if (slc.wireHitRange[plane][wire].first == UINT_MAX) continue;
4485  if (slc.wireHitRange[plane][wire].first > slhitsSize - 1 &&
4486  slc.wireHitRange[plane][wire].second > slhitsSize)
4487  return false;
4488  } // wire
4489  } // plane
4490 
4491  // Find the average multiplicity 1 hit RMS and calculate the expected max RMS for each range
4492  if (tcc.modes[kDebug] && (int)tpc == debug.TPC) {
4493  // Note that this function is called before the slice is pushed into slices so the index
4494  // isn't decremented by 1
4495  std::cout << "Slice ID/Index " << slc.ID << "/" << slices.size() << " tpc " << tpc
4496  << " tcc.unitsPerTick " << std::setprecision(3) << tcc.unitsPerTick;
4497  std::cout << " Active volume (";
4498  std::cout << std::fixed << std::setprecision(1) << slc.xLo << " < X < " << slc.xHi << ") (";
4499  std::cout << std::fixed << std::setprecision(1) << slc.yLo << " < Y < " << slc.yHi << ") (";
4500  std::cout << std::fixed << std::setprecision(1) << slc.zLo << " < Z < " << slc.zHi << ")\n";
4501  }
4502 
4503  return true;
4504 
4505  } // FillWireHitRange
4506 
4508  bool WireHitRangeOK(TCSlice const& slc, const CTP_t& inCTP)
4509  {
4510  // returns true if the passed CTP code is consistent with the CT code of the WireHitRangeVector
4511  geo::PlaneID planeID = DecodeCTP(inCTP);
4512  if (planeID.Cryostat != slc.TPCID.Cryostat) return false;
4513  if (planeID.TPC != slc.TPCID.TPC) return false;
4514  return true;
4515  }
4516 
4518  bool MergeAndStore(TCSlice& slc, unsigned int itj1, unsigned int itj2, bool doPrt)
4519  {
4520  // Merge the two trajectories in allTraj and store them. Returns true if it was successfull.
4521  // Merging is done between the end (end = 1) of tj1 and the beginning (end = 0) of tj2. This function preserves the
4522  // AlgMod state of itj1.
4523  // The itj1 -> itj2 merge order is reversed if end1 of itj2 is closer to end0 of itj1
4524 
4525  if (itj1 > slc.tjs.size() - 1) return false;
4526  if (itj2 > slc.tjs.size() - 1) return false;
4527  if (slc.tjs[itj1].AlgMod[kKilled] || slc.tjs[itj2].AlgMod[kKilled]) return false;
4528  if (slc.tjs[itj1].AlgMod[kHaloTj] || slc.tjs[itj2].AlgMod[kHaloTj]) return false;
4529 
4530  // Merging shower Tjs requires merging the showers as well.
4531  if (slc.tjs[itj1].AlgMod[kShowerTj] || slc.tjs[itj2].AlgMod[kShowerTj])
4532  return MergeShowerTjsAndStore(slc, itj1, itj2, doPrt);
4533 
4534  // Ensure that the order of 3D-matched Tjs is consistent with the convention that
4535  unsigned short pfp1 = GetPFPIndex(slc, slc.tjs[itj1].ID);
4536  unsigned short pfp2 = GetPFPIndex(slc, slc.tjs[itj2].ID);
4537  if (pfp1 != USHRT_MAX || pfp2 != USHRT_MAX) {
4538  if (pfp1 != USHRT_MAX && pfp2 != USHRT_MAX) return false;
4539  // Swap so that the order of tj1 is preserved. Tj2 may be reversed to be consistent
4540  if (pfp1 == USHRT_MAX) std::swap(itj1, itj2);
4541  } // one or both used in a PFParticle
4542 
4543  // make copies so they can be trimmed as needed
4544  Trajectory tj1 = slc.tjs[itj1];
4545  Trajectory tj2 = slc.tjs[itj2];
4546 
4547  // ensure that these are in the same step order
4548  if (tj2.StepDir != tj1.StepDir) ReverseTraj(tj2);
4549 
4550  Point2_t tp1e0 = tj1.Pts[tj1.EndPt[0]].Pos;
4551  Point2_t tp1e1 = tj1.Pts[tj1.EndPt[1]].Pos;
4552  Point2_t tp2e0 = tj2.Pts[tj2.EndPt[0]].Pos;
4553  Point2_t tp2e1 = tj2.Pts[tj2.EndPt[1]].Pos;
4554 
4555  if (doPrt) {
4556  mf::LogVerbatim("TC") << "MergeAndStore: T" << tj1.ID << " and T" << tj2.ID
4557  << " at merge points " << PrintPos(tp1e1) << " " << PrintPos(tp2e0);
4558  }
4559 
4560  // swap the order so that abs(tj1end1 - tj2end0) is less than abs(tj2end1 - tj1end0)
4561  if (PosSep2(tp1e1, tp2e0) > PosSep2(tp2e1, tp1e0)) {
4562  std::swap(tj1, tj2);
4563  std::swap(tp1e0, tp2e0);
4564  std::swap(tp1e1, tp2e1);
4565  if (doPrt)
4566  mf::LogVerbatim("TC") << " swapped the order. Merge points " << PrintPos(tp1e1) << " "
4567  << PrintPos(tp2e0);
4568  }
4569 
4570  // Here is what we are looking for, where - indicates a TP with charge.
4571  // Note that this graphic is in the stepping direction (+1 = +wire direction)
4572  // tj1: 0------------1
4573  // tj2: 0-----------1
4574  // Another possibility with overlap
4575  // tj1: 0-------------1
4576  // tj2: 0--------------1
4577 
4578  if (tj1.StepDir > 1) {
4579  // Not allowed
4580  // tj1: 0---------------------------1
4581  // tj2: 0------1
4582  if (tp2e0[0] > tp1e0[0] && tp2e1[0] < tp1e1[0]) return false;
4584  // tj1: 0------1
4585  // tj2: 0---------------------------1
4586  if (tp1e0[0] > tp2e0[0] && tp1e1[0] < tp2e1[0]) return false;
4587  }
4588  else {
4589  // same as above but with ends reversed
4590  if (tp2e1[0] > tp1e1[0] && tp2e0[0] < tp1e0[0]) return false;
4591  if (tp1e1[0] > tp2e1[0] && tp1e0[0] < tp2e0[0]) return false;
4592  }
4593 
4594  if (tj1.VtxID[1] > 0 && tj2.VtxID[0] == tj1.VtxID[1]) {
4595  auto& vx = slc.vtxs[tj1.VtxID[1] - 1];
4596  if (!MakeVertexObsolete("MAS", slc, vx, false)) {
4597  if (doPrt)
4598  mf::LogVerbatim("TC") << "MergeAndStore: Found a good vertex between Tjs " << tj1.VtxID[1]
4599  << " No merging";
4600  return false;
4601  }
4602  }
4603 
4604  if (tj1.EndFlag[1][kBragg]) {
4605  if (doPrt)
4606  mf::LogVerbatim("TC") << "MergeAndStore: You are merging the end of trajectory T" << tj1.ID
4607  << " with a Bragg peak. Not merging\n";
4608  return false;
4609  }
4610 
4611  // remove any points at the end of tj1 that don't have used hits
4612  tj1.Pts.resize(tj1.EndPt[1] + 1);
4613 
4614  // determine if they overlap by finding the point on tj2 that is closest
4615  // to the end point of tj1.
4616  TrajPoint& endtj1TP = tj1.Pts[tj1.EndPt[1]];
4617  // Set minSep large so that dead wire regions are accounted for
4618  float minSep = 1000;
4619  unsigned short tj2ClosePt = 0;
4620  // Note that TrajPointTrajDOCA only considers TPs that have charge
4621  TrajPointTrajDOCA(endtj1TP, tj2, tj2ClosePt, minSep);
4622  if (doPrt)
4623  mf::LogVerbatim("TC") << " Merge point tj1 " << PrintPos(endtj1TP) << " tj2ClosePt "
4624  << tj2ClosePt << " Pos " << PrintPos(tj2.Pts[tj2ClosePt]);
4625  // check for full overlap
4626  if (tj2ClosePt > tj2.EndPt[1]) return false;
4627 
4628  // The approach is to append tj2 to tj1, store tj1 as a new trajectory,
4629  // and re-assign all hits to the new trajectory
4630 
4631  // First ensure that any hit will appear only once in the merged trajectory in the overlap region
4632  // whether it is used or unused. The point on tj2 where the merge will begin, tj2ClosePt, will be
4633  // increased until this condition is met.
4634  // Make a temporary vector of tj1 hits in the end points for simpler searching
4635  std::vector<unsigned int> tj1Hits;
4636  for (unsigned short ii = 0; ii < tj1.Pts.size(); ++ii) {
4637  // only go back a few points in tj1
4638  if (ii > 10) break;
4639  unsigned short ipt = tj1.Pts.size() - 1 - ii;
4640  tj1Hits.insert(tj1Hits.end(), tj1.Pts[ipt].Hits.begin(), tj1.Pts[ipt].Hits.end());
4641  if (ipt == 0) break;
4642  } // ii
4643 
4644  bool bumpedPt = true;
4645  while (bumpedPt) {
4646  bumpedPt = false;
4647  for (unsigned short ii = 0; ii < tj2.Pts[tj2ClosePt].Hits.size(); ++ii) {
4648  unsigned int iht = tj2.Pts[tj2ClosePt].Hits[ii];
4649  if (std::find(tj1Hits.begin(), tj1Hits.end(), iht) != tj1Hits.end()) bumpedPt = true;
4650  } // ii
4651  if (bumpedPt && tj2ClosePt < tj2.EndPt[1]) { ++tj2ClosePt; }
4652  else {
4653  break;
4654  }
4655  } // bumpedPt
4656  if (doPrt) mf::LogVerbatim("TC") << " revised tj2ClosePt " << tj2ClosePt;
4657  // append tj2 hits to tj1
4658 
4659  tj1.Pts.insert(tj1.Pts.end(), tj2.Pts.begin() + tj2ClosePt, tj2.Pts.end());
4660  // re-define the end points
4661  SetEndPoints(tj1);
4662  tj1.EndFlag[1] = tj2.EndFlag[1];
4663 
4664  // A more exhaustive check that hits only appear once
4665  if (HasDuplicateHits(slc, tj1, doPrt)) return false;
4666  if (tj2.VtxID[1] > 0) {
4667  // move the end vertex of tj2 to the end of tj1
4668  tj1.VtxID[1] = tj2.VtxID[1];
4669  }
4670  // Transfer some of the AlgMod bits
4671  if (tj2.AlgMod[kMichel]) tj1.AlgMod[kMichel] = true;
4672  if (tj2.AlgMod[kDeltaRay]) {
4673  tj1.AlgMod[kDeltaRay] = true;
4674  tj1.ParentID = tj2.ParentID;
4675  }
4676  // keep track of the IDs before they are clobbered
4677  int tj1ID = tj1.ID;
4678  int tj2ID = tj2.ID;
4679  // kill the original trajectories
4680  MakeTrajectoryObsolete(slc, itj1);
4681  MakeTrajectoryObsolete(slc, itj2);
4682  // Do this so that StoreTraj keeps the correct WorkID (of itj1)
4683  tj1.ID = tj1.WorkID;
4684  SetPDGCode(slc, tj1);
4685  tj1.NeedsUpdate = true;
4686  if (!StoreTraj(slc, tj1)) return false;
4687  int newTjID = slc.tjs.size();
4688  // Use the ParentID to trace which new Tj is superseding the merged ones
4689  tj1.ParentID = newTjID;
4690  tj2.ParentID = newTjID;
4691  if (doPrt) mf::LogVerbatim("TC") << " MAS success. Created T" << newTjID;
4692  // Transfer the ParentIDs of any other Tjs that refer to Tj1 and Tj2 to the new Tj
4693  for (auto& tj : slc.tjs)
4694  if (tj.ParentID == tj1ID || tj.ParentID == tj2ID) tj.ParentID = newTjID;
4695  // try to attach it to a vertex
4696  AttachAnyVertexToTraj(slc, newTjID, doPrt);
4697  return true;
4698  } // MergeAndStore
4699 
4701  std::vector<int> GetAssns(TCSlice const& slc,
4702  std::string type1Name,
4703  int id,
4704  std::string type2Name)
4705  {
4706  // returns a list of IDs of objects (slc, vertices, pfps, etc) with type1Name that are in slc with
4707  // type2Name. This is intended to be a general purpose replacement for specific functions like GetVtxTjIDs, etc
4708 
4709  std::vector<int> tmp;
4710  if (id <= 0) return tmp;
4711  unsigned int uid = id;
4712 
4713  if (type1Name == "T" && uid <= slc.tjs.size() && type2Name == "P") {
4714  // return a list of PFPs that have the tj in TjIDs, P -> T<ID>
4715  for (auto& pfp : slc.pfps) {
4716  if (pfp.ID <= 0) continue;
4717  if (std::find(pfp.TjIDs.begin(), pfp.TjIDs.end(), id) != pfp.TjIDs.end())
4718  tmp.push_back(pfp.ID);
4719  } // pf
4720  return tmp;
4721  } // P -> T
4722 
4723  if (type1Name == "P" && uid <= slc.pfps.size() && (type2Name == "2S" || type2Name == "3S")) {
4724  // return a list of 3D or 2D showers with the assn 3S -> 2S -> T -> P<ID> or 2S -> T -> P.
4725  auto& pfp = slc.pfps[uid - 1];
4726  // First form a list of 2S -> T -> P<ID>
4727  std::vector<int> ssid;
4728  for (auto& ss : slc.cots) {
4729  if (ss.ID <= 0) continue;
4730  auto shared = SetIntersection(ss.TjIDs, pfp.TjIDs);
4731  if (!shared.empty() && std::find(ssid.begin(), ssid.end(), ss.ID) == ssid.end())
4732  ssid.push_back(ss.ID);
4733  } // ss
4734  if (type2Name == "2S") return ssid;
4735  for (auto& ss3 : slc.showers) {
4736  if (ss3.ID <= 0) continue;
4737  auto shared = SetIntersection(ss3.CotIDs, ssid);
4738  if (!shared.empty() && std::find(tmp.begin(), tmp.end(), ss3.ID) == tmp.end())
4739  tmp.push_back(ss3.ID);
4740  } // ss3
4741  return tmp;
4742  } // 3S -> 2S -> T -> P
4743 
4744  if (type1Name == "2V" && uid <= slc.vtxs.size() && type2Name == "T") {
4745  // 2V -> T
4746  for (auto& tj : slc.tjs) {
4747  if (tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) continue;
4748  for (unsigned short end = 0; end < 2; ++end) {
4749  if (tj.VtxID[end] != id) continue;
4750  if (std::find(tmp.begin(), tmp.end(), tj.ID) == tmp.end()) tmp.push_back(tj.ID);
4751  } // end
4752  } // tj
4753  return tmp;
4754  } // 2V -> T
4755 
4756  if (type1Name == "3V" && uid <= slc.vtx3s.size() && type2Name == "P") {
4757  for (auto& pfp : slc.pfps) {
4758  if (pfp.ID == 0) continue;
4759  for (unsigned short end = 0; end < 2; ++end) {
4760  if (pfp.Vx3ID[end] != id) continue;
4761  // encode the end with the ID
4762  if (std::find(tmp.begin(), tmp.end(), pfp.ID) == tmp.end()) tmp.push_back(pfp.ID);
4763  } // end
4764  } // pfp
4765  return tmp;
4766  } // 3V -> P
4767 
4768  if (type1Name == "3V" && uid <= slc.vtx3s.size() && type2Name == "T") {
4769  // 3V -> T
4770  for (auto& tj : slc.tjs) {
4771  if (tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) continue;
4772  for (unsigned short end = 0; end < 2; ++end) {
4773  if (tj.VtxID[end] > 0 && tj.VtxID[end] <= slc.vtxs.size()) {
4774  auto& vx2 = slc.vtxs[tj.VtxID[end] - 1];
4775  if (vx2.Vx3ID != id) continue;
4776  if (std::find(tmp.begin(), tmp.end(), tj.ID) == tmp.end()) tmp.push_back(tj.ID);
4777  }
4778  } // end
4779  } // tj
4780  return tmp;
4781  } // 3V -> T
4782 
4783  if (type1Name == "3V" && uid <= slc.vtx3s.size() && type2Name == "2V") {
4784  // 3V -> 2V
4785  for (auto& vx2 : slc.vtxs) {
4786  if (vx2.ID == 0) continue;
4787  if (vx2.Vx3ID == id) tmp.push_back(vx2.ID);
4788  } // vx2
4789  return tmp;
4790  } // 3V -> 2V
4791 
4792  if (type1Name == "3S" && uid <= slc.showers.size() && type2Name == "T") {
4793  // 3S -> T
4794  auto& ss3 = slc.showers[uid - 1];
4795  if (ss3.ID == 0) return tmp;
4796  for (auto cid : ss3.CotIDs) {
4797  auto& ss = slc.cots[cid - 1];
4798  if (ss.ID == 0) continue;
4799  tmp.insert(tmp.end(), ss.TjIDs.begin(), ss.TjIDs.end());
4800  } // cid
4801  return tmp;
4802  } // 3S -> T
4803 
4804  // This isn't strictly necessary but do it for consistency
4805  if (type1Name == "2S" && uid <= slc.cots.size() && type2Name == "T") {
4806  // 2S -> T
4807  auto& ss = slc.cots[uid - 1];
4808  return ss.TjIDs;
4809  } // 2S -> T
4810 
4811  if (type1Name == "3S" && uid <= slc.showers.size() && type2Name == "P") {
4812  // 3S -> P
4813  auto& ss3 = slc.showers[uid - 1];
4814  if (ss3.ID == 0) return tmp;
4815  for (auto cid : ss3.CotIDs) {
4816  auto& ss = slc.cots[cid - 1];
4817  if (ss.ID == 0) continue;
4818  for (auto tid : ss.TjIDs) {
4819  auto& tj = slc.tjs[tid - 1];
4820  if (tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) continue;
4821  if (!tj.AlgMod[kMat3D]) continue;
4822  for (auto& pfp : slc.pfps) {
4823  if (pfp.ID <= 0) continue;
4824  if (std::find(pfp.TjIDs.begin(), pfp.TjIDs.end(), tj.ID) == pfp.TjIDs.end()) continue;
4825  if (std::find(tmp.begin(), tmp.end(), pfp.ID) == tmp.end()) tmp.push_back(pfp.ID);
4826  } // pf
4827  } // tid
4828  } // cid
4829  return tmp;
4830  } // 3S -> P
4831 
4832  if (type1Name == "T" && uid <= slc.tjs.size() && type2Name == "2S") {
4833  // T -> 2S
4834  for (auto& ss : slc.cots) {
4835  if (ss.ID == 0) continue;
4836  if (std::find(ss.TjIDs.begin(), ss.TjIDs.end(), id) != ss.TjIDs.end()) tmp.push_back(ss.ID);
4837  } // ss
4838  return tmp;
4839  } // T -> 2S
4840 
4841  if (type1Name == "T" && uid <= slc.tjs.size() && type2Name == "3S") {
4842  // T -> 3S
4843  for (auto& ss : slc.cots) {
4844  if (ss.ID == 0) continue;
4845  if (std::find(ss.TjIDs.begin(), ss.TjIDs.end(), id) == ss.TjIDs.end()) continue;
4846  if (ss.SS3ID > 0) tmp.push_back(ss.SS3ID);
4847  } // ss
4848  return tmp;
4849  } // T -> 3S
4850 
4851  return tmp;
4852  } // GetAssns
4853 
4855  bool StartTraj(TCSlice const& slc,
4856  Trajectory& tj,
4857  unsigned int fromhit,
4858  unsigned int tohit,
4859  unsigned short pass)
4860  {
4861  // Start a trajectory located at fromHit with direction pointing to toHit
4862 
4863  auto& fromHit = (*evt.allHits)[slc.slHits[fromhit].allHitsIndex];
4864  auto& toHit = (*evt.allHits)[slc.slHits[tohit].allHitsIndex];
4865  float fromWire = fromHit.WireID().Wire;
4866  float fromTick = fromHit.PeakTime();
4867  float toWire = toHit.WireID().Wire;
4868  float toTick = toHit.PeakTime();
4869  CTP_t tCTP = EncodeCTP(fromHit.WireID());
4870  bool success = StartTraj(tj, fromWire, fromTick, toWire, toTick, tCTP, pass);
4871  if (!success) return false;
4872  // turn on debugging using the WorkID?
4873  if (tcc.modes[kDebug] && !tcc.dbgStp && !tcc.dbgDump && tcc.dbgSlc && tj.ID == debug.WorkID)
4874  tcc.dbgStp = true;
4875  if (tcc.dbgStp) {
4876  auto& tp = tj.Pts[0];
4877  mf::LogVerbatim("TC") << "StartTraj T" << tj.ID << " from " << (int)fromWire << ":"
4878  << (int)fromTick << " -> " << (int)toWire << ":" << (int)toTick
4879  << " StepDir " << tj.StepDir << " dir " << tp.Dir[0] << " " << tp.Dir[1]
4880  << " ang " << tp.Ang << " AngleCode " << tp.AngleCode << " angErr "
4881  << tp.AngErr << " ExpectedHitsRMS " << ExpectedHitsRMS(tp);
4882  } // tcc.dbgStp
4883  return true;
4884  } // StartTraj
4885 
4888  float fromWire,
4889  float fromTick,
4890  float toWire,
4891  float toTick,
4892  CTP_t& tCTP,
4893  unsigned short pass)
4894  {
4895  // Start a simple (seed) trajectory going from (fromWire, toTick) to (toWire, toTick).
4896 
4897  // decrement the work ID so we can use it for debugging problems
4898  --evt.WorkID;
4899  if (evt.WorkID == INT_MIN) evt.WorkID = -1;
4900  tj.ID = evt.WorkID;
4901  tj.Pass = pass;
4902  // Assume we are stepping in the positive WSE units direction
4903  short stepdir = 1;
4904  int fWire = std::nearbyint(fromWire);
4905  int tWire = std::nearbyint(toWire);
4906  if (tWire < fWire) { stepdir = -1; }
4907  else if (tWire == fWire) {
4908  // on the same wire
4909  if (toTick < fromTick) stepdir = -1;
4910  }
4911  tj.StepDir = stepdir;
4912  tj.CTP = tCTP;
4913  tj.ParentID = -1;
4914  tj.Strategy.reset();
4915  tj.Strategy[kNormal] = true;
4916 
4917  // create a trajectory point
4918  TrajPoint tp;
4919  if (!MakeBareTrajPoint(fromWire, fromTick, toWire, toTick, tCTP, tp)) return false;
4920  SetAngleCode(tp);
4921  tp.AngErr = 0.1;
4922  tj.Pts.push_back(tp);
4923  // turn on debugging using the WorkID?
4924  if (tcc.modes[kDebug] && !tcc.dbgStp && !tcc.dbgDump && tcc.dbgSlc && tj.ID == debug.WorkID)
4925  tcc.dbgStp = true;
4926  if (tcc.dbgStp) {
4927  auto& tp = tj.Pts[0];
4928  mf::LogVerbatim("TC") << "StartTraj T" << tj.ID << " from " << (int)fromWire << ":"
4929  << (int)fromTick << " -> " << (int)toWire << ":" << (int)toTick
4930  << " StepDir " << tj.StepDir << " dir " << tp.Dir[0] << " " << tp.Dir[1]
4931  << " ang " << tp.Ang << " AngleCode " << tp.AngleCode << " angErr "
4932  << tp.AngErr << " ExpectedHitsRMS " << ExpectedHitsRMS(tp);
4933  } // tcc.dbgStp
4934  return true;
4935 
4936  } // StartTraj
4937 
4939  std::pair<unsigned short, unsigned short> GetSliceIndex(std::string typeName, int uID)
4940  {
4941  // returns the slice index and product index of a data product having typeName and unique ID uID
4942  for (unsigned short isl = 0; isl < slices.size(); ++isl) {
4943  auto& slc = slices[isl];
4944  if (typeName == "T") {
4945  for (unsigned short indx = 0; indx < slc.tjs.size(); ++indx) {
4946  if (slc.tjs[indx].UID == uID) { return std::make_pair(isl, indx); }
4947  }
4948  } // T
4949  if (typeName == "P") {
4950  for (unsigned short indx = 0; indx < slc.pfps.size(); ++indx) {
4951  if (slc.pfps[indx].UID == uID) { return std::make_pair(isl, indx); }
4952  }
4953  } // P
4954  if (typeName == "2V") {
4955  for (unsigned short indx = 0; indx < slc.vtxs.size(); ++indx) {
4956  if (slc.vtxs[indx].UID == uID) { return std::make_pair(isl, indx); }
4957  }
4958  } // 2V
4959  if (typeName == "3V") {
4960  for (unsigned short indx = 0; indx < slc.vtx3s.size(); ++indx) {
4961  if (slc.vtx3s[indx].UID == uID) { return std::make_pair(isl, indx); }
4962  }
4963  } // 3V
4964  if (typeName == "2S") {
4965  for (unsigned short indx = 0; indx < slc.cots.size(); ++indx) {
4966  if (slc.cots[indx].UID == uID) { return std::make_pair(isl, indx); }
4967  }
4968  } // 2S
4969  if (typeName == "3S") {
4970  for (unsigned short indx = 0; indx < slc.showers.size(); ++indx) {
4971  if (slc.showers[indx].UID == uID) { return std::make_pair(isl, indx); }
4972  }
4973  } // T
4974  } // isl
4975  return std::make_pair(USHRT_MAX, USHRT_MAX);
4976  } // GetSliceIndex
4977 
4979  bool Fit2D(short mode,
4980  Point2_t inPt,
4981  float& inPtErr,
4982  Vector2_t& outVec,
4983  Vector2_t& outVecErr,
4984  float& chiDOF)
4985  {
4986  // Fit points to a 2D line.
4987  // Mode = 0: Initialize
4988  // Mode = 1: Accumulate
4989  // Mode = 2: Accumulate and store to calculate chiDOF
4990  // Mode = -1: Fit and put results in outVec and chiDOF
4991 
4992  static double sum, sumx, sumy, sumx2, sumy2, sumxy;
4993  static unsigned short cnt;
4994  static std::vector<Point2_t> fitPts;
4995  static std::vector<double> fitWghts;
4996 
4997  if (mode == 0) {
4998  // initialize
4999  cnt = 0;
5000  sum = 0.;
5001  sumx = 0.;
5002  sumy = 0.;
5003  sumx2 = 0.;
5004  sumy2 = 0.;
5005  sumxy = 0;
5006  fitPts.resize(0);
5007  fitWghts.resize(0);
5008  return true;
5009  } // mode == 0
5010 
5011  if (mode > 0) {
5012  if (inPtErr <= 0.) return false;
5013  ++cnt;
5014  double wght = 1 / (inPtErr * inPtErr);
5015  sum += wght;
5016  sumx += wght * inPt[0];
5017  sumx2 += wght * inPt[0] * inPt[0];
5018  sumy += wght * inPt[1];
5019  sumy2 += wght * inPt[1] * inPt[1];
5020  sumxy += wght * inPt[0] * inPt[1];
5021  if (mode == 1) return true;
5022  fitPts.push_back(inPt);
5023  fitWghts.push_back(wght);
5024  return true;
5025  } // Accumulate
5026 
5027  if (cnt < 2) return false;
5028  // do the fit
5029  double delta = sum * sumx2 - sumx * sumx;
5030  if (delta == 0.) return false;
5031  double A = (sumx2 * sumy - sumx * sumxy) / delta;
5032  double B = (sumxy * sum - sumx * sumy) / delta;
5033  outVec[0] = A;
5034  outVec[1] = B;
5035  chiDOF = 0;
5036  if (cnt == 2 || fitPts.empty()) return true;
5037 
5038  // calculate errors and chiDOF
5039  if (fitPts.size() != cnt) return false;
5040  double ndof = cnt - 2;
5041  double varnce =
5042  (sumy2 + A * A * sum + B * B * sumx2 - 2 * (A * sumy + B * sumxy - A * B * sumx)) / ndof;
5043  if (varnce > 0.) {
5044  outVecErr[0] = sqrt(varnce * sumx2 / delta);
5045  outVecErr[1] = sqrt(varnce * sum / delta);
5046  }
5047  else {
5048  outVecErr[0] = 0.;
5049  outVecErr[1] = 0.;
5050  }
5051  sum = 0.;
5052  // calculate chisq
5053  for (unsigned short ii = 0; ii < fitPts.size(); ++ii) {
5054  double arg = fitPts[ii][1] - A - B * fitPts[ii][0];
5055  sum += fitWghts[ii] * arg * arg;
5056  }
5057  chiDOF = sum / ndof;
5058  fitPts.resize(0);
5059  fitWghts.resize(0);
5060  return true;
5061 
5062  } // Fit2D
5063 
5065  bool DecodeDebugString(std::string strng)
5066  {
5067  // try to unpack the string as Cryostat:TPC:Plane:Wire:Tick or something
5068  // like Slice:<slice index>
5069 
5070  if (strng == "instruct") {
5071  std::cout << "****** Unrecognized DebugConfig. Here are your options\n";
5072  std::cout << " 'C:T:P:W:Tick' where C = cryostat, T = TPC, W = wire, Tick (+/-5) to debug "
5073  "stepping (DUNE)\n";
5074  std::cout << " 'P:W:Tick' for single cryostat/TPC detectors (uB, LArIAT, etc)\n";
5075  std::cout << " 'WorkID <id> <slice index>' where <id> is a tj work ID (< 0) in slice <slice "
5076  "index> (default = 0)\n";
5077  std::cout << " 'Merge <CTP>' to debug trajectory merging\n";
5078  std::cout << " '2V <CTP>' to debug 2D vertex finding\n";
5079  std::cout << " '3V' to debug 3D vertex finding\n";
5080  std::cout << " 'VxMerge' to debug 2D vertex merging\n";
5081  std::cout << " 'JunkVx' to debug 2D junk vertex finder\n";
5082  std::cout << " 'PFP' to debug 3D matching and PFParticles\n";
5083  std::cout << " 'MVI <MVI> <MVI Iteration>' for detailed debugging of one PFP MatchVecIndex\n";
5084  std::cout << " 'DeltaRay' to debug delta ray tagging\n";
5085  std::cout << " 'Muon' to debug muon tagging\n";
5086  std::cout << " '2S <CTP>' to debug a 2D shower in CTP\n";
5087  std::cout << " 'Reco TPC <TPC>' to only reconstruct hits in the specified TPC\n";
5088  std::cout << " 'Reco Slice <ID>' to reconstruct all sub-slices in the recob::Slice with the "
5089  "specified ID\n";
5090  std::cout << " 'SubSlice <sub-slice index>' where <slice index> restricts output to the "
5091  "specified sub-slice index\n";
5092  std::cout << " 'Stitch' to debug PFParticle stitching between TPCs\n";
5093  std::cout << " 'Sum' or 'Summary' to print a debug summary report\n";
5094  std::cout << " 'Dump <WorkID>' or 'Dump <UID>' to print all TPs in the trajectory to "
5095  "tcdump<UID>.csv\n";
5096  std::cout << " Note: Algs with debug printing include HamVx, HamVx2, SplitTjCVx, Comp3DVx, "
5097  "Comp3DVxIG, VtxHitsSwap\n";
5098  std::cout << " Set SkipAlgs: [\"bogusText\"] to print a list of algorithm names\n";
5099  return false;
5100  } // instruct
5101 
5102  // handle the simple cases that don't need decoding
5103  if (strng.find("3V") != std::string::npos) {
5104  tcc.dbg3V = true;
5105  tcc.modes[kDebug] = true;
5106  return true;
5107  }
5108  if (strng.find("3S") != std::string::npos) {
5109  tcc.dbg3S = true;
5110  tcc.modes[kDebug] = true;
5111  return true;
5112  }
5113  if (strng.find("VxMerge") != std::string::npos) {
5114  tcc.dbgVxMerge = true;
5115  tcc.modes[kDebug] = true;
5116  return true;
5117  }
5118  if (strng.find("JunkVx") != std::string::npos) {
5119  tcc.dbgVxJunk = true;
5120  tcc.modes[kDebug] = true;
5121  return true;
5122  }
5123  if (strng.find("DeltaRay") != std::string::npos) {
5124  tcc.dbgDeltaRayTag = true;
5125  tcc.modes[kDebug] = true;
5126  return true;
5127  }
5128  if (strng.find("Muon") != std::string::npos) {
5129  tcc.dbgMuonTag = true;
5130  tcc.modes[kDebug] = true;
5131  return true;
5132  }
5133  if (strng.find("Stitch") != std::string::npos) {
5134  tcc.dbgStitch = true;
5135  tcc.modes[kDebug] = true;
5136  return true;
5137  }
5138  if (strng.find("HamVx") != std::string::npos) {
5139  tcc.dbgAlg[kHamVx] = true;
5140  tcc.modes[kDebug] = true;
5141  return true;
5142  }
5143  if (strng.find("HamVx2") != std::string::npos) {
5144  tcc.dbgAlg[kHamVx2] = true;
5145  tcc.modes[kDebug] = true;
5146  return true;
5147  }
5148  if (strng.find("Sum") != std::string::npos) {
5149  tcc.dbgSummary = true;
5150  tcc.modes[kDebug] = true;
5151  return true;
5152  }
5153 
5154  std::vector<std::string> words;
5155  boost::split(words, strng, boost::is_any_of(" :"), boost::token_compress_on);
5156  if (words.size() == 5) {
5157  // configure for DUNE
5158  debug.Cryostat = std::stoi(words[0]);
5159  debug.TPC = std::stoi(words[1]);
5160  debug.Plane = std::stoi(words[2]);
5161  debug.Wire = std::stoi(words[3]);
5162  debug.Tick = std::stoi(words[4]);
5163  tcc.modes[kDebug] = true;
5164  tcc.dbgStp = true;
5165  // also dump this tj
5166  tcc.dbgDump = true;
5167  return true;
5168  } // nums.size() == 5
5169  if (words[0] == "PFP" || words[0] == "MVI") {
5170  tcc.dbgPFP = true;
5171  tcc.modes[kDebug] = true;
5172  // Use debug.Hit to identify the matchVec index
5173  if (words.size() > 2) {
5174  debug.MVI = std::stoi(words[1]);
5175  if (words.size() == 3) debug.MVI_Iter = std::stoi(words[2]);
5176  }
5177  return true;
5178  } // PFP
5179  if (words.size() == 2 && words[0] == "Dump") {
5180  debug.WorkID = std::stoi(words[1]);
5181  debug.Slice = 0;
5182  tcc.modes[kDebug] = true;
5183  tcc.dbgDump = true;
5184  return true;
5185  }
5186  if (words.size() > 1 && words[0] == "WorkID") {
5187  debug.WorkID = std::stoi(words[1]);
5188  if (debug.WorkID >= 0) return false;
5189  // default to sub-slice index 0
5190  debug.Slice = 0;
5191  if (words.size() > 2) debug.Slice = std::stoi(words[2]);
5192  tcc.modes[kDebug] = true;
5193  // dbgStp is set true after debug.WorkID is found
5194  tcc.dbgStp = false;
5195  return true;
5196  } // words.size() == 3 && words[0] == "WorkID"
5197  if (words.size() == 3 && words[0] == "Reco" && words[1] == "TPC") {
5198  tcc.recoTPC = std::stoi(words[2]);
5199  tcc.modes[kDebug] = true;
5200  std::cout << "Reconstructing only in TPC " << tcc.recoTPC << "\n";
5201  return true;
5202  }
5203  if (words.size() == 3 && words[0] == "Reco" && words[1] == "Slice") {
5204  tcc.recoSlice = std::stoi(words[1]);
5205  std::cout << "Reconstructing Slice " << tcc.recoSlice << "\n";
5206  return true;
5207  }
5208  if (words.size() == 3) {
5209  // configure for uB, LArIAT, etc
5210  debug.Cryostat = 0;
5211  debug.TPC = 0;
5212  debug.Plane = std::stoi(words[0]);
5213  debug.Wire = std::stoi(words[1]);
5214  debug.Tick = std::stoi(words[2]);
5216  tcc.modes[kDebug] = true;
5217  tcc.dbgStp = true;
5218  return true;
5219  }
5220  if (words.size() == 2 && words[0] == "Merge") {
5221  debug.CTP = std::stoi(words[1]);
5222  tcc.dbgMrg = true;
5223  tcc.modes[kDebug] = true;
5224  return true;
5225  }
5226  if (words.size() == 2 && words[0] == "2V") {
5227  debug.CTP = std::stoi(words[1]);
5228  tcc.dbg2V = true;
5229  tcc.modes[kDebug] = true;
5230  return true;
5231  }
5232  if (words.size() == 2 && words[0] == "2S") {
5233  debug.CTP = std::stoi(words[1]);
5234  tcc.dbg2S = true;
5235  tcc.modes[kDebug] = true;
5236  return true;
5237  }
5238  // Slice could apply to several debug options.
5239  if (words.size() == 2 && words[0] == "SubSlice") {
5240  debug.Slice = std::stoi(words[1]);
5241  return true;
5242  }
5243  return false;
5244  } // DecodeDebugString
5245 
5246  // ****************************** Printing ******************************
5247 
5248  void DumpTj()
5249  {
5250  // Dump all of the points in a trajectory to the output in a form that can
5251  // be imported by another application, e.g. Excel
5252  // Search for the trajectory with the specified WorkID or Unique ID
5253 
5254  for (auto const& slc : slices) {
5255  for (auto& tj : slc.tjs) {
5256  if (tj.WorkID != debug.WorkID && tj.UID != debug.WorkID) continue;
5257  // print a header
5258  std::ofstream outfile;
5259  std::string fname = "tcdump" + std::to_string(tj.UID) + ".csv";
5260  outfile.open(fname, std::ios::out | std::ios::trunc);
5261  outfile << "Dump trajectory T" << tj.UID << " WorkID " << tj.WorkID;
5262  outfile << " ChgRMS " << std::setprecision(2) << tj.ChgRMS;
5263  outfile << "\n";
5264  outfile << "Wire, Chg T" << tj.UID
5265  << ", totChg, Tick, Delta, NTPsFit, Ang, ChiDOF, KinkSig, HitPosErr\n";
5266  for (unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
5267  auto& tp = tj.Pts[ipt];
5268  outfile << std::fixed;
5269  outfile << std::setprecision(0) << std::nearbyint(tp.Pos[0]);
5270  outfile << "," << (int)tp.Chg;
5271  // total charge near the TP
5272  float totChg = 0;
5273  for (auto iht : tp.Hits) {
5274  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
5275  totChg += hit.Integral();
5276  }
5277  outfile << "," << (int)totChg;
5278  outfile << "," << std::setprecision(0) << std::nearbyint(tp.Pos[1] / tcc.unitsPerTick);
5279  outfile << "," << std::setprecision(2) << tp.Delta;
5280  outfile << "," << tp.NTPsFit;
5281  outfile << "," << std::setprecision(3) << tp.Ang;
5282  outfile << "," << std::setprecision(2) << tp.FitChi;
5283  outfile << "," << std::setprecision(2) << tp.KinkSig;
5284  outfile << "," << std::setprecision(2) << sqrt(tp.HitPosErr2);
5285  outfile << "\n";
5286  } // ipt
5287  outfile.close();
5288  std::cout << "Points on T" << tj.UID << " dumped to " << fname << "\n";
5289  tcc.dbgDump = false;
5290  return;
5291  } // tj
5292  } // slc
5293 
5294  } // DumpTj
5295 
5298  {
5299  // print the debug mode configuration to the screen
5300  std::cout << "*** TrajCluster debug mode configuration in";
5301  std::cout << " CTP=";
5302  if (debug.CTP == UINT_MAX) { std::cout << "NA"; }
5303  else {
5304  std::cout << debug.CTP;
5305  }
5306  std::cout << " Cryostat=" << debug.Cryostat;
5307  std::cout << " TPC=" << debug.TPC;
5308  std::cout << " Plane=" << debug.Plane;
5309  std::cout << " Wire=" << debug.Wire;
5310  std::cout << " Tick=" << debug.Tick;
5311  std::cout << " Hit=";
5312  if (debug.Hit == UINT_MAX) { std::cout << "NA"; }
5313  else {
5314  std::cout << debug.Hit;
5315  }
5316  std::cout << " WorkID=";
5317  if (debug.WorkID == 0) { std::cout << "NA"; }
5318  else {
5319  std::cout << debug.WorkID;
5320  }
5321  std::cout << " Slice=";
5322  if (debug.Slice == -1) { std::cout << "All"; }
5323  else {
5324  std::cout << debug.Slice;
5325  }
5326  std::cout << "\n";
5327  std::cout << "*** tcc.dbg modes:";
5328  if (tcc.dbgSlc) std::cout << " dbgSlc";
5329  if (tcc.dbgStp) std::cout << " dbgStp";
5330  if (tcc.dbgMrg) std::cout << " dbgMrg";
5331  if (tcc.dbg2V) std::cout << " dbg2V";
5332  if (tcc.dbg2S) std::cout << " dbg2S";
5333  if (tcc.dbgVxNeutral) std::cout << " dbgVxNeutral";
5334  if (tcc.dbgVxMerge) std::cout << " dbgVxMerge";
5335  if (tcc.dbgVxJunk) std::cout << " dbgVxJunk";
5336  if (tcc.dbg3V) std::cout << " dbg3V";
5337  if (tcc.dbgPFP) std::cout << " dbgPFP";
5338  if (tcc.dbgDeltaRayTag) std::cout << " dbgDeltaRayTag";
5339  if (tcc.dbgMuonTag) std::cout << " dbgMuonTag";
5340  if (tcc.dbgStitch) std::cout << " dbgStitch";
5341  if (tcc.dbgSummary) std::cout << " dbgSummary";
5342  if (tcc.dbgDump) std::cout << " dbgDump";
5343  std::cout << "\n";
5344  std::cout << "*** Using algs:";
5345  unsigned short cnt = 0;
5346  for (unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) {
5347  if (tcc.useAlg[ib] && ib != kKilled) {
5348  ++cnt;
5349  if (cnt % 10 == 0) std::cout << "\n ";
5350  std::cout << " " << AlgBitNames[ib];
5351  }
5352  }
5353  std::cout << "\n";
5354  std::cout << "*** Skipping algs:";
5355  cnt = 0;
5356  for (unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) {
5357  if (!tcc.useAlg[ib] && ib != kKilled) {
5358  ++cnt;
5359  if (cnt % 10 == 0) std::cout << "\n ";
5360  std::cout << " " << AlgBitNames[ib];
5361  }
5362  }
5363  std::cout << "\n";
5364  } // PrintDebugMode
5365 
5367  void PrintAll(detinfo::DetectorPropertiesData const& detProp, std::string someText)
5368  {
5369  // print everything in all slices
5370  bool prt3V = false;
5371  bool prt2V = false;
5372  bool prtT = false;
5373  bool prtP = false;
5374  bool prtS3 = false;
5375  for (size_t isl = 0; isl < slices.size(); ++isl) {
5376  if (debug.Slice >= 0 && int(isl) != debug.Slice) continue;
5377  auto const& slc = slices[isl];
5378  if (!slc.vtx3s.empty()) prt3V = true;
5379  if (!slc.vtxs.empty()) prt2V = true;
5380  if (!slc.tjs.empty()) prtT = true;
5381  if (!slc.pfps.empty()) prtP = true;
5382  if (!slc.showers.empty()) prtS3 = true;
5383  } // slc
5384  mf::LogVerbatim myprt("TC");
5385  myprt << "Debug report from caller " << someText << "\n";
5386  myprt << " 'prodID' = <sliceID>:<subSliceIndex>:<productID>/<productUID>\n";
5387  if (prtS3) {
5388  myprt << "************ Showers ************\n";
5389  myprt << " prodID Vtx parUID ___ChgPos____ ______Dir_____ ____posInPln____ "
5390  "___projInPln____ 2D shower UIDs\n";
5391  for (size_t isl = 0; isl < slices.size(); ++isl) {
5392  if (debug.Slice >= 0 && int(isl) != debug.Slice) continue;
5393  auto const& slc = slices[isl];
5394  if (slc.showers.empty()) continue;
5395  for (auto& ss3 : slc.showers)
5396  Print3S(detProp, myprt, ss3);
5397  } // slc
5398  } // prtS3
5399  if (prtP) {
5400  bool printHeader = true;
5401  for (size_t isl = 0; isl < slices.size(); ++isl) {
5402  if (debug.Slice >= 0 && int(isl) != debug.Slice) continue;
5403  auto const& slc = slices[isl];
5404  if (slc.pfps.empty()) continue;
5405  for (auto& pfp : slc.pfps)
5406  PrintP(myprt, pfp, printHeader);
5407  } // slc
5408  } // prtS3
5409  if (prt3V) {
5410  bool printHeader = true;
5411  myprt << "****** 3D vertices "
5412  "******************************************__2DVtx_UID__*******\n";
5413  myprt << " prodID Cstat TPC X Y Z XEr YEr "
5414  "ZEr pln0 pln1 pln2 Wire score Prim? Nu? nTru";
5415  myprt << " ___________2D_Pos____________ _____Tj UIDs________\n";
5416  for (size_t isl = 0; isl < slices.size(); ++isl) {
5417  if (debug.Slice >= 0 && int(isl) != debug.Slice) continue;
5418  auto const& slc = slices[isl];
5419  if (slc.vtx3s.empty()) continue;
5420  for (auto& vx3 : slc.vtx3s)
5421  Print3V(detProp, myprt, vx3, printHeader);
5422  } // slc
5423  } // prt3V
5424  if (prt2V) {
5425  bool printHeader = true;
5426  myprt << "************ 2D vertices ************\n";
5427  myprt << " prodID CTP wire err tick err ChiDOF NTj Pass "
5428  " Topo ChgFrac Score v3D Tj UIDs\n";
5429  for (size_t isl = 0; isl < slices.size(); ++isl) {
5430  if (debug.Slice >= 0 && int(isl) != debug.Slice) continue;
5431  auto const& slc = slices[isl];
5432  if (slc.vtxs.empty()) continue;
5433  for (auto& vx2 : slc.vtxs)
5434  Print2V(myprt, vx2, printHeader);
5435  } // slc
5436  } // prt2V
5437  if (prtT) {
5438  bool printHeader = true;
5439  for (size_t isl = 0; isl < slices.size(); ++isl) {
5440  if (debug.Slice >= 0 && int(isl) != debug.Slice) continue;
5441  auto const& slc = slices[isl];
5442  if (slc.tjs.empty()) continue;
5443  for (auto& tj : slc.tjs)
5444  PrintT(myprt, tj, printHeader);
5445  } // slc
5446  } // prtT
5447  } // PrintAll
5448 
5450  void PrintP(mf::LogVerbatim& myprt, PFPStruct const& pfp, bool& printHeader)
5451  {
5452  if (pfp.ID <= 0) return;
5453  if (printHeader) {
5454  myprt << "************ PFParticles ************\n";
5455  myprt << " prodID sVx _____sPos____ CS _______sDir______ ____sdEdx_____ eVx "
5456  "_____ePos____ CS ____edEdx_____ MVI MCSMom Len nTP3 nSec SLk? PDG Par \n";
5457  printHeader = false;
5458  } // printHeader
5459  auto sIndx = GetSliceIndex("P", pfp.UID);
5460  if (sIndx.first == USHRT_MAX) return;
5461  auto const& slc = slices[sIndx.first];
5462  std::string str =
5463  std::to_string(slc.ID) + ":" + std::to_string(sIndx.first) + ":" + std::to_string(pfp.ID);
5464  str += "/" + std::to_string(pfp.UID);
5465  myprt << std::setw(12) << str;
5466  // start and end stuff
5467  for (unsigned short end = 0; end < 2; ++end) {
5468  str = "--";
5469  if (pfp.Vx3ID[end] > 0) str = "3V" + std::to_string(slc.vtx3s[pfp.Vx3ID[end] - 1].UID);
5470  myprt << std::setw(6) << str;
5471  myprt << std::fixed << std::right << std::setprecision(0);
5472  auto pos = PosAtEnd(pfp, end);
5473  myprt << std::setw(5) << pos[0];
5474  myprt << std::setw(5) << pos[1];
5475  myprt << std::setw(5) << pos[2];
5476  // print character for Outside or Inside the FV
5477  if (InsideFV(slc, pfp, end)) { myprt << " I"; }
5478  else {
5479  myprt << " O";
5480  }
5481  // only print the starting direction
5482  if (end == 0) {
5483  myprt << std::fixed << std::right << std::setprecision(2);
5484  auto dir = DirAtEnd(pfp, end);
5485  myprt << std::setw(6) << dir[0];
5486  myprt << std::setw(6) << dir[1];
5487  myprt << std::setw(6) << dir[2];
5488  } // end == 0
5489  for (auto& dedx : pfp.dEdx[end]) {
5490  if (dedx < 50) { myprt << std::setw(5) << std::setprecision(1) << dedx; }
5491  else {
5492  myprt << std::setw(5) << std::setprecision(0) << dedx;
5493  }
5494  } // dedx
5495  if (pfp.dEdx[end].size() < 3) {
5496  for (size_t i = 0; i < 3 - pfp.dEdx[end].size(); ++i) {
5497  myprt << std::setw(6) << ' ';
5498  }
5499  }
5500  } // startend
5501  myprt << std::setw(6) << pfp.MVI;
5502  // global stuff
5503  myprt << std::setw(7) << MCSMom(slc, pfp.TjIDs);
5504  float length = Length(pfp);
5505  if (length < 100) { myprt << std::setw(5) << std::setprecision(1) << length; }
5506  else {
5507  myprt << std::setw(5) << std::setprecision(0) << length;
5508  }
5509  myprt << std::setw(5) << pfp.TP3Ds.size();
5510  myprt << std::setw(5) << pfp.SectionFits.size();
5511  myprt << std::setw(5) << IsShowerLike(slc, pfp.TjIDs);
5512  myprt << std::setw(5) << pfp.PDGCode;
5513  myprt << std::setw(4) << pfp.ParentUID;
5514  if (!pfp.TjIDs.empty()) {
5515  if (pfp.TjUIDs.empty()) {
5516  // print Tjs in one TPC
5517  for (auto tjid : pfp.TjIDs)
5518  myprt << " TU" << slc.tjs[tjid - 1].UID;
5519  }
5520  else {
5521  // print Tjs in all TPCs (if this is called after FinishEvent)
5522  for (auto tjuid : pfp.TjUIDs)
5523  myprt << " TU" << tjuid;
5524  }
5525  } // TjIDs exist
5526  if (!pfp.DtrUIDs.empty()) {
5527  myprt << " dtrs";
5528  for (auto dtruid : pfp.DtrUIDs)
5529  myprt << " PU" << dtruid;
5530  } // dtr ids exist
5531  myprt << "\n";
5532  } // PrintP
5533 
5536  mf::LogVerbatim& myprt,
5537  Vtx3Store const& vx3,
5538  bool& printHeader)
5539  {
5540  // print a 3D vertex on one line
5541  if (vx3.ID <= 0) return;
5542  auto sIndx = GetSliceIndex("3V", vx3.UID);
5543  if (sIndx.first == USHRT_MAX) return;
5544  auto const& slc = slices[sIndx.first];
5545  if (printHeader) {
5546  myprt
5547  << "****** 3D vertices ******************************************__2DVtx_UID__*******\n";
5548  myprt << " prodID Cstat TPC X Y Z pln0 pln1 pln2 Wire score "
5549  "Prim? Nu? nTru";
5550  myprt << " ___________2D_Pos____________ _____Tj UIDs________\n";
5551  printHeader = false;
5552  }
5553  std::string str = "3V" + std::to_string(vx3.ID) + "/3VU" + std::to_string(vx3.UID);
5554  myprt << std::right << std::setw(12) << std::fixed << str;
5555  myprt << std::setprecision(0);
5556  myprt << std::right << std::setw(7) << vx3.TPCID.Cryostat;
5557  myprt << std::right << std::setw(5) << vx3.TPCID.TPC;
5558  myprt << std::right << std::setw(8) << vx3.X;
5559  myprt << std::right << std::setw(8) << vx3.Y;
5560  myprt << std::right << std::setw(8) << vx3.Z;
5561  for (auto vx2id : vx3.Vx2ID) {
5562  if (vx2id > 0) {
5563  str = "2VU" + std::to_string(slc.vtxs[vx2id - 1].UID);
5564  myprt << std::right << std::setw(7) << str;
5565  }
5566  else {
5567  myprt << " --";
5568  }
5569  } // vx2id
5570  myprt << std::right << std::setw(5) << vx3.Wire;
5571  unsigned short nTruMatch = 0;
5572  for (unsigned short ipl = 0; ipl < slc.nPlanes; ++ipl) {
5573  if (vx3.Vx2ID[ipl] == 0) continue;
5574  unsigned short iv2 = vx3.Vx2ID[ipl] - 1;
5575  if (slc.vtxs[iv2].Stat[kVxTruMatch]) ++nTruMatch;
5576  } // ipl
5577  myprt << std::right << std::setw(6) << std::setprecision(1) << vx3.Score;
5578  myprt << std::setw(6) << vx3.Primary;
5579  myprt << std::setw(4) << vx3.Neutrino;
5580  myprt << std::right << std::setw(5) << nTruMatch;
5581  Point2_t pos;
5582  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
5583  PosInPlane(detProp, vx3, plane, pos);
5584  myprt << " " << PrintPos(pos);
5585  } // plane
5586  if (vx3.Wire == -2) {
5587  // find the Tjs that are attached to it
5588  for (unsigned short end = 0; end < 2; ++end) {
5589  for (auto& pfp : slc.pfps) {
5590  if (pfp.Vx3ID[end] == vx3.ID) {
5591  for (auto tjID : pfp.TjIDs) {
5592  auto& tj = slc.tjs[tjID - 1];
5593  myprt << " T" << tj.UID;
5594  } // tjID
5595  } // pfp.Vx3ID[0] == vx3.ID
5596  } // pfp
5597  } // end
5598  }
5599  else {
5600  auto vxtjs = GetAssns(slc, "3V", vx3.ID, "T");
5601  for (auto tjid : vxtjs) {
5602  auto& tj = slc.tjs[tjid - 1];
5603  myprt << " TU" << tj.UID;
5604  }
5605  } // vx3.Wire != -2
5606  myprt << "\n";
5607  } // Print3V
5608 
5610  void Print2V(mf::LogVerbatim& myprt, VtxStore const& vx2, bool& printHeader)
5611  {
5612  // print a 2D vertex on one line
5613  if (vx2.ID <= 0) return;
5614  if (debug.CTP != UINT_MAX && vx2.CTP != debug.CTP) return;
5615  auto sIndx = GetSliceIndex("2V", vx2.UID);
5616  if (sIndx.first == USHRT_MAX) return;
5617  auto const& slc = slices[sIndx.first];
5618  if (printHeader) {
5619  myprt << "************ 2D vertices ************\n";
5620  myprt << " prodID CTP wire err tick err ChiDOF NTj Pass Topo ChgFrac Score "
5621  " v3D Tj UIDs\n";
5622  printHeader = false;
5623  }
5624  std::string str = "2V" + std::to_string(vx2.ID) + "/2VU" + std::to_string(vx2.UID);
5625  myprt << std::right << std::setw(12) << std::fixed << str;
5626  myprt << std::right << std::setw(6) << vx2.CTP;
5627  myprt << std::right << std::setw(8) << std::setprecision(0) << std::nearbyint(vx2.Pos[0]);
5628  myprt << std::right << std::setw(5) << std::setprecision(1) << vx2.PosErr[0];
5629  myprt << std::right << std::setw(8) << std::setprecision(0)
5630  << std::nearbyint(vx2.Pos[1] / tcc.unitsPerTick);
5631  myprt << std::right << std::setw(5) << std::setprecision(1) << vx2.PosErr[1] / tcc.unitsPerTick;
5632  myprt << std::right << std::setw(7) << vx2.ChiDOF;
5633  myprt << std::right << std::setw(5) << vx2.NTraj;
5634  myprt << std::right << std::setw(5) << vx2.Pass;
5635  myprt << std::right << std::setw(6) << vx2.Topo;
5636  myprt << std::right << std::setw(9) << std::setprecision(2) << vx2.TjChgFrac;
5637  myprt << std::right << std::setw(6) << std::setprecision(1) << vx2.Score;
5638  int v3id = 0;
5639  if (vx2.Vx3ID > 0) v3id = slc.vtx3s[vx2.Vx3ID - 1].UID;
5640  myprt << std::right << std::setw(5) << v3id;
5641  myprt << " ";
5642  // display the traj IDs
5643  for (unsigned short ii = 0; ii < slc.tjs.size(); ++ii) {
5644  auto const& tj = slc.tjs[ii];
5645  if (tj.AlgMod[kKilled]) continue;
5646  for (unsigned short end = 0; end < 2; ++end) {
5647  if (tj.VtxID[end] != (short)vx2.ID) continue;
5648  std::string tid = " TU" + std::to_string(tj.UID) + "_" + std::to_string(end);
5649  myprt << std::right << std::setw(6) << tid;
5650  } // end
5651  } // ii
5652  myprt << " Stat:";
5653  // Special flags. Ignore the first flag bit (0 = kVxTrjTried) which is done for every vertex
5654  for (unsigned short ib = 1; ib < VtxBitNames.size(); ++ib)
5655  if (vx2.Stat[ib]) myprt << " " << VtxBitNames[ib];
5656  myprt << "\n";
5657  } // Print2V
5658 
5661  mf::LogVerbatim& myprt,
5662  ShowerStruct3D const& ss3)
5663  {
5664  if (ss3.ID <= 0) return;
5665  auto sIndx = GetSliceIndex("3S", ss3.UID);
5666  if (sIndx.first == USHRT_MAX) return;
5667  auto const& slc = slices[sIndx.first];
5668  std::string str =
5669  std::to_string(slc.ID) + ":" + std::to_string(sIndx.first) + ":" + std::to_string(ss3.ID);
5670  str += "/" + std::to_string(ss3.UID);
5671  myprt << std::fixed << std::setw(12) << str;
5672  str = "--";
5673  if (ss3.Vx3ID > 0) str = "3V" + std::to_string(slc.vtx3s[ss3.Vx3ID - 1].UID);
5674  myprt << std::setw(6) << str;
5675  for (unsigned short xyz = 0; xyz < 3; ++xyz)
5676  myprt << std::setprecision(0) << std::setw(5) << ss3.ChgPos[xyz];
5677  for (unsigned short xyz = 0; xyz < 3; ++xyz)
5678  myprt << std::setprecision(2) << std::setw(5) << ss3.Dir[xyz];
5679  std::vector<float> projInPlane(slc.nPlanes);
5680  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
5681  CTP_t inCTP = EncodeCTP(ss3.TPCID.Cryostat, ss3.TPCID.TPC, plane);
5682  auto tp = MakeBareTP(detProp, ss3.ChgPos, ss3.Dir, inCTP);
5683  myprt << " " << PrintPos(tp.Pos);
5684  projInPlane[plane] = tp.Delta;
5685  } // plane
5686  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
5687  myprt << std::setprecision(2) << std::setw(5) << projInPlane[plane];
5688  } // plane
5689  for (auto cid : ss3.CotIDs) {
5690  auto& ss = slc.cots[cid - 1];
5691  str = "2SU" + std::to_string(ss.UID);
5692  myprt << std::setw(5) << str;
5693  } // ci
5694  if (ss3.NeedsUpdate) myprt << " *** Needs update";
5695  myprt << "\n";
5696  } // Print3S
5697 
5699  void PrintT(mf::LogVerbatim& myprt, Trajectory const& tj, bool& printHeader)
5700  {
5701  // print a 2D vertex on one line
5702  if (tj.ID <= 0) return;
5703  if (debug.CTP != UINT_MAX && tj.CTP != debug.CTP) return;
5704  if (printHeader) {
5705  myprt << "************ Trajectories ************\n";
5706  myprt << "Tj AngleCode-EndFlag decoder (EF): <AngleCode> + <reason for stopping>";
5707  myprt << " (B=Bragg Peak, V=Vertex, A=AngleKink, C=ChargeKink, T=Trajectory)\n";
5708  myprt << " prodID CTP Pass Pts W:T Ang EF AveQ W:T Ang EF AveQ "
5709  "Chg(k) chgRMS Mom __Vtx__ PDG eLike Par Pri NuPar WorkID \n";
5710  printHeader = false;
5711  }
5712  auto sIndx = GetSliceIndex("T", tj.UID);
5713  if (sIndx.first == USHRT_MAX) return;
5714  auto const& slc = slices[sIndx.first];
5715  std::string str = "T" + std::to_string(tj.ID) + "/TU" + std::to_string(tj.UID);
5716  myprt << std::fixed << std::setw(12) << str;
5717  myprt << std::setw(6) << tj.CTP;
5718  myprt << std::setw(5) << tj.Pass;
5719  myprt << std::setw(5) << tj.EndPt[1] - tj.EndPt[0] + 1;
5720  unsigned short endPt0 = tj.EndPt[0];
5721  auto& tp0 = tj.Pts[endPt0];
5722  int itick = tp0.Pos[1] / tcc.unitsPerTick;
5723  if (itick < 0) itick = 0;
5724  myprt << std::setw(6) << (int)(tp0.Pos[0] + 0.5) << ":" << itick; // W:T
5725  if (itick < 10) { myprt << " "; }
5726  if (itick < 100) { myprt << " "; }
5727  if (itick < 1000) { myprt << " "; }
5728  myprt << std::setw(6) << std::setprecision(2) << tp0.Ang;
5729  myprt << std::setw(2) << tp0.AngleCode;
5730  if (tj.EndFlag[0][kBragg]) { myprt << "B"; }
5731  else if (tj.EndFlag[0][kAtVtx]) {
5732  myprt << "V";
5733  }
5734  else if (tj.EndFlag[0][kAtKink]) {
5735  myprt << "K";
5736  }
5737  else if (tj.EndFlag[0][kAtTj]) {
5738  myprt << "T";
5739  }
5740  else {
5741  myprt << " ";
5742  }
5743  myprt << std::setw(5) << (int)tp0.AveChg;
5744  unsigned short endPt1 = tj.EndPt[1];
5745  auto& tp1 = tj.Pts[endPt1];
5746  itick = tp1.Pos[1] / tcc.unitsPerTick;
5747  myprt << std::setw(6) << (int)(tp1.Pos[0] + 0.5) << ":" << itick; // W:T
5748  if (itick < 10) { myprt << " "; }
5749  if (itick < 100) { myprt << " "; }
5750  if (itick < 1000) { myprt << " "; }
5751  myprt << std::setw(6) << std::setprecision(2) << tp1.Ang;
5752  myprt << std::setw(2) << tp1.AngleCode;
5753  if (tj.EndFlag[1][kBragg]) { myprt << "B"; }
5754  else if (tj.EndFlag[1][kAtVtx]) {
5755  myprt << "V";
5756  }
5757  else if (tj.EndFlag[1][kAtKink]) {
5758  myprt << "K";
5759  }
5760  else if (tj.EndFlag[1][kAtTj]) {
5761  myprt << "T";
5762  }
5763  else {
5764  myprt << " ";
5765  }
5766  myprt << std::setw(5) << (int)tp1.AveChg;
5767  myprt << std::setw(7) << std::setprecision(1) << tj.TotChg / 1000;
5768  myprt << std::setw(7) << std::setprecision(2) << tj.ChgRMS;
5769  myprt << std::setw(5) << tj.MCSMom;
5770  int vxid = 0;
5771  if (tj.VtxID[0] > 0) vxid = slc.vtxs[tj.VtxID[0] - 1].UID;
5772  myprt << std::setw(4) << vxid;
5773  vxid = 0;
5774  if (tj.VtxID[1] > 0) vxid = slc.vtxs[tj.VtxID[1] - 1].UID;
5775  myprt << std::setw(4) << vxid;
5776  myprt << std::setw(5) << tj.PDGCode;
5777  myprt << std::setw(7) << std::setprecision(2) << ElectronLikelihood(slc, tj);
5778  myprt << std::setw(5) << tj.ParentID;
5779  myprt << std::setw(5) << PrimaryID(slc, tj);
5780  myprt << std::setw(6) << NeutrinoPrimaryTjID(slc, tj);
5781  myprt << std::setw(7) << tj.WorkID;
5782  for (unsigned short ib = 0; ib < AlgBitNames.size(); ++ib)
5783  if (tj.AlgMod[ib]) myprt << " " << AlgBitNames[ib];
5784  for (unsigned short ib = 0; ib < StrategyBitNames.size(); ++ib)
5785  if (tj.Strategy[ib]) myprt << " " << StrategyBitNames[ib];
5786  myprt << "\n";
5787  } // PrintT
5788 
5791  std::string someText,
5792  TCSlice const& slc,
5793  unsigned short itj,
5794  unsigned short ipt,
5795  bool prtVtx)
5796  {
5797 
5798  mf::LogVerbatim myprt("TC");
5799 
5800  if (prtVtx) {
5801  if (!slc.vtx3s.empty()) {
5802  // print out 3D vertices
5803  myprt
5804  << someText
5805  << "****** 3D vertices ******************************************__2DVtx_ID__*******\n";
5806  myprt << someText
5807  << " Vtx Cstat TPC X Y Z XEr YEr ZEr pln0 pln1 pln2 Wire "
5808  "score Prim? Nu? nTru";
5809  myprt << " ___________2D_Pos____________ _____Tjs________\n";
5810  for (unsigned short iv = 0; iv < slc.vtx3s.size(); ++iv) {
5811  if (slc.vtx3s[iv].ID == 0) continue;
5812  const Vtx3Store& vx3 = slc.vtx3s[iv];
5813  myprt << someText;
5814  std::string vid = "3v" + std::to_string(vx3.ID);
5815  myprt << std::right << std::setw(5) << std::fixed << vid;
5816  myprt << std::setprecision(1);
5817  myprt << std::right << std::setw(7) << vx3.TPCID.Cryostat;
5818  myprt << std::right << std::setw(5) << vx3.TPCID.TPC;
5819  myprt << std::right << std::setw(8) << vx3.X;
5820  myprt << std::right << std::setw(8) << vx3.Y;
5821  myprt << std::right << std::setw(8) << vx3.Z;
5822  myprt << std::right << std::setw(5) << vx3.XErr;
5823  myprt << std::right << std::setw(5) << vx3.YErr;
5824  myprt << std::right << std::setw(5) << vx3.ZErr;
5825  myprt << std::right << std::setw(5) << vx3.Vx2ID[0];
5826  myprt << std::right << std::setw(5) << vx3.Vx2ID[1];
5827  myprt << std::right << std::setw(5) << vx3.Vx2ID[2];
5828  myprt << std::right << std::setw(5) << vx3.Wire;
5829  unsigned short nTruMatch = 0;
5830  for (unsigned short ipl = 0; ipl < slc.nPlanes; ++ipl) {
5831  if (vx3.Vx2ID[ipl] == 0) continue;
5832  unsigned short iv2 = vx3.Vx2ID[ipl] - 1;
5833  if (slc.vtxs[iv2].Stat[kVxTruMatch]) ++nTruMatch;
5834  } // ipl
5835  myprt << std::right << std::setw(6) << std::setprecision(1) << vx3.Score;
5836  myprt << std::setw(6) << vx3.Primary;
5837  myprt << std::setw(4) << vx3.Neutrino;
5838  myprt << std::right << std::setw(5) << nTruMatch;
5839  Point2_t pos;
5840  for (unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
5841  PosInPlane(detProp, vx3, plane, pos);
5842  myprt << " " << PrintPos(pos);
5843  } // plane
5844  if (vx3.Wire == -2) {
5845  // find the Tjs that are attached to it
5846  for (auto& pfp : slc.pfps) {
5847  if (pfp.Vx3ID[0] == slc.vtx3s[iv].ID) {
5848  for (auto& tjID : pfp.TjIDs)
5849  myprt << " t" << tjID;
5850  }
5851  if (pfp.Vx3ID[1] == slc.vtx3s[iv].ID) {
5852  for (auto& tjID : pfp.TjIDs)
5853  myprt << " t" << tjID;
5854  }
5855  } // ipfp
5856  }
5857  else {
5858  auto vxtjs = GetAssns(slc, "3V", vx3.ID, "T");
5859  for (auto tjid : vxtjs)
5860  myprt << " t" << tjid;
5861  }
5862  myprt << "\n";
5863  }
5864  } // slc.vtx3s.size
5865  if (!slc.vtxs.empty()) {
5866  bool foundOne = false;
5867  for (unsigned short iv = 0; iv < slc.vtxs.size(); ++iv) {
5868  auto& vx2 = slc.vtxs[iv];
5869  if (debug.Plane < 3 && debug.Plane != (int)DecodeCTP(vx2.CTP).Plane) continue;
5870  if (vx2.NTraj == 0) continue;
5871  foundOne = true;
5872  } // iv
5873  if (foundOne) {
5874  // print out 2D vertices
5875  myprt << someText << "************ 2D vertices ************\n";
5876  myprt << someText
5877  << " ID CTP wire err tick err ChiDOF NTj Pass Topo ChgFrac Score v3D "
5878  "TjIDs\n";
5879  for (auto& vx2 : slc.vtxs) {
5880  if (vx2.ID == 0) continue;
5881  if (debug.Plane < 3 && debug.Plane != (int)DecodeCTP(vx2.CTP).Plane) continue;
5882  myprt << someText;
5883  std::string vid = "2v" + std::to_string(vx2.ID);
5884  myprt << std::right << std::setw(5) << std::fixed << vid;
5885  myprt << std::right << std::setw(6) << vx2.CTP;
5886  myprt << std::right << std::setw(8) << std::setprecision(0)
5887  << std::nearbyint(vx2.Pos[0]);
5888  myprt << std::right << std::setw(5) << std::setprecision(1) << vx2.PosErr[0];
5889  myprt << std::right << std::setw(8) << std::setprecision(0)
5890  << std::nearbyint(vx2.Pos[1] / tcc.unitsPerTick);
5891  myprt << std::right << std::setw(5) << std::setprecision(1)
5892  << vx2.PosErr[1] / tcc.unitsPerTick;
5893  myprt << std::right << std::setw(7) << vx2.ChiDOF;
5894  myprt << std::right << std::setw(5) << vx2.NTraj;
5895  myprt << std::right << std::setw(5) << vx2.Pass;
5896  myprt << std::right << std::setw(6) << vx2.Topo;
5897  myprt << std::right << std::setw(9) << std::setprecision(2) << vx2.TjChgFrac;
5898  myprt << std::right << std::setw(6) << std::setprecision(1) << vx2.Score;
5899  myprt << std::right << std::setw(5) << vx2.Vx3ID;
5900  myprt << " ";
5901  // display the traj IDs
5902  for (unsigned short ii = 0; ii < slc.tjs.size(); ++ii) {
5903  auto const& aTj = slc.tjs[ii];
5904  if (debug.Plane < 3 && debug.Plane != (int)DecodeCTP(aTj.CTP).Plane) continue;
5905  if (aTj.AlgMod[kKilled]) continue;
5906  for (unsigned short end = 0; end < 2; ++end) {
5907  if (aTj.VtxID[end] != (short)vx2.ID) continue;
5908  std::string tid = " t" + std::to_string(aTj.ID) + "_" + std::to_string(end);
5909  myprt << std::right << std::setw(6) << tid;
5910  } // end
5911  } // ii
5912  // Special flags. Ignore the first flag bit (0 = kVxTrjTried) which is done for every vertex
5913  for (unsigned short ib = 1; ib < VtxBitNames.size(); ++ib)
5914  if (vx2.Stat[ib]) myprt << " " << VtxBitNames[ib];
5915  myprt << "\n";
5916  } // iv
5917  }
5918  } // slc.vtxs.size
5919  }
5920 
5921  if (slc.tjs.empty()) {
5922  mf::LogVerbatim("TC") << someText << " No allTraj trajectories to print";
5923  return;
5924  }
5925 
5926  // Print all trajectories in slc.tjs if itj == USHRT_MAX
5927  // Print a single traj (itj) and a single TP (ipt) or all TPs (USHRT_MAX)
5928  if (itj == USHRT_MAX) {
5929  // Print summary trajectory information
5930  myprt << "Tj AngleCode-EndFlag (EF) decoder: <AngleCode> + <reason for stopping>";
5931  myprt << " (B=Bragg Peak, V=Vertex, A=AngleKink, C=ChargeKink, T=Trajectory)\n";
5932  std::vector<unsigned int> tmp;
5933  myprt << someText
5934  << " UID CTP Pass Pts W:T Ang EF AveQ W:T Ang EF AveQ Chg(k) "
5935  "chgRMS Mom SDr __Vtx__ PDG Par Pri NuPar WorkID \n";
5936  for (unsigned short ii = 0; ii < slc.tjs.size(); ++ii) {
5937  auto& aTj = slc.tjs[ii];
5938  if (debug.CTP != UINT_MAX && aTj.CTP != debug.CTP) continue;
5939  myprt << someText << " ";
5940  std::string tid;
5941  if (aTj.AlgMod[kKilled]) { tid = "k" + std::to_string(aTj.UID); }
5942  else {
5943  tid = "t" + std::to_string(aTj.UID);
5944  }
5945  myprt << std::fixed << std::setw(5) << tid;
5946  myprt << std::setw(6) << aTj.CTP;
5947  myprt << std::setw(5) << aTj.Pass;
5948  myprt << std::setw(5) << aTj.EndPt[1] - aTj.EndPt[0] + 1;
5949  unsigned short endPt0 = aTj.EndPt[0];
5950  auto& tp0 = aTj.Pts[endPt0];
5951  int itick = tp0.Pos[1] / tcc.unitsPerTick;
5952  if (itick < 0) itick = 0;
5953  myprt << std::setw(6) << (int)(tp0.Pos[0] + 0.5) << ":" << itick; // W:T
5954  if (itick < 10) { myprt << " "; }
5955  if (itick < 100) { myprt << " "; }
5956  if (itick < 1000) { myprt << " "; }
5957  myprt << std::setw(6) << std::setprecision(2) << tp0.Ang;
5958  myprt << std::setw(2) << tp0.AngleCode;
5959  if (aTj.EndFlag[0][kBragg]) { myprt << "B"; }
5960  else if (aTj.EndFlag[0][kAtVtx]) {
5961  myprt << "V";
5962  }
5963  else if (aTj.EndFlag[0][kAtKink]) {
5964  myprt << "K";
5965  }
5966  else if (aTj.EndFlag[0][kAtTj]) {
5967  myprt << "T";
5968  }
5969  else {
5970  myprt << " ";
5971  }
5972  myprt << std::setw(5) << (int)tp0.AveChg;
5973  unsigned short endPt1 = aTj.EndPt[1];
5974  auto& tp1 = aTj.Pts[endPt1];
5975  itick = tp1.Pos[1] / tcc.unitsPerTick;
5976  myprt << std::setw(6) << (int)(tp1.Pos[0] + 0.5) << ":" << itick; // W:T
5977  if (itick < 10) { myprt << " "; }
5978  if (itick < 100) { myprt << " "; }
5979  if (itick < 1000) { myprt << " "; }
5980  myprt << std::setw(6) << std::setprecision(2) << tp1.Ang;
5981  myprt << std::setw(2) << tp1.AngleCode;
5982  if (aTj.EndFlag[1][kBragg]) { myprt << "B"; }
5983  else if (aTj.EndFlag[1][kAtVtx]) {
5984  myprt << "V";
5985  }
5986  else {
5987  myprt << " ";
5988  }
5989  myprt << std::setw(5) << (int)tp1.AveChg;
5990  myprt << std::setw(7) << std::setprecision(1) << aTj.TotChg / 1000;
5991  myprt << std::setw(7) << std::setprecision(2) << aTj.ChgRMS;
5992  myprt << std::setw(5) << aTj.MCSMom;
5993  myprt << std::setw(4) << aTj.StepDir;
5994  myprt << std::setw(4) << aTj.VtxID[0];
5995  myprt << std::setw(4) << aTj.VtxID[1];
5996  myprt << std::setw(5) << aTj.PDGCode;
5997  myprt << std::setw(5) << aTj.ParentID;
5998  myprt << std::setw(5) << PrimaryID(slc, aTj);
5999  myprt << std::setw(6) << NeutrinoPrimaryTjID(slc, aTj);
6000  myprt << std::setw(7) << aTj.WorkID;
6001  for (unsigned short ib = 0; ib < AlgBitNames.size(); ++ib)
6002  if (aTj.AlgMod[ib]) myprt << " " << AlgBitNames[ib];
6003  myprt << "\n";
6004  } // ii
6005  return;
6006  } // itj > slc.tjs.size()-1
6007 
6008  if (itj > slc.tjs.size() - 1) return;
6009 
6010  auto const& aTj = slc.tjs[itj];
6011 
6012  mf::LogVerbatim("TC") << "Print slc.tjs[" << itj << "] Vtx[0] " << aTj.VtxID[0] << " Vtx[1] "
6013  << aTj.VtxID[1];
6014  myprt << "AlgBits";
6015  for (unsigned short ib = 0; ib < AlgBitNames.size(); ++ib)
6016  if (aTj.AlgMod[ib]) myprt << " " << AlgBitNames[ib];
6017  myprt << "\n";
6018 
6019  PrintTPHeader(someText);
6020  if (ipt == USHRT_MAX) {
6021  // print all points
6022  for (unsigned short ii = 0; ii < aTj.Pts.size(); ++ii)
6023  PrintTP(someText, slc, ii, aTj.StepDir, aTj.Pass, aTj.Pts[ii]);
6024  }
6025  else {
6026  // print just one
6027  PrintTP(someText, slc, ipt, aTj.StepDir, aTj.Pass, aTj.Pts[ipt]);
6028  }
6029  } // PrintAllTraj
6030 
6032  void PrintTrajectory(std::string someText,
6033  const TCSlice& slc,
6034  const Trajectory& tj,
6035  unsigned short tPoint)
6036  {
6037  // prints one or all trajectory points on tj
6038 
6039  if (tPoint == USHRT_MAX) {
6040  if (tj.ID < 0) {
6041  mf::LogVerbatim myprt("TC");
6042  myprt << someText << " ";
6043  myprt << "Work: UID " << tj.UID << " CTP " << tj.CTP << " StepDir " << tj.StepDir
6044  << " PDG " << tj.PDGCode << " slc.vtxs " << tj.VtxID[0] << " " << tj.VtxID[1]
6045  << " nPts " << tj.Pts.size() << " EndPts " << tj.EndPt[0] << " " << tj.EndPt[1];
6046  myprt << " MCSMom " << tj.MCSMom;
6047  myprt << " EndFlags " << PrintEndFlag(tj, 0) << " " << PrintEndFlag(tj, 1);
6048  myprt << " AlgMods:";
6049  for (unsigned short ib = 0; ib < AlgBitNames.size(); ++ib)
6050  if (tj.AlgMod[ib]) myprt << " " << AlgBitNames[ib];
6051  }
6052  else {
6053  mf::LogVerbatim myprt("TC");
6054  myprt << someText << " ";
6055  myprt << "slcID " << slc.ID << " T" << tj.ID << " uT" << tj.UID << " WorkID " << tj.WorkID
6056  << " StepDir " << tj.StepDir << " PDG " << tj.PDGCode << " VtxID " << tj.VtxID[0]
6057  << " " << tj.VtxID[1] << " nPts " << tj.Pts.size() << " EndPts " << tj.EndPt[0] << " "
6058  << tj.EndPt[1];
6059  myprt << " MCSMom " << tj.MCSMom;
6060  myprt << " EndFlags " << PrintEndFlag(tj, 0) << " " << PrintEndFlag(tj, 1);
6061  myprt << " AlgMods:";
6062  for (unsigned short ib = 0; ib < AlgBitNames.size(); ++ib)
6063  if (tj.AlgMod[ib]) myprt << " " << AlgBitNames[ib];
6064  }
6065  PrintTPHeader(someText);
6066  for (unsigned short ipt = 0; ipt < tj.Pts.size(); ++ipt)
6067  PrintTP(someText, slc, ipt, tj.StepDir, tj.Pass, tj.Pts[ipt]);
6068  // See if this trajectory is a shower Tj
6069  if (tj.AlgMod[kShowerTj]) {
6070  for (unsigned short ic = 0; ic < slc.cots.size(); ++ic) {
6071  if (slc.cots[ic].TjIDs.empty()) continue;
6072  // only print out the info for the correct Tj
6073  if (slc.cots[ic].ShowerTjID != tj.ID) continue;
6074  const ShowerStruct& ss = slc.cots[ic];
6075  mf::LogVerbatim myprt("TC");
6076  myprt << "cots index " << ic << " ";
6077  myprt << someText << " Envelope";
6078  if (ss.Envelope.empty()) { myprt << " NA"; }
6079  else {
6080  for (auto& vtx : ss.Envelope)
6081  myprt << " " << (int)vtx[0] << ":" << (int)(vtx[1] / tcc.unitsPerTick);
6082  }
6083  myprt << " Energy " << (int)ss.Energy;
6084  myprt << " Area " << std::fixed << std::setprecision(1) << (int)ss.EnvelopeArea
6085  << " ChgDensity " << ss.ChgDensity;
6086  myprt << "\nInShower TjIDs";
6087  for (auto& tjID : ss.TjIDs) {
6088  myprt << " " << tjID;
6089  } // tjID
6090 
6091  myprt << "\n";
6092  myprt << "NearTjIDs";
6093  for (auto& tjID : ss.NearTjIDs) {
6094  myprt << " " << tjID;
6095  } // tjID
6096  myprt << "\n";
6097  myprt << "\n";
6098  myprt << "Angle " << std::fixed << std::setprecision(2) << ss.Angle << " +/- "
6099  << ss.AngleErr;
6100  myprt << " AspectRatio " << std::fixed << std::setprecision(2) << ss.AspectRatio;
6101  myprt << " DirectionFOM " << std::fixed << std::setprecision(2) << ss.DirectionFOM;
6102  if (ss.ParentID > 0) { myprt << " Parent Tj " << ss.ParentID << " FOM " << ss.ParentFOM; }
6103  else {
6104  myprt << " No parent";
6105  }
6106  myprt << " TruParentID " << ss.TruParentID << " SS3ID " << ss.SS3ID << "\n";
6107  if (ss.NeedsUpdate) myprt << "*********** This shower needs to be updated ***********";
6108  myprt << "................................................";
6109  } // ic
6110  } // Shower Tj
6111  }
6112  else {
6113  // just print one traj point
6114  if (tPoint > tj.Pts.size() - 1) {
6115  mf::LogVerbatim("TC") << "Can't print non-existent traj point " << tPoint;
6116  return;
6117  }
6118  PrintTP(someText, slc, tPoint, tj.StepDir, tj.Pass, tj.Pts[tPoint]);
6119  }
6120  } // PrintTrajectory
6121 
6123  void PrintTPHeader(std::string someText)
6124  {
6125  mf::LogVerbatim("TC") << someText
6126  << " TRP CTP Ind Stp Delta RMS Ang C Err Dir0 Dir1 Q "
6127  " AveQ Pull FitChi NTPF KinkSig Hits ";
6128  } // PrintTPHeader
6129 
6131  void PrintTP(std::string someText,
6132  const TCSlice& slc,
6133  unsigned short ipt,
6134  short dir,
6135  unsigned short pass,
6136  const TrajPoint& tp)
6137  {
6138  mf::LogVerbatim myprt("TC");
6139  myprt << someText << " TRP" << std::fixed;
6140  myprt << pass;
6141  if (dir > 0) { myprt << "+"; }
6142  else {
6143  myprt << "-";
6144  }
6145  myprt << std::setw(6) << tp.CTP;
6146  myprt << std::setw(5) << ipt;
6147  myprt << std::setw(5) << tp.Step;
6148  myprt << std::setw(6) << std::setprecision(2) << tp.Delta;
6149  myprt << std::setw(6) << std::setprecision(2) << tp.DeltaRMS;
6150  myprt << std::setw(6) << std::setprecision(2) << tp.Ang;
6151  myprt << std::setw(2) << tp.AngleCode;
6152  myprt << std::setw(6) << std::setprecision(2) << tp.AngErr;
6153  myprt << std::setw(6) << std::setprecision(2) << tp.Dir[0];
6154  myprt << std::setw(6) << std::setprecision(2) << tp.Dir[1];
6155  myprt << std::setw(7) << (int)tp.Chg;
6156  myprt << std::setw(8) << (int)tp.AveChg;
6157  myprt << std::setw(6) << std::setprecision(1) << tp.ChgPull;
6158  myprt << std::setw(7) << tp.FitChi;
6159  myprt << std::setw(6) << tp.NTPsFit;
6160  myprt << std::setw(7) << std::setprecision(3) << tp.KinkSig;
6161  // print the hits associated with this traj point
6162  if (tp.Hits.size() > 16) {
6163  // don't print too many hits (e.g. from a shower Tj)
6164  myprt << " " << tp.Hits.size() << " shower hits";
6165  }
6166  else {
6167  for (unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
6168  unsigned int iht = tp.Hits[ii];
6169  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
6170  myprt << " " << hit.WireID().Wire << ":" << (int)hit.PeakTime();
6171  if (tp.UseHit[ii]) {
6172  // Distinguish used hits from nearby hits
6173  myprt << "_";
6174  }
6175  else {
6176  myprt << "x";
6177  }
6178  myprt << "T" << slc.slHits[iht].InTraj;
6179  } // iht
6180  if (tp.InPFP > 0) myprt << " inP" << tp.InPFP;
6181  }
6182  // print Environment
6183  if (tp.Environment.any()) myprt << " Env: " << TPEnvString(tp);
6184  } // PrintTP
6185 
6187  std::string TPEnvString(const TrajPoint& tp)
6188  {
6189  // Print environment bits in human-readable format
6190  std::string str = "";
6191  for (unsigned short ib = 0; ib < 8; ++ib) {
6192  // There aren't any bit names for Environment_t
6193  if (!tp.Environment[ib]) continue;
6194  if (ib == kEnvNotGoodWire) str += " NoGdwire";
6195  if (ib == kEnvNearMuon) str += " NearMuon";
6196  if (ib == kEnvNearShower) str += " NearShower";
6197  if (ib == kEnvOverlap) str += " Overlap";
6198  if (ib == kEnvUnusedHits) str += " UnusedHits";
6199  if (ib == kEnvNearSrcHit) str += " NearSrcHit";
6200  if (ib == kEnvFlag) str += " Flag";
6201  } // ib
6202  return str;
6203  } // TPEnvironment
6204 
6206  void PrintPFP(std::string someText, TCSlice const& slc, const PFPStruct& pfp, bool printHeader)
6207  {
6208  mf::LogVerbatim myprt("TC");
6209  if (printHeader) {
6210  myprt << someText;
6211  myprt << " PFP sVx ________sPos_______ EF _______sDir______ ____sdEdx_____ eVx "
6212  "________ePos_______ EF _______eDir______ ____edEdx____ Len nTp3 MCSMom ShLike? "
6213  "PDG Par Prim\n";
6214  }
6215  myprt << someText;
6216  std::string pid = "P" + std::to_string(pfp.ID);
6217  myprt << std::setw(5) << pid;
6218  // start and end stuff
6219  for (unsigned short end = 0; end < 2; ++end) {
6220  myprt << std::setw(4) << pfp.Vx3ID[end];
6221  myprt << std::fixed << std::right << std::setprecision(1);
6222  auto pos = PosAtEnd(pfp, end);
6223  myprt << std::setw(7) << pos[0];
6224  myprt << std::setw(7) << pos[1];
6225  myprt << std::setw(7) << pos[2];
6226  // print characters that encode the EndFlag
6227  std::string ef;
6228  if (pfp.EndFlag[end][kOutFV]) { ef = "O"; }
6229  else {
6230  ef = "I";
6231  }
6232  if (pfp.EndFlag[end][kBragg]) ef += "B";
6233  myprt << std::setw(6) << ef;
6234  myprt << std::fixed << std::right << std::setprecision(2);
6235  auto dir = DirAtEnd(pfp, end);
6236  myprt << std::setw(6) << dir[0];
6237  myprt << std::setw(6) << dir[1];
6238  myprt << std::setw(6) << dir[2];
6239  for (auto& dedx : pfp.dEdx[end]) {
6240  if (dedx < 50) { myprt << std::setw(5) << std::setprecision(1) << dedx; }
6241  else {
6242  myprt << std::setw(5) << std::setprecision(0) << dedx;
6243  }
6244  } // dedx
6245  if (pfp.dEdx[end].size() < 3) {
6246  for (size_t i = 0; i < 3 - pfp.dEdx[end].size(); ++i) {
6247  myprt << std::setw(6) << ' ';
6248  }
6249  }
6250  } // startend
6251  // global stuff
6252  float length = Length(pfp);
6253  if (length < 100) { myprt << std::setw(5) << std::setprecision(1) << length; }
6254  else {
6255  myprt << std::setw(5) << std::setprecision(0) << length;
6256  }
6257  myprt << std::setw(5) << std::setprecision(2) << pfp.TP3Ds.size();
6258  myprt << std::setw(7) << MCSMom(slc, pfp.TjIDs);
6259  myprt << std::setw(5) << IsShowerLike(slc, pfp.TjIDs);
6260  myprt << std::setw(5) << pfp.PDGCode;
6261  myprt << " NA";
6262  myprt << std::setw(4) << pfp.ParentUID;
6263  myprt << std::setw(5) << PrimaryUID(pfp);
6264  if (!pfp.TjIDs.empty()) {
6265  for (auto& tjID : pfp.TjIDs)
6266  myprt << " T" << tjID;
6267  }
6268  if (!pfp.DtrUIDs.empty()) {
6269  myprt << " dtrs";
6270  for (auto& dtrUID : pfp.DtrUIDs)
6271  myprt << " P" << dtrUID;
6272  }
6273  } // PrintPFP
6274 
6276  void PrintPFPs(std::string someText, TCSlice const& slc)
6277  {
6278  if (slc.pfps.empty()) return;
6279 
6280  mf::LogVerbatim myprt("TC");
6281  myprt << someText;
6282  myprt
6283  << " PFP sVx ________sPos_______ ______sDir______ ______sdEdx_____ eVx "
6284  "________ePos_______ ______eDir______ ______edEdx_____ BstPln PDG TruPDG Par Prim E*P\n";
6285  bool printHeader = true;
6286  for (auto& pfp : slc.pfps) {
6287  PrintPFP(someText, slc, pfp, printHeader);
6288  printHeader = false;
6289  } // im
6290 
6291  } // PrintPFPs
6292 
6294  std::string PrintEndFlag(const PFPStruct& pfp, unsigned short end)
6295  {
6296  if (end > 1) return "Invalid end";
6297  std::string tmp;
6298  bool first = true;
6299  for (unsigned short ib = 0; ib < EndFlagNames.size(); ++ib) {
6300  if (pfp.EndFlag[end][ib]) {
6301  if (first) {
6302  tmp = std::to_string(end) + ":" + EndFlagNames[ib];
6303  first = false;
6304  }
6305  else {
6306  tmp += "," + EndFlagNames[ib];
6307  }
6308  }
6309  } // ib
6310  if (first) tmp = " none";
6311  return tmp;
6312  } // PrintEndFlag
6313 
6315  std::string PrintEndFlag(const Trajectory& tj, unsigned short end)
6316  {
6317  if (end > 1) return "Invalid end";
6318  std::string tmp;
6319  bool first = true;
6320  for (unsigned short ib = 0; ib < EndFlagNames.size(); ++ib) {
6321  if (tj.EndFlag[end][ib]) {
6322  if (first) {
6323  tmp = std::to_string(end) + ":" + EndFlagNames[ib];
6324  first = false;
6325  }
6326  else {
6327  tmp += "," + EndFlagNames[ib];
6328  }
6329  }
6330  } // ib
6331  return tmp;
6332  } // PrintEndFlag
6333 
6335  std::string PrintHitShort(const TCHit& tch)
6336  {
6337  if (tch.allHitsIndex > (*evt.allHits).size() - 1) return "NA";
6338  auto& hit = (*evt.allHits)[tch.allHitsIndex];
6339  return std::to_string(hit.WireID().Plane) + ":" + std::to_string(hit.WireID().Wire) + ":" +
6340  std::to_string((int)hit.PeakTime());
6341  } // PrintHit
6342 
6344  std::string PrintHit(const TCHit& tch)
6345  {
6346  if (tch.allHitsIndex > (*evt.allHits).size() - 1) return "NA";
6347  auto& hit = (*evt.allHits)[tch.allHitsIndex];
6348  return std::to_string(hit.WireID().Plane) + ":" + std::to_string(hit.WireID().Wire) + ":" +
6349  std::to_string((int)hit.PeakTime()) + "_" + std::to_string(tch.InTraj);
6350  } // PrintHit
6351 
6353  std::string PrintPos(const TrajPoint& tp)
6354  {
6355  return std::to_string(DecodeCTP(tp.CTP).Plane) + ":" + PrintPos(tp.Pos);
6356  } // PrintPos
6357 
6359  std::string PrintPos(const Point2_t& pos)
6360  {
6361  unsigned int wire = 0;
6362  if (pos[0] > -0.4) wire = std::nearbyint(pos[0]);
6363  int time = std::nearbyint(pos[1] / tcc.unitsPerTick);
6364  return std::to_string(wire) + ":" + std::to_string(time);
6365  } // PrintPos
6366 
6367 } // namespace tca
Expect tracks entering from the front face. Don&#39;t create neutrino PFParticles.
Definition: DataStructs.h:525
Float_t x
Definition: compare.C:6
std::bitset< 16 > UseHit
Definition: DataStructs.h:170
void PrintAll(detinfo::DetectorPropertiesData const &detProp, std::string someText)
Definition: Utils.cxx:5367
Vector2_t Dir
Definition: DataStructs.h:153
float HitsPosTime(const TCSlice &slc, const std::vector< unsigned int > &hitsInMultiplet, float &sum, HitStatus_t hitRequest)
Definition: Utils.cxx:4163
void CheckTrajBeginChg(TCSlice &slc, unsigned short itj)
Definition: Utils.cxx:1316
float AveChg
Calculated using ALL hits.
Definition: DataStructs.h:194
details::range_type< T > Iterate() const
Initializes the specified ID with the ID of the first cryostat.
Definition: GeometryCore.h:541
Point2_t Pos
Definition: DataStructs.h:152
short MCSMom(const TCSlice &slc, const std::vector< int > &tjIDs)
Definition: Utils.cxx:3380
Point2_t PosErr
Definition: DataStructs.h:71
MaybeLogger_< ELseverityLevel::ELsev_info, true > LogVerbatim
std::vector< Trajectory > tjs
vector of all trajectories in each plane
Definition: DataStructs.h:663
void FitTraj(TCSlice const &slc, Trajectory &tj)
Definition: Utils.cxx:797
Point2_t dEdx
dE/dx for 3D matched trajectories
Definition: DataStructs.h:198
float Length(const PFPStruct &pfp)
Definition: PFPUtils.cxx:3269
bool dbgStitch
debug PFParticle stitching
Definition: DataStructs.h:596
Length_t WireCoordinate(Point_t const &pos, PlaneID const &planeid) const
Returns the index of the nearest wire to the specified position.
void SetEndPoints(Trajectory &tj)
Definition: Utils.cxx:3329
bool TrajHitsOK(TCSlice const &slc, const std::vector< unsigned int > &iHitsInMultiplet, const std::vector< unsigned int > &jHitsInMultiplet)
Definition: Utils.cxx:1838
bool MakeVertexObsolete(std::string fcnLabel, TCSlice &slc, VtxStore &vx2, bool forceKill)
Definition: TCVertex.cxx:2690
std::vector< float > kinkCuts
kink finder algorithm
Definition: DataStructs.h:552
constexpr auto const & right(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:102
Double_t xx
Definition: macro.C:12
geo::TPCID TPCID
Definition: DataStructs.h:614
float ParSlpErr
Definition: DataStructs.h:181
bool InTrajOK(TCSlice &slc, std::string someText)
Definition: Utils.cxx:1256
Point2_t Pos
Definition: DataStructs.h:176
bool InsideFV(const TCSlice &slc, const PFPStruct &pfp, unsigned short end)
Definition: PFPUtils.cxx:3022
struct of temporary 2D vertices (end points)
Definition: DataStructs.h:69
std::vector< unsigned int > PutTrajHitsInVector(const Trajectory &tj, HitStatus_t hitRequest)
Definition: Utils.cxx:2688
const std::vector< std::string > AlgBitNames
Definition: DataStructs.cxx:16
std::vector< ShowerStruct > cots
Definition: DataStructs.h:672
bool AttachAnyVertexToTraj(TCSlice &slc, int tjID, bool prt)
Definition: TCVertex.cxx:1629
CTP_t CTP
Cryostat, TPC, Plane code.
Definition: DataStructs.h:150
std::vector< float > maxPos0
Definition: DataStructs.h:565
short recoTPC
only reconstruct in the seleted TPC
Definition: DataStructs.h:582
unsigned short NTPsFit
Definition: DataStructs.h:166
std::vector< int > NearTjIDs
Definition: DataStructs.h:319
int UID
unique global ID
Definition: DataStructs.h:360
std::array< double, 3 > Point3_t
Definition: DataStructs.h:41
std::vector< ShowerStruct3D > showers
Definition: DataStructs.h:675
bool SignalAtTp(TrajPoint &tp)
Definition: Utils.cxx:1963
void SetPDGCode(TCSlice &slc, unsigned short itj)
Definition: Utils.cxx:4217
std::vector< Point2_t > Envelope
Definition: DataStructs.h:325
tagged as a vertex between Tjs that are matched to MC truth neutrino interaction particles ...
Definition: DataStructs.h:93
TCConfig tcc
Definition: DataStructs.cxx:9
unsigned short Step
Definition: DataStructs.h:167
unsigned int Nplanes() const
Number of planes in this tpc.
Definition: TPCGeo.h:137
Float_t den
Definition: plot.C:35
vertex position fixed manually - no fitting done
Definition: DataStructs.h:90
Declaration of signal hit object.
void FindAlongTrans(Point3_t pos1, Vector3_t dir1, Point3_t pos2, Point2_t &alongTrans)
Definition: PFPUtils.cxx:3069
void PrintTrajectory(std::string someText, const TCSlice &slc, const Trajectory &tj, unsigned short tPoint)
Definition: Utils.cxx:6032
Float_t y
Definition: compare.C:6
bool StartTraj(TCSlice const &slc, Trajectory &tj, unsigned int fromhit, unsigned int tohit, unsigned short pass)
Definition: Utils.cxx:4855
void PrintTP(std::string someText, const TCSlice &slc, unsigned short ipt, short dir, unsigned short pass, const TrajPoint &tp)
Definition: Utils.cxx:6131
std::vector< int > Vx2ID
Definition: DataStructs.h:111
std::vector< std::vector< std::pair< unsigned int, unsigned int > > > wireHitRange
Definition: DataStructs.h:668
The data type to uniquely identify a Plane.
Definition: geo_types.h:463
void UnsetUsedHits(TCSlice &slc, TrajPoint &tp)
Definition: Utils.cxx:1060
int ParentID
ID of the parent, or the ID of the Tj this one was merged with if it is killed.
Definition: DataStructs.h:192
std::vector< unsigned int > Hits
Definition: DataStructs.h:169
std::string PrintEndFlag(const PFPStruct &pfp, unsigned short end)
Definition: Utils.cxx:6294
std::vector< int > TjIDs
Definition: DataStructs.h:318
bool StoreTraj(TCSlice &slc, Trajectory &tj)
Definition: Utils.cxx:1073
float TotChg
Total including an estimate for dead wires.
Definition: DataStructs.h:195
double Temperature() const
In kelvin.
short MCSMom
Normalized RMS using ALL hits. Assume it is 50% to start.
Definition: DataStructs.h:197
bool IsShowerLike(TCSlice const &slc, std::vector< int > const &TjIDs)
Definition: TCShower.cxx:1891
float TpSumHitChg(const TCSlice &slc, TrajPoint const &tp)
Definition: Utils.cxx:2060
constexpr auto abs(T v)
Returns the absolute value of the argument.
unsigned short Pass
Definition: DataStructs.h:73
bool dbgDeltaRayTag
Definition: DataStructs.h:592
void ChkChgAsymmetry(TCSlice &slc, Trajectory &tj, bool prt)
Definition: Utils.cxx:1713
CryostatID_t Cryostat
Index of cryostat.
Definition: geo_types.h:211
for(Int_t i=0;i< nentries;i++)
Definition: comparison.C:30
bool TrajClosestApproach(Trajectory const &tj, float x, float y, unsigned short &closePt, float &DOCA)
Definition: Utils.cxx:2621
void FillWireHitRange(geo::TPCID inTPCID)
Definition: Utils.cxx:4323
float TrajPointSeparation(const TrajPoint &tp1, const TrajPoint &tp2)
Definition: Utils.cxx:2612
std::vector< std::pair< unsigned int, unsigned int > > tpcSrcHitRange
Definition: DataStructs.h:621
bool CompatibleMerge(const TCSlice &slc, std::vector< int > const &tjIDs, bool prt)
Definition: Utils.cxx:576
void SetAngleCode(TrajPoint &tp)
Definition: Utils.cxx:764
PFPStruct CreatePFP(const TCSlice &slc)
Definition: PFPUtils.cxx:2805
Float_t tmp
Definition: plot.C:35
Point3_t PosAtEnd(const PFPStruct &pfp, unsigned short end)
Definition: PFPUtils.cxx:3259
float PointTrajDOCA(const TCSlice &slc, unsigned int iht, TrajPoint const &tp)
Definition: Utils.cxx:2513
double DeltaAngle(const Vector3_t v1, const Vector3_t v2)
Definition: PFPUtils.cxx:2535
a general purpose flag bit used in 3D matching
Definition: DataStructs.h:519
bool dbgSlc
debug only in the user-defined slice? default is all slices
Definition: DataStructs.h:583
bool MakeBareTrajPoint(const TCSlice &slc, unsigned int fromHit, unsigned int toHit, TrajPoint &tp)
Definition: Utils.cxx:4006
void Print2V(mf::LogVerbatim &myprt, VtxStore const &vx2, bool &printHeader)
Definition: Utils.cxx:5610
std::vector< unsigned int > lastWire
the last wire with a hit
Definition: DataStructs.h:648
HitStatus_t
Definition: Utils.h:42
float GoodnessOfFit() const
Degrees of freedom in the determination of the hit signal shape (-1 by default)
Definition: Hit.h:260
bool LongPulseHit(const recob::Hit &hit)
Definition: Utils.cxx:4315
Particle class.
std::array< int, 2 > Vx3ID
Definition: DataStructs.h:287
float AvePar
Definition: DataStructs.h:178
bool expectSlicedHits
info passed from the module - used to (not) define wireHitRange
Definition: DataStructs.h:641
void PrintPFPs(std::string someText, TCSlice const &slc)
Definition: Utils.cxx:6276
short int Multiplicity() const
How many hits could this one be shared with.
Definition: Hit.h:252
float TPHitsRMSTime(const TCSlice &slc, const TrajPoint &tp, HitStatus_t hitRequest)
Definition: Utils.cxx:4090
unsigned int MVI
MatchVec Index for detailed 3D matching.
Definition: DebugStruct.h:29
std::string TPEnvString(const TrajPoint &tp)
Definition: Utils.cxx:6187
pure virtual base interface for detector clocks
std::vector< int > CotIDs
Definition: DataStructs.h:356
std::string PrintPos(const TrajPoint &tp)
Definition: Utils.cxx:6353
bool StoreVertex(TCSlice &slc, VtxStore &vx)
Definition: TCVertex.cxx:1900
float MaxTjLen(const TCSlice &slc, std::vector< int > &tjIDs)
Definition: Utils.cxx:2566
bool MergeTjIntoPFP(TCSlice &slc, int mtjid, PFPStruct &pfp, bool prt)
Definition: Utils.cxx:511
void Print3S(detinfo::DetectorPropertiesData const &detProp, mf::LogVerbatim &myprt, ShowerStruct3D const &ss3)
Definition: Utils.cxx:5660
double DeltaAngle2(double Ang1, double Ang2)
Definition: Utils.cxx:3311
std::vector< T > SetIntersection(const std::vector< T > &set1, const std::vector< T > &set2)
Definition: Utils.h:385
std::vector< float > angleRanges
list of max angles for each angle range
Definition: DataStructs.h:562
const std::vector< std::string > EndFlagNames
Definition: DataStructs.cxx:87
std::vector< float > showerTag
shower-like trajectory tagging + shower reconstruction
Definition: DataStructs.h:551
unsigned short Pass
the pass on which it was created
Definition: DataStructs.h:206
bool dbg3V
debug 3D vertex finding
Definition: DataStructs.h:590
double Efield(unsigned int planegap=0) const
kV/cm
bool SignalBetween(const TrajPoint &tp1, const TrajPoint &tp2, const float MinWireSignalFraction)
Definition: Utils.cxx:1778
float MaxChargeAsymmetry(TCSlice &slc, std::vector< int > const &tjIDs)
Definition: Utils.cxx:377
TPCGeo const & TPC(TPCID const &tpcid=tpc_zero) const
Returns the specified TPC.
Definition: GeometryCore.h:722
float OverlapFraction(const Trajectory &tj1, const Trajectory &tj2)
Definition: Utils.cxx:706
std::string PrintHitShort(const TCHit &tch)
Definition: Utils.cxx:6335
decltype(auto) constexpr end(T &&obj)
ADL-aware version of std::end.
Definition: StdUtils.h:77
decltype(auto) constexpr size(T &&obj)
ADL-aware version of std::size.
Definition: StdUtils.h:101
float HitsRMSTime(const TCSlice &slc, const std::vector< unsigned int > &hitsInMultiplet, HitStatus_t hitRequest)
Definition: Utils.cxx:4123
Access the description of detector geometry.
unsigned int Hit
set to the hit index in evt.allHits if a Plane:Wire:Tick match is found
Definition: DebugStruct.h:26
std::vector< int > TjUIDs
Definition: DataStructs.h:281
void FitPar(const Trajectory &tj, unsigned short originPt, unsigned short npts, short fitDir, ParFit &pFit, unsigned short usePar)
Definition: Utils.cxx:1201
std::vector< unsigned int > PutHitsInVector(const TCSlice &slc, PFPStruct const &pfp, HitStatus_t hitRequest)
Definition: Utils.cxx:2664
const std::vector< std::string > StrategyBitNames
Definition: DataStructs.cxx:98
float HitSep2(const TCSlice &slc, unsigned int iht, unsigned int jht)
Definition: Utils.cxx:2482
int Cryostat
Select Cryostat.
Definition: DebugStruct.h:20
int NeutrinoPrimaryTjID(const TCSlice &slc, const Trajectory &tj)
Definition: Utils.cxx:445
float HitsRMSTick(const TCSlice &slc, const std::vector< unsigned int > &hitsInMultiplet, HitStatus_t hitRequest)
Definition: Utils.cxx:4131
struct of temporary 3D vertices
Definition: DataStructs.h:101
short nPtsAve
dump trajectory points
Definition: DataStructs.h:599
if(nlines<=0)
int PrimaryID(const TCSlice &slc, const Trajectory &tj)
Definition: Utils.cxx:467
TCanvas * c1
Definition: plotHisto.C:7
unsigned short NearestPtWithChg(const Trajectory &tj, unsigned short thePt)
Definition: Utils.cxx:3434
bool dbgStp
debug stepping using debug.Cryostat, debug.TPC, etc
Definition: DataStructs.h:584
int Wire
Select hit Wire for debugging.
Definition: DebugStruct.h:24
TCanvas * c2
Definition: plot_hist.C:75
std::array< float, 2 > Point2_t
Definition: DataStructs.h:43
std::vector< float > maxPos1
Definition: DataStructs.h:566
int PDGCodeVote(detinfo::DetectorClocksData const &clockData, detinfo::DetectorPropertiesData const &detProp, const TCSlice &slc, PFPStruct &pfp)
Definition: PFPUtils.cxx:3314
float unitsPerTick
scale factor from Tick to WSE equivalent units
Definition: DataStructs.h:564
IDparameter< geo::WireID > WireID
Member type of validated geo::WireID parameter.
int WorkID
Select the StartWorkID for debugging.
Definition: DebugStruct.h:28
float DeadWireCount(const TCSlice &slc, const TrajPoint &tp1, const TrajPoint &tp2)
Definition: Utils.cxx:2094
bool BraggSplit(TCSlice &slc, unsigned short itj)
Definition: Utils.cxx:1422
void DefineHitPos(TCSlice &slc, TrajPoint &tp)
Definition: StepUtils.cxx:1791
geo::TPCID TPCID
Definition: DataStructs.h:110
DebugStuff debug
Definition: DebugStruct.cxx:4
Vector3_t PointDirection(const Point3_t p1, const Point3_t p2)
Definition: PFPUtils.cxx:2542
float PointTrajDOCA2(float wire, float time, TrajPoint const &tp)
Definition: Utils.cxx:2529
TP is near a hit in the srcHit collection but no allHit hit exists (DUNE disambiguation error) ...
Definition: DataStructs.h:518
CTP_t CTP
Cryostat, TPC, Plane code.
Definition: DataStructs.h:189
decltype(auto) constexpr to_string(T &&obj)
ADL-aware version of std::to_string.
Float_t E
Definition: plot.C:20
void DumpTj()
Definition: Utils.cxx:5248
int UID
unique global ID
Definition: DataStructs.h:113
bool MergeShowerTjsAndStore(TCSlice &slc, unsigned short istj, unsigned short jstj, bool prt)
Definition: TCShower.cxx:2978
bool dbg2V
debug 2D vertex finding
Definition: DataStructs.h:586
std::vector< float > aveHitRMS
average RMS of an isolated hit
Definition: DataStructs.h:631
std::vector< TrajPoint > Pts
Trajectory points.
Definition: DataStructs.h:188
float ElectronLikelihood(const TCSlice &slc, const Trajectory &tj)
Definition: Utils.cxx:3143
float TwoTPAngle(const TrajPoint &tp1, const TrajPoint &tp2)
Definition: Utils.cxx:2655
void TjDeltaRMS(const Trajectory &tj, unsigned short firstPt, unsigned short lastPt, double &rms, unsigned short &cnt)
Definition: Utils.cxx:3490
std::vector< std::vector< bool > > goodWire
Definition: DataStructs.h:623
int Plane
Select plane.
Definition: DebugStruct.h:22
float ChgFracNearPos(const TCSlice &slc, const Point2_t &pos, const std::vector< int > &tjIDs)
Definition: Utils.cxx:3162
Float_t sn
Definition: plot.C:23
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:2773
float ChgFracBetween(detinfo::DetectorPropertiesData const &detProp, const TCSlice &slc, Point3_t pos1, Point3_t pos2)
Definition: PFPUtils.cxx:3166
void MakeHaloTj(TCSlice &slc, Trajectory &muTj, bool prt)
Definition: Utils.cxx:52
TrajPoint MakeBareTP(detinfo::DetectorPropertiesData const &detProp, const Point3_t &pos, CTP_t inCTP)
Definition: Utils.cxx:3927
void PrintT(mf::LogVerbatim &myprt, Trajectory const &tj, bool &printHeader)
Definition: Utils.cxx:5699
std::vector< VtxStore > vtxs
2D vertices
Definition: DataStructs.h:669
std::array< unsigned short, 2 > EndPt
First and last point in the trajectory that has charge.
Definition: DataStructs.h:200
unsigned short PDGCode
shower-like or track-like {default is track-like}
Definition: DataStructs.h:205
double ConvertXToTicks(double X, int p, int t, int c) const
bool PointInsideEnvelope(const Point2_t &Point, const std::vector< Point2_t > &Envelope)
Definition: Utils.cxx:3245
std::vector< float > match3DCuts
3D matching cuts
Definition: DataStructs.h:553
std::vector< SectionFit > SectionFits
Definition: DataStructs.h:283
void MakeTrajectoryObsolete(TCSlice &slc, unsigned int itj)
Definition: Utils.cxx:2137
short StartEnd
The starting end (-1 = don&#39;t know)
Definition: DataStructs.h:208
double PosSep2(const Point3_t &pos1, const Point3_t &pos2)
Definition: PFPUtils.cxx:2564
unsigned short GetPFPIndex(const TCSlice &slc, int tjID)
Definition: Utils.cxx:1037
std::string PrintHit(const TCHit &tch)
Definition: Utils.cxx:6344
Point2_t Pos
Definition: DataStructs.h:70
bool Fit2D(short mode, Point2_t inPt, float &inPtErr, Vector2_t &outVec, Vector2_t &outVecErr, float &chiDOF)
Definition: Utils.cxx:4979
bool SplitTraj(detinfo::DetectorPropertiesData const &detProp, TCSlice &slc, unsigned short itj, float XPos, bool makeVx2, bool prt)
Definition: Utils.cxx:2222
const geo::GeometryCore * geom
Definition: DataStructs.h:569
double DriftVelocity(double efield=0., double temperature=0.) const
cm/us
int UID
a unique ID for all slices
Definition: DataStructs.h:203
The data type to uniquely identify a TPC.
Definition: geo_types.h:381
PlaneID_t Plane
Index of the plane within its TPC.
Definition: geo_types.h:481
void PrintP(mf::LogVerbatim &myprt, PFPStruct const &pfp, bool &printHeader)
Definition: Utils.cxx:5450
float ExpectedHitsRMS(const TrajPoint &tp)
Definition: Utils.cxx:1910
bool valsIncreasing(const SortEntry &c1, const SortEntry &c2)
Definition: Utils.cxx:42
void ReleaseHits(TCSlice &slc, Trajectory const &tj)
Definition: Utils.cxx:1048
bool SetMag(Vector3_t &v1, double mag)
Definition: PFPUtils.cxx:2574
std::array< double, 2 > Vector2_t
Definition: DataStructs.h:44
Definition of data types for geometry description.
unsigned short NumHitsInTP(const TrajPoint &tp, HitStatus_t hitRequest)
Definition: Utils.cxx:4196
void TrimEndPts(std::string fcnLabel, TCSlice &slc, Trajectory &tj, const std::vector< float > &fQualityCuts, bool prt)
Definition: Utils.cxx:1575
unsigned short FarEnd(const PFPStruct &pfp, const Point3_t &pos)
Definition: PFPUtils.cxx:3302
void DefineTjParents(TCSlice &slc, bool prt)
Definition: Utils.cxx:172
float HitsPosTick(const TCSlice &slc, const std::vector< unsigned int > &hitsInMultiplet, float &sum, HitStatus_t hitRequest)
Definition: Utils.cxx:4172
std::vector< unsigned int > firstWire
the first wire with a hit
Definition: DataStructs.h:647
float ChiDOF
Definition: DataStructs.h:182
int ID
ID that is local to one slice.
Definition: DataStructs.h:202
std::vector< TCSlice > slices
Definition: DataStructs.cxx:13
std::array< unsigned short, 2 > VtxID
ID of 2D vertex.
Definition: DataStructs.h:199
Detector simulation of raw signals on wires.
float MCSThetaRMS(const Trajectory &tj)
Definition: Utils.cxx:3452
std::bitset< 16 > modes
number of points to find AveChg
Definition: DataStructs.h:600
std::vector< TCHit > slHits
Definition: DataStructs.h:662
unsigned short NearbyCleanPt(const Trajectory &tj, unsigned short end)
Definition: Utils.cxx:2887
bool MergeAndStore(TCSlice &slc, unsigned int itj1, unsigned int itj2, bool doPrt)
Definition: Utils.cxx:4518
void RestoreObsoleteTrajectory(TCSlice &slc, unsigned int itj)
Definition: Utils.cxx:2149
bool aveHitRMSValid
set true when the average hit RMS is well-known
Definition: DataStructs.h:640
std::bitset< 16 > Stat
Vertex status bits using kVtxBit_t.
Definition: DataStructs.h:85
int ID
set to 0 if killed
Definition: DataStructs.h:80
float ParSlp
Definition: DataStructs.h:180
void PrintPFP(std::string someText, TCSlice const &slc, const PFPStruct &pfp, bool printHeader)
Definition: Utils.cxx:6206
unsigned short AngleCode
Definition: DataStructs.h:168
ROOT::Math::PositionVector3D< ROOT::Math::Cartesian3D< double >, ROOT::Math::GlobalCoordinateSystemTag > Point_t
Type for representation of position in physical 3D space.
Definition: geo_vectors.h:180
std::vector< int > GetAssns(TCSlice const &slc, std::string type1Name, int id, std::string type2Name)
Definition: Utils.cxx:4701
int Tick
Select hit PeakTime for debugging (< 0 for vertex finding)
Definition: DebugStruct.h:25
void TrajIntersection(TrajPoint const &tp1, TrajPoint const &tp2, Point2_t &pos)
Definition: Utils.cxx:2542
int TPC
Select TPC.
Definition: DebugStruct.h:21
raw::ChannelID_t PlaneWireToChannel(WireID const &wireid) const
Returns the ID of the TPC channel connected to the specified wire.
const std::vector< std::string > VtxBitNames
Definition: DataStructs.cxx:89
int PrimaryUID(const PFPStruct &pfp)
Definition: Utils.cxx:486
double DotProd(const Vector3_t &v1, const Vector3_t &v2)
Definition: PFPUtils.h:126
unsigned int CTP_t
Definition: DataStructs.h:47
void UpdateTjChgProperties(std::string inFcnLabel, TCSlice const &slc, Trajectory &tj, bool prt)
Definition: Utils.cxx:3576
std::vector< Vtx3Store > vtx3s
3D vertices
Definition: DataStructs.h:670
float KinkSignificance(TCSlice const &slc, Trajectory const &tj1, unsigned short end1, Trajectory const &tj2, unsigned short end2, unsigned short nPtsFit, bool useChg, bool prt)
Definition: Utils.cxx:2985
void Print3V(detinfo::DetectorPropertiesData const &detProp, mf::LogVerbatim &myprt, Vtx3Store const &vx3, bool &printHeader)
Definition: Utils.cxx:5535
geo::TPCID TPCID
Definition: DataStructs.h:655
bool DecodeDebugString(std::string strng)
Definition: Utils.cxx:5065
std::vector< recob::Hit > const * srcHits
Definition: DataStructs.h:616
std::bitset< 128 > useAlg
Max hit separation for making junk trajectories. < 0 to turn off.
Definition: DataStructs.h:579
Float_t norm
bool AnalyzeHits()
Definition: Utils.cxx:4259
in close()
Contains all timing reference information for the detector.
TDirectory * dir
Definition: macro.C:5
float ParErr
Definition: DataStructs.h:179
short StepDir
-1 = going US (-> small wire#), 1 = going DS (-> large wire#)
Definition: DataStructs.h:207
float MaxHitDelta(TCSlice &slc, Trajectory &tj)
Definition: Utils.cxx:3201
float TrajLength(const Trajectory &tj)
Definition: Utils.cxx:2581
std::bitset< 128 > AlgMod
Bit set if algorithm AlgBit_t modifed the trajectory.
Definition: DataStructs.h:190
std::vector< short > muonTag
Definition: DataStructs.h:548
void UpdateVxEnvironment(TCSlice &slc)
Definition: Utils.cxx:3764
unsigned short NTraj
Definition: DataStructs.h:72
std::tuple< double, double, const reco::ClusterHit3D * > Point
Definitions used by the VoronoiDiagram algorithm.
Definition: DCEL.h:42
geo::PlaneID DecodeCTP(CTP_t CTP)
void TagJunkTj(Trajectory &tj, bool prt)
Definition: Utils.cxx:2715
View_t View(PlaneID const &pid) const
Returns the view (wire orientation) on the channels of specified TPC plane.
std::vector< int > GetVtxTjIDs(const TCSlice &slc, const VtxStore &vx2)
Definition: TCVertex.cxx:2799
double PosSep(const Point3_t &pos1, const Point3_t &pos2)
Definition: PFPUtils.cxx:2558
std::vector< int > TjIDs
Definition: DataStructs.h:280
MaybeLogger_< ELseverityLevel::ELsev_warning, false > LogWarning
void TrimHiChgEndPts(TCSlice &slc, Trajectory &tj, bool prt)
Definition: Utils.cxx:1533
std::bitset< 128 > dbgAlg
Allow user to turn on debug printing in algorithms (that print...)
Definition: DataStructs.h:580
unsigned short PDGCodeIndex(int PDGCode)
Definition: Utils.cxx:2125
float PointPull(const TP3D &tp3d)
Definition: PFPUtils.cxx:2797
std::bitset< 8 > Environment
Definition: DataStructs.h:171
std::vector< recob::Hit > const * allHits
Definition: DataStructs.h:615
bool valsDecreasing(const SortEntry &c1, const SortEntry &c2)
Definition: Utils.cxx:38
CTP_t EncodeCTP(unsigned int cryo, unsigned int tpc, unsigned int plane)
Definition: DataStructs.h:51
unsigned int Nwires(PlaneID const &planeid) const
Returns the total number of wires in the specified plane.
std::pair< unsigned short, unsigned short > GetSliceIndex(std::string typeName, int uID)
Definition: Utils.cxx:4939
unsigned short MVI_Iter
MVI iteration - see FindPFParticles.
Definition: DebugStruct.h:30
span(IterB &&b, IterE &&e, Adaptor &&adaptor) -> span< decltype(adaptor(std::forward< IterB >(b))), decltype(adaptor(std::forward< IterE >(e))) >
std::vector< unsigned int > nWires
Definition: DataStructs.h:646
use the stiff electron strategy
Definition: DataStructs.h:494
std::array< std::bitset< 8 >, 2 > EndFlag
Definition: DataStructs.h:209
float PointTrajSep2(float wire, float time, TrajPoint const &tp)
Definition: Utils.cxx:2505
std::array< double, 3 > Vector3_t
Definition: DataStructs.h:42
std::vector< float > chkStopCuts
Bragg peak finder configuration.
Definition: DataStructs.h:550
bool SignalAtTpInSlc(const TCSlice &slc, const TrajPoint &tp)
Definition: Utils.cxx:1922
std::vector< TP3D > TP3Ds
Definition: DataStructs.h:282
void PrintDebugMode()
Definition: Utils.cxx:5297
int ID
ID of the recob::Slice (not the sub-slice)
Definition: DataStructs.h:657
std::array< std::vector< float >, 2 > dEdx
Definition: DataStructs.h:285
int UID
unique global ID
Definition: DataStructs.h:81
void SetVx2Score(TCSlice &slc)
Definition: TCVertex.cxx:2238
unsigned int Nplanes(TPCID const &tpcid=tpc_zero) const
Returns the total number of planes in the specified TPC.
Definition: GeometryCore.h:977
std::vector< float > vtx2DCuts
Max position pull, max Position error rms.
Definition: DataStructs.h:543
std::bitset< 8 > Strategy
Definition: DataStructs.h:210
unsigned short nPlanes
Definition: DataStructs.h:656
bool NearbySrcHit(geo::PlaneID plnID, unsigned int wire, float loTick, float hiTick)
Definition: Utils.cxx:2029
std::vector< PFPStruct > pfps
Definition: DataStructs.h:671
std::vector< int > FindCloseTjs(const TCSlice &slc, const TrajPoint &fromTp, const TrajPoint &toTp, const float &maxDelta)
Definition: Utils.cxx:2907
2D representation of charge deposited in the TDC/wire plane
Definition: Hit.h:46
void PrintAllTraj(detinfo::DetectorPropertiesData const &detProp, std::string someText, TCSlice const &slc, unsigned short itj, unsigned short ipt, bool prtVtx)
Definition: Utils.cxx:5790
void MoveTPToWire(TrajPoint &tp, float wire)
Definition: Utils.cxx:2762
unsigned short NumPtsWithCharge(const TCSlice &slc, const Trajectory &tj, bool includeDeadWires)
Definition: Utils.cxx:2071
unsigned short CloseEnd(const Trajectory &tj, const Point2_t &pos)
Definition: Utils.cxx:2494
void MergeGhostTjs(TCSlice &slc, CTP_t inCTP)
Definition: Utils.cxx:2171
bool TrajIsClean(Trajectory const &tj, bool prt)
Definition: Utils.cxx:3357
TCEvent evt
Definition: DataStructs.cxx:8
unsigned int ChannelID_t
Type representing the ID of a readout channel.
Definition: RawTypes.h:28
unsigned int allHitsIndex
Definition: DataStructs.h:606
unsigned short MVI
Definition: DataStructs.h:296
TPCID_t TPC
Index of the TPC within its cryostat.
Definition: geo_types.h:399
bool StorePFP(TCSlice &slc, PFPStruct &pfp)
Definition: PFPUtils.cxx:2982
Double_t sum
Definition: plot.C:31
size_t ParentUID
Definition: DataStructs.h:291
double sampling_rate(DetectorClocksData const &data)
Returns the period of the TPC readout electronics clock.
unsigned short nPtsFit
Definition: DataStructs.h:183
bool TrajTrajDOCA(const TCSlice &slc, const Trajectory &tj1, const Trajectory &tj2, unsigned short &ipt1, unsigned short &ipt2, float &minSep)
Definition: Utils.cxx:2406
std::vector< std::vector< std::pair< unsigned int, unsigned int > > > wireHitRange
Definition: DataStructs.h:619
Vector3_t DirAtEnd(const PFPStruct &pfp, unsigned short end)
Definition: PFPUtils.cxx:3251
short recoSlice
only reconstruct the slice with ID (0 = all)
Definition: DataStructs.h:581
bool useChannelStatus
Definition: DataStructs.h:602
Float_t w
Definition: plot.C:20
float TjChgFrac
Fraction of charge near the vertex that is from hits on the vertex Tjs.
Definition: DataStructs.h:84
bool dbgSummary
print a summary report
Definition: DataStructs.h:597
bool NeedsUpdate
Set true when the Tj needs to be updated.
Definition: DataStructs.h:211
bool WireHitRangeOK(TCSlice const &slc, const CTP_t &inCTP)
Definition: Utils.cxx:4508
void TrajPointTrajDOCA(TrajPoint const &tp, Trajectory const &tj, unsigned short &closePt, float &minSep)
Definition: Utils.cxx:2383
master switch for turning on debug mode
Definition: DataStructs.h:526
Point2_t HitPos
Definition: DataStructs.h:151
Length_t WirePitch(PlaneID const &planeid=plane_zero) const
Returns the distance between two consecutive wires.
float TPHitsRMSTick(const TCSlice &slc, const TrajPoint &tp, HitStatus_t hitRequest)
Definition: Utils.cxx:4096
void PrintTPHeader(std::string someText)
Definition: Utils.cxx:6123
void ChkEndKink(TCSlice const &slc, Trajectory &tj, bool prt)
Definition: Utils.cxx:1681
CTP_t CTP
set to an invalid CTP
Definition: DebugStruct.h:23
std::vector< int > DtrUIDs
Definition: DataStructs.h:290
constexpr Point origin()
Returns a origin position with a point of the specified type.
Definition: geo_vectors.h:229
unsigned short AngleRange(TrajPoint const &tp)
Definition: Utils.cxx:758
use the stiff muon strategy
Definition: DataStructs.h:495
void PosInPlane(detinfo::DetectorPropertiesData const &detProp, const Vtx3Store &vx3, unsigned short plane, Point2_t &pos)
Definition: TCVertex.cxx:2839
Encapsulate the construction of a single detector plane.
std::array< std::bitset< 8 >, 2 > EndFlag
Definition: DataStructs.h:298
void SetTPEnvironment(TCSlice &slc, CTP_t inCTP)
Definition: Utils.cxx:3530
bool HasDuplicateHits(const TCSlice &slc, Trajectory const &tj, bool prt)
Definition: Utils.cxx:2744
void ReverseTraj(Trajectory &tj)
Definition: Utils.cxx:3218