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