Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
PHMicromegasTpcTrackMatching.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file PHMicromegasTpcTrackMatching.cc
2 
3 //#include "PHTrackPropagating.h" // for PHTrackPropagating
4 
6 
9 
11 #include <trackbase/ActsGeometry.h>
13 #include <trackbase/TrkrCluster.h> // for TrkrCluster
14 #include <trackbase/TrkrClusterv3.h> // for TrkrCluster
15 #include <trackbase/TrkrDefs.h> // for cluskey, getLayer, TrkrId
18 #include <trackbase/TpcDefs.h>
19 
23 
25 
27 #include <fun4all/SubsysReco.h> // for SubsysReco
28 
29 #include <phool/getClass.h>
30 #include <phool/phool.h>
31 
32 #include <TF1.h>
33 #include <TVector3.h>
34 
35 #include <array>
36 #include <cmath> // for sqrt, std::abs, atan2, cos
37 #include <iostream> // for operator<<, basic_ostream
38 #include <map> // for map
39 #include <set> // for _Rb_tree_const_iterator
40 #include <utility> // for pair, make_pair
41 
42 namespace
43 {
44 
46  template<class T>
47  inline constexpr T square( const T& x ) { return x*x; }
48 
50  template<class T>
51  inline constexpr T get_r( const T& x, const T& y ) { return std::sqrt( square(x) + square(y) ); }
52 
54 
61  bool circle_line_intersection(
62  double r, double xc, double yc,
63  double x0, double y0, double nx, double ny,
64  double &xplus, double &yplus, double &xminus, double &yminus)
65  {
66  if( ny == 0 )
67  {
68  // vertical lines are defined by ny=0 and x = x0
69  xplus = xminus = x0;
70 
71  // calculate y accordingly
72  const double delta = square(r) - square(x0-xc);
73  if( delta < 0 ) return false;
74 
75  const double sqdelta = std::sqrt( delta );
76  yplus = yc + sqdelta;
77  yminus = yc - sqdelta;
78 
79  } else {
80 
81  const double a = square(nx) + square(ny);
82  const double b = -2.*( square(ny)*xc + square(nx)*x0 + nx*ny*(y0-yc) );
83  const double c = square(ny)*(square(xc)-square(r)) + square(ny*(y0-yc)+nx*x0);
84  const double delta = square(b) - 4.*a*c;
85  if( delta < 0 ) return false;
86 
87  const double sqdelta = std::sqrt( delta );
88  xplus = (-b + sqdelta)/(2.*a);
89  xminus = (-b - sqdelta)/(2.*a);
90 
91  yplus = y0 -(nx/ny)*(xplus-x0);
92  yminus = y0 - (nx/ny)*(xminus-x0);
93 
94  }
95 
96  return true;
97 
98  }
99 
100  // streamer of TVector3
101  [[maybe_unused]] inline std::ostream& operator << (std::ostream& out, const TVector3& vector )
102  {
103  out << "( " << vector.x() << "," << vector.y() << "," << vector.z() << ")";
104  return out;
105  }
106 
107 }
108 
109 //____________________________________________________________________________..
111  SubsysReco(name)
112 {}
113 
114 //____________________________________________________________________________..
116 {
117 
118  std::cout << std::endl << PHWHERE
119  << " rphi_search_win inner layer " << _rphi_search_win[0]
120  << " z_search_win inner layer " << _z_search_win[0]
121  << " rphi_search_win outer layer " << _rphi_search_win[1]
122  << " z_search_win outer layer " << _z_search_win[1]
123  << std::endl;
124 
125  // load micromegas geometry
126  _geomContainerMicromegas = findNode::getClass<PHG4CylinderGeomContainer>(topNode, "CYLINDERGEOM_MICROMEGAS_FULL" );
128  {
129  std::cout << PHWHERE << "Could not find CYLINDERGEOM_MICROMEGAS_FULL." << std::endl;
131  }
132 
133  // ensures there are at least two micromegas layers
135  {
136  std::cout << PHWHERE << "Inconsistent number of micromegas layers." << std::endl;
138  }
139 
140  // get first micromegas layer
142 
143  int ret = GetNodes(topNode);
144  if (ret != Fun4AllReturnCodes::EVENT_OK) return ret;
145 
146  return ret;
147 }
148 
149 //____________________________________________________________________________..
151 {
152 
153  if(_n_iteration >0)
154  {
155  _iteration_map = findNode::getClass<TrkrClusterIterationMapv1>(topNode, "CLUSTER_ITERATION_MAP");
156  if (!_iteration_map)
157  {
158  std::cerr << PHWHERE << "Cluster Iteration Map missing, aborting." << std::endl;
160  }
161  }
162 
163  // We will add the micromegas cluster to the TPC tracks already on the node tree
164 
165  _event++;
166 
167  if(Verbosity() > 0)
168  std::cout << PHWHERE << " Event " << _event << " Seed track map size " << _svtx_seed_map->size() << std::endl;
169 
170  // loop over the seed tracks - these are the seeds formed from matched tpc and silicon track seeds
171  for (unsigned int seedID = 0;
172  seedID != _svtx_seed_map->size(); ++seedID)
173  {
174  auto seed = _svtx_seed_map->get(seedID);
175  auto siID = seed->get_silicon_seed_index();
176  auto tracklet_si = _si_track_map->get(siID);
177  if(!tracklet_si) continue; // cannot use tracks not matched to silicon because crossing is unknown
178 
179  short int crossing = tracklet_si->get_crossing();
180  if (crossing == SHRT_MAX)
181  {
182  if(Verbosity() > 0)
183  std::cout << " svtx seed " << seedID << " with si seed " << siID << " crossing not defined: crossing = " << crossing << " skip this track" << std::endl;
184  continue;
185  }
186 
187  auto tpcID = seed->get_tpc_seed_index();
188 
189  auto tracklet_tpc = _tpc_track_map->get(tpcID);
190  if(!tracklet_tpc) { continue; }
191 
192  if (Verbosity() >= 1)
193  {
194  std::cout << std::endl
195  << __LINE__
196  << ": Processing TPC seed track: " << tpcID
197  << ": crossing: " << crossing
198  << ": nhits: " << tracklet_tpc-> size_cluster_keys()
199  << ": Total TPC tracks: " << _tpc_track_map->size()
200  << ": phi: " << tracklet_tpc->get_phi(_cluster_map,_tGeometry)
201  << std::endl;
202  }
203 
204  // Get the outermost TPC clusters for this tracklet
205  std::map<unsigned int, TrkrCluster*> outer_clusters;
206  std::vector<TrkrCluster*> clusters;
207  std::vector<Acts::Vector3> clusGlobPos;
208 
209  for (auto key_iter = tracklet_tpc->begin_cluster_keys(); key_iter != tracklet_tpc->end_cluster_keys(); ++key_iter)
210  {
211  TrkrDefs::cluskey cluster_key = *key_iter;
212  unsigned int layer = TrkrDefs::getLayer(cluster_key);
213 
214  if(layer < _min_tpc_layer) continue;
215  if(layer >= _min_mm_layer) continue;
216 
217  // get the cluster
218  TrkrCluster *tpc_clus = _cluster_map->findCluster(cluster_key);
219  if(!tpc_clus) continue;
220 
221  outer_clusters.insert(std::make_pair(layer, tpc_clus));
222  clusters.push_back(tpc_clus);
223  // make necessary corrections to the global position
224  unsigned int side = TpcDefs::getSide(cluster_key);
225  const Acts::Vector3 global = getGlobalPosition(cluster_key, tpc_clus, crossing, side);
226  clusGlobPos.push_back(global);
227 
228  if(Verbosity() > 10)
229  {
230  auto global_raw = _tGeometry->getGlobalPosition(cluster_key, tpc_clus);
231  std::cout
232  << " TPC cluster key " << cluster_key << " in layer " << layer << " side " << side << " crossing " << crossing
233  << " with local position " << tpc_clus->getLocalX() << " " << tpc_clus->getLocalY() << std::endl;
234  std::cout << " raw global position " << global_raw[0] << " " << global_raw[1] << " " << global_raw[2]
235  << " corrected global position " << global[0] << " " << global[1] << " " << global[2]
236  << std::endl;
237  }
238  }
239 
240  // need at least 3 clusters to fit a circle
241  if(outer_clusters.size() < 3)
242  {
243  if(Verbosity() > 3) std::cout << PHWHERE << " -- skip this tpc tracklet, not enough outer clusters " << std::endl;
244  continue; // skip to the next TPC tracklet
245  }
246 
247  // fit a circle to the clusters
248  const auto [R, X0, Y0] = TrackFitUtils::circle_fit_by_taubin( clusGlobPos );
249  if(Verbosity() > 10) std::cout << " Fitted circle has R " << R << " X0 " << X0 << " Y0 " << Y0 << std::endl;
250 
251  // toss tracks for which the fitted circle could not have come from the vertex
252  if(R < 40.0) continue;
253 
254  // get the straight line representing the z trajectory in the form of z vs radius
255  const auto [A, B] = TrackFitUtils::line_fit( clusGlobPos );
256  if(Verbosity() > 10) std::cout << " Fitted line has A " << A << " B " << B << std::endl;
257 
258  // loop over micromegas layer
259  for(unsigned int imm = 0; imm < _n_mm_layers; ++imm)
260  {
261 
262  // get micromegas geometry object
263  const unsigned int layer = _min_mm_layer + imm;
264  const auto layergeom = static_cast<CylinderGeomMicromegas*>(_geomContainerMicromegas->GetLayerGeom(layer));
265  const auto layer_radius = layergeom->get_radius();
266 
267  // method to find where fitted circle intersects this layer
268  auto [xplus, yplus, xminus, yminus] = TrackFitUtils::circle_circle_intersection( layer_radius, R, X0, Y0 );
269 
270  // finds the intersection of the fitted circle with the micromegas layer
271  if( !std::isfinite(xplus) )
272  {
273  if(Verbosity() > 10)
274  {
275  std::cout << PHWHERE << " circle/circle intersection calculation failed, skip this case" << std::endl;
276  std::cout << PHWHERE << " mm_radius " << layer_radius << " fitted R " << R << " fitted X0 " << X0 << " fitted Y0 " << Y0 << std::endl;
277  }
278 
279  continue;
280  }
281 
282  // we can figure out which solution is correct based on the last cluster position in the TPC
283  const double last_clus_phi = std::atan2(clusGlobPos.back()(1), clusGlobPos.back()(0));
284  double phi_plus = std::atan2(yplus, xplus);
285  double phi_minus = std::atan2(yminus, xminus);
286 
287  // calculate z
288  double r = layer_radius;
289  double z = B + A*r;
290 
291  // select the angle that is the closest to last cluster
292  // store phi, apply coarse space charge corrections in calibration mode
293  double phi = std::abs(last_clus_phi - phi_plus) < std::abs(last_clus_phi - phi_minus) ? phi_plus:phi_minus;
294 
295  // create cylinder intersection point in world coordinates
296  const TVector3 world_intersection_cylindrical( r*std::cos(phi), r*std::sin(phi), z );
297 
298  // find matching tile
299  int tileid = layergeom->find_tile_cylindrical( world_intersection_cylindrical );
300  if( tileid < 0 ) continue;
301 
302  // get tile center and norm vector
303  const auto tile_center = layergeom->get_world_from_local_coords( tileid, _tGeometry, {0, 0} );
304  const double x0 = tile_center.x();
305  const double y0 = tile_center.y();
306 
307  const auto tile_norm = layergeom->get_world_from_local_vect( tileid, _tGeometry, {0, 0, 1 } );
308  const double nx = tile_norm.x();
309  const double ny = tile_norm.y();
310 
311  // calculate intersection to tile
312  if( !circle_line_intersection( R, X0, Y0, x0, y0, nx, ny, xplus, yplus, xminus, yminus) )
313  {
314  if(Verbosity() > 10)
315  { std::cout << PHWHERE << "circle_line_intersection - failed" << std::endl; }
316  continue;
317  }
318 
319  // select again angle closest to last cluster
320  phi_plus = std::atan2(yplus, xplus);
321  phi_minus = std::atan2(yminus, xminus);
322  const bool is_plus = (std::abs(last_clus_phi - phi_plus) < std::abs(last_clus_phi - phi_minus));
323 
324  // calculate x, y and z
325  const double x = (is_plus ? xplus:xminus);
326  const double y = (is_plus ? yplus:yminus);
327  r = get_r( x, y );
328  z = B + A*r;
329 
330  /*
331  * create planar intersection point in world coordinates
332  * this is the position to be compared to the clusters
333  */
334  const TVector3 world_intersection_planar( x, y, z );
335 
336  // convert to tile local reference frame, apply SC correction
337  const auto local_intersection_planar = layergeom->get_local_from_world_coords( tileid, _tGeometry, world_intersection_planar );
338 
339  // store segmentation type
340  const auto segmentation_type = layergeom->get_segmentation_type();
341 
342  // generate tilesetid and get corresponding clusters
343  const auto tilesetid = MicromegasDefs::genHitSetKey(layer, segmentation_type, tileid);
344  const auto mm_clusrange = _cluster_map->getClusters(tilesetid);
345 
346  // convert to tile local coordinate and compare
347  for(auto clusiter = mm_clusrange.first; clusiter != mm_clusrange.second; ++clusiter)
348  {
349  TrkrDefs::cluskey ckey = clusiter->first;
350  if(_iteration_map)
351  {
352  if( _iteration_map->getIteration(ckey) > 0)
353  { continue; }
354  }
355 
356  // store cluster and key
357  const auto& [key, cluster] = *clusiter;
358 
359  // compute residuals and store
360  /* in local tile coordinate, x is along rphi, and z is along y) */
361  const double drphi = local_intersection_planar.x() - cluster->getLocalX();
362  const double dz = local_intersection_planar.y() - cluster->getLocalY();
363 
364  // compare to cuts and add to track if matching
365  if( std::abs(drphi) < _rphi_search_win[imm] && std::abs(dz) < _z_search_win[imm] )
366  {
367  tracklet_tpc->insert_cluster_key(key);
368 
369  if(Verbosity() > 0)
370  std::cout << " Match to MM's found for seedID " << seedID << " tpcID " << tpcID << " siID " << siID << std::endl;
371 
372  // prints out a line that can be grep-ed from the output file to feed to a display macro
373  if( _test_windows )
374  {
375 
376  // cluster rphi and z
377  const auto glob = _tGeometry->getGlobalPosition(key, cluster);
378  const double mm_clus_rphi = get_r( glob.x(), glob.y() ) * std::atan2( glob.y(), glob.x() );
379  const double mm_clus_z = glob.z();
380 
381  // projection phi and z, without correction
382  const double rphi_proj = get_r( world_intersection_planar.x(), world_intersection_planar.y() ) * std::atan2( world_intersection_planar.y(), world_intersection_planar.x() );
383  const double z_proj = world_intersection_planar.z();
384 
385  /*
386  * Note: drphi and dz might not match the difference of the rphi and z quoted values. This is because
387  * 1/ drphi and dz are actually calculated in Tile's local reference frame, not in world coordinates
388  * 2/ drphi also includes SC distortion correction, which the world coordinates don't
389  */
390  std::cout
391  << " Try_mms: " << (int) layer
392  << " drphi " << drphi
393  << " dz " << dz
394  << " mm_clus_rphi " << mm_clus_rphi << " mm_clus_z " << mm_clus_z
395  << " rphi_proj " << rphi_proj << " z_proj " << z_proj
396  << std::endl;
397  }
398  }
399 
400  } // end loop over clusters
401 
402  } // end loop over Micromegas layers
403 
404  if(Verbosity() > 3)
405  { tracklet_tpc->identify(); }
406 
407  }
408 
409  if(Verbosity() > 0)
410  { std::cout << " Final seed map size " << _svtx_seed_map->size() << std::endl; }
411 
413 }
414 
415 //_________________________________________________________________________________________________
418 
419 //_________________________________________________________________________________________________
421 {
422 
423  // all clusters
425  _cluster_map = findNode::getClass<TrkrClusterContainer>(topNode, "TRKR_CLUSTER_TRUTH");
426  else
427  {
428  _cluster_map = findNode::getClass<TrkrClusterContainer>(topNode, "TRKR_CLUSTER");
429  }
430 
431  if (!_cluster_map)
432  {
433  std:: cerr << PHWHERE << " ERROR: Can't find node TRKR_CLUSTER" << std::endl;
435  }
436 
437  _tGeometry = findNode::getClass<ActsGeometry>(topNode, "ActsGeometry");
438  if(!_tGeometry)
439  {
440  std::cerr << PHWHERE << "No acts tracking geometry, can't continue."
441  << std::endl;
443  }
444 
445  _svtx_seed_map = findNode::getClass<TrackSeedContainer>(topNode, "SvtxTrackSeedContainer");
446  if(!_svtx_seed_map)
447  {
448  std::cerr << PHWHERE << " ERROR: Can't find "<< "SvtxTrackSeedContainer" << std::endl;
450  }
451 
452  _tpc_track_map = findNode::getClass<TrackSeedContainer>(topNode, "TpcTrackSeedContainer");
453  if (!_tpc_track_map)
454  {
455  std::cerr << PHWHERE << " ERROR: Can't find "<< "TpcTrackSeedContainer" << std::endl;
457  }
458 
459  _si_track_map = findNode::getClass<TrackSeedContainer>(topNode, "SiliconTrackSeedContainer");
460  if (!_si_track_map)
461  {
462  std::cerr << PHWHERE << " ERROR: Can't find "<< "SiliconTrackSeedContainer" << std::endl;
464  }
465 
466  // micromegas geometry
467  _geomContainerMicromegas = findNode::getClass<PHG4CylinderGeomContainer>(topNode, "CYLINDERGEOM_MICROMEGAS_FULL" );
469  {
470  std::cout << PHWHERE << "Could not find CYLINDERGEOM_MICROMEGAS_FULL." << std::endl;
472  }
473 
474  // tpc distortion corrections
475  m_dcc_static = findNode::getClass<TpcDistortionCorrectionContainer>(topNode,"TpcDistortionCorrectionContainerStatic");
476  if( m_dcc_static )
477  {
478  std::cout << PHWHERE << " found static TPC distortion correction container" << std::endl;
479  }
480 
481  m_dcc_average = findNode::getClass<TpcDistortionCorrectionContainer>(topNode,"TpcDistortionCorrectionContainerAverage");
482  if( m_dcc_average )
483  {
484  std::cout << PHWHERE << " found average TPC distortion correction container" << std::endl;
485  }
486 
487  m_dcc_fluctuation = findNode::getClass<TpcDistortionCorrectionContainer>(topNode,"TpcDistortionCorrectionContainerFluctuation");
488  if( m_dcc_fluctuation )
489  {
490  std::cout << PHWHERE << " found fluctuation TPC distortion correction container" << std::endl;
491  }
492 
494 }
495 
497 {
498  auto globalPosition = _tGeometry->getGlobalPosition(key, cluster);
499  const auto trkrid = TrkrDefs::getTrkrId(key);
500  if(trkrid == TrkrDefs::tpcId)
501  {
502  // ADF: for streaming mode, will need a crossing z correction here
503  globalPosition.z() = m_clusterCrossingCorrection.correctZ(globalPosition.z(), side, crossing);
504 
505  // apply distortion corrections
506  if(m_dcc_static)
507  {
508  globalPosition = m_distortionCorrection.get_corrected_position( globalPosition, m_dcc_static );
509  }
510 
511  if(m_dcc_average)
512  {
513  globalPosition = m_distortionCorrection.get_corrected_position( globalPosition, m_dcc_average );
514  }
515 
516  if(m_dcc_fluctuation)
517  {
518  globalPosition = m_distortionCorrection.get_corrected_position( globalPosition, m_dcc_fluctuation );
519  }
520 
521  }
522 
523  return globalPosition;
524 }
525 
526 
527