LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
PMAlgStitching.cxx
Go to the documentation of this file.
1 // Class: PMAlgStitching
3 // Author: L.Whitehead (leigh.howard.whitehead@cern.ch) January 2017
5 
14 
16 
17 #include "TVector3.h"
18 
19 // Constructor
21 {
22 
23  // Set parameters from the config.
25  fNodesFromEnd = config.NodesFromEnd();
26 
27  // Get CPA and APA positions.
29 }
30 
31 // CPA stitching wrapper
33  const detinfo::DetectorPropertiesData& detProp,
34  pma::TrkCandidateColl& tracks)
35 {
36  mf::LogInfo("pma::PMAlgStitching") << "Passed " << tracks.size() << " tracks for CPA stitching.";
37  StitchTracks(clockData, detProp, tracks, true);
38 }
39 
40 // APA stitching wrapper
42  const detinfo::DetectorPropertiesData& detProp,
43  pma::TrkCandidateColl& tracks)
44 {
45  mf::LogInfo("pma::PMAlgStitching") << "Passed " << tracks.size() << " tracks for APA stitching.";
46  StitchTracks(clockData, detProp, tracks, false);
47 }
48 
49 // Main function of the algorithm
50 // isCPA = true : attempt to stitch tracks across the cathode.
51 // = false : attempt to stitch tracks across the anode.
53  const detinfo::DetectorPropertiesData& detProp,
54  pma::TrkCandidateColl& tracks,
55  bool isCPA)
56 {
57 
58  unsigned int minTrkLength = 2 * fNodesFromEnd + 3;
59  // Special case for fNodesFromEnd = 0
60  if (minTrkLength < 6) minTrkLength = 6;
61 
62  // Loop over the track collection
63  unsigned int t = 0;
64  while (t < tracks.size()) {
65 
66  pma::Track3D* t1 = tracks[t].Track();
67  if (t1->Nodes().size() < minTrkLength) {
68  ++t;
69  continue;
70  }
71 
72  // Look through the following tracks for one to stitch
73  pma::Track3D* bestTrkMatch = 0x0;
74 
75  // Don't use the very end points of the tracks in case of scatter or distortion.
76  TVector3 trk1Front = t1->Nodes()[(fNodesFromEnd)]->Point3D();
77  TVector3 trk1Back = t1->Nodes()[t1->Nodes().size() - 1 - fNodesFromEnd]->Point3D();
78  TVector3 trk1FrontDir = (trk1Front - t1->Nodes()[(fNodesFromEnd + 1)]->Point3D()).Unit();
79  TVector3 trk1BackDir =
80  (trk1Back - t1->Nodes()[t1->Nodes().size() - 1 - (fNodesFromEnd + 1)]->Point3D()).Unit();
81 
82  // For stitching, we need to consider both ends of the track.
83  double offsetFront1 = GetTPCOffset(t1->FrontTPC(), t1->FrontCryo(), isCPA);
84  double offsetBack1 = GetTPCOffset(t1->BackTPC(), t1->BackCryo(), isCPA);
85 
86  bool isBestFront1 = false;
87  bool isBestFront2 = false;
88  double xBestShift = 0;
89  double frontShift1 = t1->Nodes()[0]->Point3D().X() - offsetFront1;
90  double backShift1 = t1->Nodes()[t1->Nodes().size() - 1]->Point3D().X() - offsetBack1;
91 
92  double bestMatchScore = 99999;
93 
94  for (unsigned int u = t + 1; u < tracks.size(); ++u) {
95 
96  pma::Track3D* t2 = tracks[u].Track();
97  if (t2->Nodes().size() < minTrkLength) continue;
98 
99  // Don't use the very end points of the tracks in case of scatter or distortion.
100  TVector3 trk2Front = t2->Nodes()[(fNodesFromEnd)]->Point3D();
101  TVector3 trk2Back = t2->Nodes()[t2->Nodes().size() - 1 - fNodesFromEnd]->Point3D();
102  TVector3 trk2FrontDir = (trk2Front - t2->Nodes()[(fNodesFromEnd + 1)]->Point3D()).Unit();
103  TVector3 trk2BackDir =
104  (trk2Back - t2->Nodes()[t2->Nodes().size() - 1 - (fNodesFromEnd + 1)]->Point3D()).Unit();
105 
106  // For stitching, we need to consider both ends of the track.
107  double offsetFront2 = GetTPCOffset(t2->FrontTPC(), t2->FrontCryo(), isCPA);
108  double offsetBack2 = GetTPCOffset(t2->BackTPC(), t2->BackCryo(), isCPA);
109 
110  // If the points to match are in the same TPC, then don't bother.
111  // Remember we have 4 points to consider here.
112  bool giveUp = false;
113  geo::TPCID tpc1(0, 0);
114  geo::TPCID tpc2(0, 0);
115  // Front-to-front
116  tpc1 = geo::TPCID(t1->FrontCryo(), t1->FrontTPC());
117  tpc2 = geo::TPCID(t2->FrontCryo(), t2->FrontTPC());
118  if (tpc1 == tpc2) giveUp = true;
119  // Front-to-back
120  tpc2 = geo::TPCID(t2->BackCryo(), t2->BackTPC());
121  if (tpc1 == tpc2) giveUp = true;
122  // Back-to-front
123  tpc1 = geo::TPCID(t1->BackCryo(), t1->BackTPC());
124  tpc2 = geo::TPCID(t2->FrontCryo(), t2->FrontTPC());
125  if (tpc1 == tpc2) giveUp = true;
126  // Back-to-back
127  tpc2 = geo::TPCID(t2->BackCryo(), t2->BackTPC());
128  if (tpc1 == tpc2) giveUp = true;
129 
130  // If the tracks have one end in the same TPC, give up.
131  if (giveUp) continue;
132 
133  // Also check that these tpcs do meet at the stitching surface (not a problem for protoDUNE).
134  double surfaceGap = 10.0;
135  bool carryOn[4] = {true, true, true, true};
136  if (fabs(offsetFront1 - offsetFront2) > surfaceGap) carryOn[0] = false;
137  if (fabs(offsetFront1 - offsetBack2) > surfaceGap) carryOn[1] = false;
138  if (fabs(offsetBack1 - offsetFront2) > surfaceGap) carryOn[2] = false;
139  if (fabs(offsetBack1 - offsetBack2) > surfaceGap) carryOn[3] = false;
140 
141  // Loop over the four options
142  for (int i = 0; i < 4; ++i) {
143 
144  if (!carryOn[i]) continue;
145 
146  TVector3 t1Pos;
147  TVector3 t2Pos;
148  TVector3 t1Dir;
149  TVector3 t2Dir;
150  double xShift1;
151  if (i < 2) {
152  t1Pos = trk1Front;
153  t1Dir = trk1FrontDir;
154  xShift1 = frontShift1;
155  }
156  else {
157  t1Pos = trk1Back;
158  t1Dir = trk1BackDir;
159  xShift1 = backShift1;
160  }
161  if (i % 2 == 0) {
162  t2Pos = trk2Front;
163  t2Dir = trk2FrontDir;
164  }
165  else {
166  t2Pos = trk2Back;
167  t2Dir = trk2BackDir;
168  }
169 
170  // Make sure the x directions point towards eachother (could be an issue for matching a short track)
171  if (t1Dir.X() * t2Dir.X() > 0) { continue; }
172  // t1Pos.SetX(t1Pos.X() - xShift1);
173  // t2Pos.SetX(t2Pos.X() + xShift1);
174 
175  double score = 0;
176  // score = GetTrackPairDelta(t1Pos,t2Pos,t1Dir,t2Dir);
177  score = GetOptimalStitchShift(t1Pos, t2Pos, t1Dir, t2Dir, xShift1);
178 
179  if (score < fStitchingThreshold && score < bestMatchScore) {
180 
181  bestTrkMatch = t2;
182  xBestShift = xShift1;
183  bestMatchScore = score;
184  if (i < 2) { isBestFront1 = true; }
185  else {
186  isBestFront1 = false;
187  }
188  if (i % 2 == 0) { isBestFront2 = true; }
189  else {
190  isBestFront2 = false;
191  }
192  mf::LogInfo("pma::PMAlgStitcher")
193  << "Tracks " << t << " and " << u << " matching score = " << score << std::endl
194  << " - " << t1Pos.X() << ", " << t1Pos.Y() << ", " << t1Pos.Z() << " :: " << t1Dir.X()
195  << ", " << t1Dir.Y() << ", " << t1Dir.Z() << std::endl
196  << " - " << t2Pos.X() << ", " << t2Pos.Y() << ", " << t2Pos.Z() << " :: " << t2Dir.X()
197  << ", " << t2Dir.Y() << ", " << t2Dir.Z() << std::endl
198  << " - " << t1->FrontCryo() << ", " << t1->FrontTPC() << " :: " << t1->BackCryo()
199  << ", " << t1->BackTPC() << std::endl
200  << " - " << t2->FrontCryo() << ", " << t2->FrontTPC() << " :: " << t2->BackCryo()
201  << ", " << t2->BackTPC() << std::endl
202  << " - " << isBestFront1 << " :: " << isBestFront2 << std::endl;
203  } // End successful match if
204  } // Loop over matching options
205  } // Loop over track 2
206 
207  // If we found a match, do something about it.
208  if (bestTrkMatch != 0x0) {
209 
210  bool flip1 = false;
211  bool flip2 = false;
212  bool reverse = false;
213 
214  // Front-to-front match
215  if (isBestFront1 && isBestFront2) { flip1 = true; }
216  // Front-to-back match
217  else if (isBestFront1 && !isBestFront2) {
218  reverse = true;
219  }
220  // Back-to-back match
221  else if (!isBestFront1 && !isBestFront2) {
222  flip2 = true;
223  }
224  // Back-to-front match (do nothing)
225 
226  int tid1 = tracks.getCandidateTreeId(t1);
227  int tid2 = tracks.getCandidateTreeId(bestTrkMatch);
228 
229  bool canMerge = true;
230  if ((tid1 < 0) || (tid2 < 0)) {
231  throw cet::exception("pma::PMAlgStitching")
232  << "Track not found in the collection." << std::endl;
233  }
234  if (flip1) {
235 
236  std::vector<pma::Track3D*> newTracks;
237  if (t1->Flip(detProp, newTracks)) {
238  mf::LogInfo("pma::PMAlgStitching") << "Track 1 flipped.";
239  }
240  else {
241  mf::LogInfo("pma::PMAlgStitching") << "Unable to flip Track 1.";
242  canMerge = false;
243  }
244  for (const auto ts : newTracks) { // there may be a new track even if
245  // entire flip was not possible
246  tracks.tracks().emplace_back(ts, -1, tid1);
247  }
248  }
249  if (flip2) {
250 
251  std::vector<pma::Track3D*> newTracks;
252  if (bestTrkMatch->Flip(detProp, newTracks)) {
253  mf::LogInfo("pma::PMAlgStitching") << "Track 2 flipped.";
254  }
255  else {
256  mf::LogInfo("pma::PMAlgStitching") << "Unable to flip Track 1.";
257  canMerge = false;
258  }
259  for (const auto ts : newTracks) { // there may be a new track even if
260  // entire flip was not possible
261  tracks.tracks().emplace_back(ts, -1, tid2);
262  }
263  }
264 
265  t1->GetRoot()->ApplyDriftShiftInTree(clockData, detProp, -xBestShift);
266  bestTrkMatch->GetRoot()->ApplyDriftShiftInTree(clockData, detProp, +xBestShift);
267 
268  if (canMerge) {
269  mf::LogInfo("pma::PMAlgStitching") << "Merging tracks...";
270  int idx1 = tracks.getCandidateIndex(t1);
271  int idx2 = tracks.getCandidateIndex(bestTrkMatch);
272  if (reverse) // merge current track to another track, do not increase
273  // the outer loop index t (next after current track jumps
274  // in at t)
275  {
276  if (tracks.setTreeOriginAtFront(detProp, t1)) {
277  tracks.merge((size_t)idx2, (size_t)idx1);
278  }
279  else {
280  mf::LogWarning("pma::PMAlgStitching") << " could not merge.";
281  ++t;
282  }
283  }
284  else // merge to the current track, do not increase the outer loop index t (maybe something else will match to the extended track)
285  {
286  if (tracks.setTreeOriginAtFront(detProp, bestTrkMatch)) {
287  tracks.merge((size_t)idx1, (size_t)idx2);
288  }
289  else {
290  mf::LogWarning("pma::PMAlgStitching") << " could not merge.";
291  ++t;
292  }
293  }
294  mf::LogInfo("pma::PMAlgStitching") << "...done";
295  }
296  else {
297  ++t;
298  } // track matched, but not merged, go to the next track in the outer loop
299  }
300  else {
301  ++t;
302  } // no track matched, go to the next track in the outer loop
303  }
304 }
305 
306 // Perform the matching, allowing the shift to vary within +/- 5cm.
308  TVector3& pos2,
309  TVector3& dir1,
310  TVector3& dir2,
311  double& shift) const
312 {
313 
314  double stepSize = 0.1;
315  double minShift = shift - (50. * stepSize);
316  double maxShift = shift + (50. * stepSize);
317  double bestShift = 99999;
318  double bestScore = 99999;
319 
320  for (shift = minShift; shift <= maxShift; shift += stepSize) {
321  TVector3 newPos1 = pos1;
322  TVector3 newPos2 = pos2;
323  newPos1.SetX(pos1.X() - shift);
324  newPos2.SetX(pos2.X() + shift);
325  double thisScore = GetTrackPairDelta(newPos1, newPos2, dir1, dir2);
326  if (thisScore < bestScore) {
327  bestShift = shift;
328  bestScore = thisScore;
329  }
330  }
331 
332  shift = bestShift;
333  return bestScore;
334 }
335 
336 // Perform the extrapolation between the two vectors and return the distance between them.
338  TVector3& pos2,
339  TVector3& dir1,
340  TVector3& dir2) const
341 {
342 
343  double delta = -999.;
344 
345  // Calculate number of steps to reach the merge point in x.
346  double steps1 = (pos2.X() - pos1.X()) / dir1.X();
347  double steps2 = (pos1.X() - pos2.X()) / dir2.X();
348 
349  // Extrapolate each vector to the other's extrapolation point
350  TVector3 trk1Merge = pos1 + steps1 * dir1;
351  TVector3 trk2Merge = pos2 + steps2 * dir2;
352 
353  // Find the difference between each vector and the extrapolation
354  delta = (trk1Merge - pos2).Mag() + (trk2Merge - pos1).Mag();
355 
356  return delta;
357 }
358 
359 // Get the CPA and APA positions from the geometry
361 {
362 
363  // Grab hold of the geometry
364  auto const* geom = lar::providerFrom<geo::Geometry>();
365 
366  // Loop over each TPC and store the require information
367  for (geo::TPCID const& tID : geom->Iterate<geo::TPCID>()) {
368 
369  geo::TPCGeo const& aTPC = geom->TPC(tID);
370 
371  // Loop over the 3 possible readout planes to find the x position
372  unsigned int plane = 0;
373  bool hasPlane = false;
374  for (; plane < 4; ++plane) {
375  hasPlane = aTPC.HasPlane(plane);
376  if (hasPlane) { break; }
377  }
378 
379  if (!hasPlane) { continue; }
380 
381  // Get the x position of the readout plane
382  double xAnode = aTPC.Plane(0).GetCenter().X();
383  fTPCXOffsetsAPA.insert(std::make_pair(tID, xAnode));
384 
385  // For the cathode, we have to try a little harder. Firstly, find the
386  // min and max x values for the TPC.
387  auto const center = aTPC.GetCenter();
388  double tpcDim[3] = {aTPC.HalfWidth(), aTPC.HalfHeight(), 0.5 * aTPC.Length()};
389  double xmin = center.X() - tpcDim[0];
390  double xmax = center.X() + tpcDim[0];
391  double xCathode = 0.;
392  // Now check which is further from the APA and use that as the cathode value.
393  if (fabs(xmin - xAnode) > fabs(xmax - xAnode)) { xCathode = xmin; }
394  else {
395  xCathode = xmax;
396  }
397  fTPCXOffsetsCPA.insert(std::make_pair(tID, xCathode));
398  }
399 }
400 
401 // Interface to get the CPA and APA positions from the maps in which they are stored.
402 double pma::PMAlgStitching::GetTPCOffset(unsigned int tpc, unsigned int cryo, bool isCPA)
403 {
404 
405  geo::TPCID thisTPCID(cryo, tpc);
406  double offset = 0.0;
407  if (isCPA) { offset = fTPCXOffsetsCPA[thisTPCID]; }
408  else {
409  offset = fTPCXOffsetsAPA[thisTPCID];
410  }
411  return offset;
412 }
bool HasPlane(unsigned int iplane) const
Returns whether a plane with index iplane is present in this TPC.
Definition: TPCGeo.h:147
size_t size() const
Utilities related to art service access.
double GetTPCOffset(unsigned int tpc, unsigned int cryo, bool isCPA)
TTree * t1
Definition: plottest35.C:26
int getCandidateTreeId(pma::Track3D const *candidate) const
Implementation of the Projection Matching Algorithm.
Point_t const & GetCenter() const
Returns the centre of the wire plane in world coordinates [cm].
Definition: PlaneGeo.h:435
void StitchTracks(const detinfo::DetectorClocksData &clockData, const detinfo::DetectorPropertiesData &detProp, pma::TrkCandidateColl &tracks, bool isCPA)
MaybeLogger_< ELseverityLevel::ELsev_info, false > LogInfo
void ApplyDriftShiftInTree(const detinfo::DetectorClocksData &clockData, detinfo::DetectorPropertiesData const &detProp, double dx, bool skipFirst=false)
Geometry information for a single TPC.
Definition: TPCGeo.h:36
::fhicl::TupleAs< Point(::geo::Length_t,::geo::Length_t,::geo::Length_t)> Point3D
Atom object for reading a 3D point or vector (centimeters).
PMAlgStitching(const pma::PMAlgStitching::Config &config)
Implementation of the Projection Matching Algorithm.
double GetTrackPairDelta(TVector3 &pos1, TVector3 &pos2, TVector3 &dir1, TVector3 &dir2) const
unsigned int BackTPC() const
Definition: PmaTrack3D.h:121
std::map< geo::TPCID, double > fTPCXOffsetsAPA
double Length() const
Length is associated with z coordinate [cm].
Definition: TPCGeo.h:104
unsigned int fNodesFromEnd
unsigned int BackCryo() const
Definition: PmaTrack3D.h:122
Access the description of detector geometry.
Point_t GetCenter() const
Returns the center of the TPC volume in world coordinates [cm].
Definition: TPCGeo.h:247
double GetOptimalStitchShift(TVector3 &pos1, TVector3 &pos2, TVector3 &dir1, TVector3 &dir2, double &shift) const
bool setTreeOriginAtFront(detinfo::DetectorPropertiesData const &detProp, pma::Track3D *trk)
pma::Track3D * GetRoot()
std::map< geo::TPCID, double > fTPCXOffsetsCPA
fhicl::Atom< int > StitchingThreshold
int getCandidateIndex(pma::Track3D const *candidate) const
unsigned int FrontTPC() const
Definition: PmaTrack3D.h:118
unsigned int FrontCryo() const
Definition: PmaTrack3D.h:119
The data type to uniquely identify a TPC.
Definition: geo_types.h:381
void merge(size_t idx1, size_t idx2)
TTree * t2
Definition: plottest35.C:36
Track finding helper for the Projection Matching Algorithm.
double HalfHeight() const
Height is associated with y coordinate [cm].
Definition: TPCGeo.h:100
Contains all timing reference information for the detector.
fhicl::Atom< unsigned int > NodesFromEnd
void StitchTracksAPA(const detinfo::DetectorClocksData &clockData, const detinfo::DetectorPropertiesData &detProp, pma::TrkCandidateColl &tracks)
MaybeLogger_< ELseverityLevel::ELsev_warning, false > LogWarning
std::vector< pma::Node3D * > const & Nodes() const noexcept
Definition: PmaTrack3D.h:274
IDparameter< geo::TPCID > TPCID
Member type of validated geo::TPCID parameter.
PlaneGeo const & Plane(geo::View_t view) const
Return the plane in the tpc with View_t view.
Definition: TPCGeo.cxx:252
bool Flip(const detinfo::DetectorPropertiesData &detProp, std::vector< pma::Track3D * > &allTracks)
Definition: PmaTrack3D.cxx:527
void StitchTracksCPA(const detinfo::DetectorClocksData &clockData, const detinfo::DetectorPropertiesData &detProp, pma::TrkCandidateColl &tracks)
art framework interface to geometry description
double HalfWidth() const
Width is associated with x coordinate [cm].
Definition: TPCGeo.h:96
std::vector< TrkCandidate > const & tracks() const
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
Encapsulate the construction of a single detector plane.