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