Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
TpcSimpleClusterizer.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file TpcSimpleClusterizer.cc
1 #include "TpcSimpleClusterizer.h"
2 
3 #include <trackbase/TpcDefs.h>
4 
8 #include <trackbase/TrkrDefs.h> // for hitkey, getLayer
9 #include <trackbase/TrkrHitv2.h>
10 #include <trackbase/TrkrHitSet.h>
12 
14 #include <fun4all/SubsysReco.h> // for SubsysReco
15 
18 
21 
22 #include <phool/PHCompositeNode.h>
23 #include <phool/PHIODataNode.h> // for PHIODataNode
24 #include <phool/PHNode.h> // for PHNode
25 #include <phool/PHNodeIterator.h>
26 #include <phool/PHObject.h> // for PHObject
27 #include <phool/getClass.h>
28 #include <phool/phool.h> // for PHWHERE
29 
30 #include <TMatrixFfwd.h> // for TMatrixF
31 #include <TMatrixT.h> // for TMatrixT, ope...
32 #include <TMatrixTUtils.h> // for TMatrixTRow
33 
34 #include <TFile.h>
35 
36 #include <cmath> // for sqrt, cos, sin
37 #include <iostream>
38 #include <map> // for _Rb_tree_cons...
39 #include <string>
40 #include <utility> // for pair
41 #include <array>
42 #include <vector>
43 // Terra incognita....
44 #include <pthread.h>
45 
46 namespace
47 {
48  template<class T> inline constexpr T square( const T& x ) { return x*x; }
49 
50  using iphiz = std::pair<unsigned short, unsigned short>;
51  using ihit = std::pair<unsigned short, iphiz>;
52  using assoc = std::pair<uint32_t, TrkrDefs::hitkey> ;
53 
54  struct thread_data
55  {
56  PHG4TpcCylinderGeom *layergeom = nullptr;
57  TrkrHitSet *hitset = nullptr;
58  ActsGeometry *tGeometry = nullptr;
59  unsigned int layer = 0;
60  int side = 0;
61  unsigned int sector = 0;
62  float pedestal = 0;
63  bool do_assoc = true;
64  unsigned short phibins = 0;
65  unsigned short phioffset = 0;
66  unsigned short zbins = 0;
67  unsigned short zoffset = 0;
68  double par0_neg = 0;
69  double par0_pos = 0;
70  std::vector<assoc> association_vector;
71  std::vector<TrkrCluster*> cluster_vector;
72  };
73 
74  pthread_mutex_t mythreadlock;
75 
76  void remove_hit(double adc, int phibin, int zbin, std::multimap<unsigned short, ihit> &all_hit_map, std::vector<std::vector<unsigned short>> &adcval)
77  {
78  typedef std::multimap<unsigned short, ihit>::iterator hit_iterator;
79  std::pair<hit_iterator, hit_iterator> iterpair = all_hit_map.equal_range(adc);
80  hit_iterator it = iterpair.first;
81  for (; it != iterpair.second; ++it) {
82  if (it->second.second.first == phibin && it->second.second.second == zbin) {
83  all_hit_map.erase(it);
84  break;
85  }
86  }
87  adcval[phibin][zbin] = 0;
88  }
89 
90  void remove_hits(std::vector<ihit> &ihit_list, std::multimap<unsigned short, ihit> &all_hit_map,std::vector<std::vector<unsigned short>> &adcval)
91  {
92  for(auto iter = ihit_list.begin(); iter != ihit_list.end();++iter){
93  unsigned short adc = iter->first;
94  unsigned short phibin = iter->second.first;
95  unsigned short zbin = iter->second.second;
96  remove_hit(adc,phibin,zbin,all_hit_map,adcval);
97  }
98  }
99 
100  void get_cluster(int phibin, int zbin, const std::vector<std::vector<unsigned short>> &adcval, std::vector<ihit> &ihit_list)
101  {
102  // on hit = one cluster
103  const int& iphi = phibin;
104  const int& iz = zbin;
105  iphiz iCoord(std::make_pair(iphi,iz));
106  ihit thisHit(adcval[iphi][iz],iCoord);
107  ihit_list.push_back(thisHit);
108  }
109 
110  void calc_cluster_parameter(std::vector<ihit> &ihit_list, thread_data& my_data)
111  {
112 
113  // loop over the hits in this cluster
114  double z_sum = 0.0;
115  double phi_sum = 0.0;
116  double adc_sum = 0.0;
117  double z2_sum = 0.0;
118  double phi2_sum = 0.0;
119 
120  double radius = my_data.layergeom->get_radius(); // returns center of layer
121 
122  int phibinhi = -1;
123  int phibinlo = 666666;
124  int zbinhi = -1;
125  int zbinlo = 666666;
126 
127  std::vector<TrkrDefs::hitkey> hitkeyvec;
128  for(auto iter = ihit_list.begin(); iter != ihit_list.end();++iter){
129  double adc = iter->first;
130 
131  if (adc <= 0) continue;
132 
133  int iphi = iter->second.first + my_data.phioffset;
134  int iz = iter->second.second + my_data.zoffset;
135  if(iphi > phibinhi) phibinhi = iphi;
136  if(iphi < phibinlo) phibinlo = iphi;
137  if(iz > zbinhi) zbinhi = iz;
138  if(iz < zbinlo) zbinlo = iz;
139 
140  // update phi sums
141  double phi_center = my_data.layergeom->get_phicenter(iphi);
142  phi_sum += phi_center * adc;
143  phi2_sum += square(phi_center)*adc;
144 
145  // update z sums
146  double z = my_data.layergeom->get_zcenter(iz);
147  z_sum += z * adc;
148  z2_sum += square(z)*adc;
149 
150  adc_sum += adc;
151 
152  // capture the hitkeys for all adc values above a certain threshold
154  // if(adc>5)
155  hitkeyvec.push_back(hitkey);
156  }
157  // if (adc_sum < 10) return; // skip obvious noise "clusters"
158 
159  // This is the global position
160  double clusphi = phi_sum / adc_sum;
161  double clusz = z_sum / adc_sum;
162  const double clusx = radius * std::cos(clusphi);
163  const double clusy = radius * std::sin(clusphi);
164 
165  const double phi_cov = phi2_sum/adc_sum - square(clusphi);
166  const double z_cov = z2_sum/adc_sum - square(clusz);
167 
168  // create the cluster entry directly in the node tree
169  // Estimate the errors
170  const double phi_err_square = (phibinhi == phibinlo) ?
171  square(radius*my_data.layergeom->get_phistep())/12:
172  square(radius)*phi_cov/(adc_sum*0.14);
173 
174  const double z_err_square = (zbinhi == zbinlo) ?
175  square(my_data.layergeom->get_zstep())/12:
176  z_cov/(adc_sum*0.14);
177 
178  // phi_cov = (weighted mean of dphi^2) - (weighted mean of dphi)^2, which is essentially the weighted mean of dphi^2. The error is then:
179  // e_phi = sigma_dphi/sqrt(N) = sqrt( sigma_dphi^2 / N ) -- where N is the number of samples of the distribution with standard deviation sigma_dphi
180  // - N is the number of electrons that drift to the readout plane
181  // We have to convert (sum of adc units for all bins in the cluster) to number of ionization electrons N
182  // Conversion gain is 20 mV/fC - relates total charge collected on pad to PEAK voltage out of ADC. The GEM gain is assumed to be 2000
183  // To get equivalent charge per Z bin, so that summing ADC input voltage over all Z bins returns total input charge, divide voltages by 2.4 for 80 ns SAMPA
184  // Equivalent charge per Z bin is then (ADU x 2200 mV / 1024) / 2.4 x (1/20) fC/mV x (1/1.6e-04) electrons/fC x (1/2000) = ADU x 0.14
185 
186  // cluster z correction
187  clusz -= (clusz<0) ? my_data.par0_neg:my_data.par0_pos;
188 
189 
190  // create cluster and fill
191  auto clus = new TrkrClusterv3;
192  clus->setAdc(adc_sum);
193 
195  TrkrDefs::hitsetkey tpcHitSetKey = TpcDefs::genHitSetKey(my_data.layer, my_data.sector, my_data.side);
196 
197  Acts::Vector3 global(clusx, clusy, clusz);
198 
200  Surface surface = my_data.tGeometry->get_tpc_surface_from_coords(
201  tpcHitSetKey,
202  global,
203  subsurfkey);
204 
205  if(!surface)
206  {
209  return;
210  }
211 
212  clus->setSubSurfKey(subsurfkey);
213 
214  Acts::Vector3 center = surface->center(my_data.tGeometry->geometry().getGeoContext())
216 
218  const Acts::Vector3 normal = surface->normal(my_data.tGeometry->geometry().getGeoContext());
219  const double clusRadius = std::sqrt(square(clusx) + square(clusy));
220  const double rClusPhi = clusRadius * clusphi;
221  const double surfRadius = sqrt(center(0)*center(0) + center(1)*center(1));
222  const double surfPhiCenter = atan2(center[1], center[0]);
223  const double surfRphiCenter = surfPhiCenter * surfRadius;
224  const double surfZCenter = center[2];
225  auto local = surface->globalToLocal(my_data.tGeometry->geometry().getGeoContext(),
226  global * Acts::UnitConstants::cm,
227  normal);
228  Acts::Vector2 localPos;
229 
231  if(local.ok())
232  {
233  localPos = local.value() / Acts::UnitConstants::cm;
234  }
235  else
236  {
238  localPos(0) = rClusPhi - surfRphiCenter;
239  localPos(1) = clusz - surfZCenter;
240  }
241 
242  clus->setLocalX(localPos(0));
243  clus->setLocalY(localPos(1));
244  clus->setActsLocalError(0,0, phi_err_square);
245  clus->setActsLocalError(1,0, 0);
246  clus->setActsLocalError(0,1, 0);
247  clus->setActsLocalError(1,1, z_err_square);
248 
249  my_data.cluster_vector.push_back(clus);
250 
251  // Add the hit associations to the TrkrClusterHitAssoc node
252  // we need the cluster key and all associated hit keys (note: the cluster key includes the hitset key)
253 
254  if( my_data.do_assoc )
255  {
256  // get cluster index in vector. It is used to store associations, and build relevant cluster keys when filling the containers
257  uint32_t index = my_data.cluster_vector.size()-1;
258  for (unsigned int i = 0; i < hitkeyvec.size(); i++){
259  my_data.association_vector.push_back(std::make_pair(index, hitkeyvec[i]));
260  }
261  }
262  }
263 
264  void *ProcessSector(void *threadarg) {
265 
266  auto my_data = (struct thread_data *) threadarg;
267 
268  const auto& pedestal = my_data->pedestal;
269  const auto& phibins = my_data->phibins;
270  const auto& phioffset = my_data->phioffset;
271  const auto& zbins = my_data->zbins ;
272  const auto& zoffset = my_data->zoffset ;
273 
274  TrkrHitSet *hitset = my_data->hitset;
275  TrkrHitSet::ConstRange hitrangei = hitset->getHits();
276 
277  // for convenience, create a 2D vector to store adc values in and initialize to zero
278  std::vector<std::vector<unsigned short>> adcval(phibins, std::vector<unsigned short>(zbins, 0));
279  std::multimap<unsigned short, ihit> all_hit_map;
280  std::vector<ihit> hit_vect;
281 
282  for (TrkrHitSet::ConstIterator hitr = hitrangei.first;
283  hitr != hitrangei.second;
284  ++hitr){
285  unsigned short phibin = TpcDefs::getPad(hitr->first) - phioffset;
286  unsigned short zbin = TpcDefs::getTBin(hitr->first) - zoffset;
287 
288  float_t fadc = (hitr->second->getAdc()) - pedestal; // proper int rounding +0.5
289  //std::cout << " layer: " << my_data->layer << " phibin " << phibin << " zbin " << zbin << " fadc " << hitr->second->getAdc() << " pedestal " << pedestal << " fadc " << std::endl
290 
291  unsigned short adc = 0;
292  if(fadc>0)
293  adc = (unsigned short) fadc;
294 
295  // if(phibin < 0) continue; // phibin is unsigned int, <0 cannot happen
296  if(phibin >= phibins) continue;
297  // if(zbin < 0) continue;
298  if(zbin >= zbins) continue; // zbin is unsigned int, <0 cannot happen
299 
300  if(adc>0){
301  iphiz iCoord(std::make_pair(phibin,zbin));
302  ihit thisHit(adc,iCoord);
303  if(adc>5){
304  all_hit_map.insert(std::make_pair(adc, thisHit));
305  }
306  //adcval[phibin][zbin] = (unsigned short) adc;
307  adcval[phibin][zbin] = (unsigned short) adc;
308  }
309  }
310 
311  while(all_hit_map.size()>0){
312 
313  auto iter = all_hit_map.rbegin();
314  if(iter == all_hit_map.rend()){
315  break;
316  }
317  ihit hiHit = iter->second;
318  int iphi = hiHit.second.first;
319  int iz = hiHit.second.second;
320 
321  //put all hits in the all_hit_map (sorted by adc)
322  //start with highest adc hit
323  // -> cluster around it and get vector of hits
324  std::vector<ihit> ihit_list;
325  get_cluster(iphi, iz, adcval, ihit_list);
326 
327  // -> calculate cluster parameters
328  // -> add hits to truth association
329  // remove hits from all_hit_map
330  // repeat untill all_hit_map empty
331  calc_cluster_parameter(ihit_list, *my_data );
332  remove_hits(ihit_list,all_hit_map, adcval);
333  }
334  pthread_exit(nullptr);
335  }
336 }
337 
339  : SubsysReco(name)
340 {}
341 
342 bool TpcSimpleClusterizer::is_in_sector_boundary(int phibin, int sector, PHG4TpcCylinderGeom *layergeom) const
343 {
344  bool reject_it = false;
345 
346  // sector boundaries occur every 1/12 of the full phi bin range
347  int PhiBins = layergeom->get_phibins();
348  int PhiBinsSector = PhiBins/12;
349 
350  double radius = layergeom->get_radius();
351  double PhiBinSize = 2.0* radius * M_PI / (double) PhiBins;
352 
353  // sector starts where?
354  int sector_lo = sector * PhiBinsSector;
355  int sector_hi = sector_lo + PhiBinsSector - 1;
356 
357  int sector_fiducial_bins = (int) (SectorFiducialCut / PhiBinSize);
358 
359  if(phibin < sector_lo + sector_fiducial_bins || phibin > sector_hi - sector_fiducial_bins)
360  {
361  reject_it = true;
362  /*
363  int layer = layergeom->get_layer();
364  std::cout << " local maximum is in sector fiducial boundary: layer " << layer << " radius " << radius << " sector " << sector
365  << " PhiBins " << PhiBins << " sector_fiducial_bins " << sector_fiducial_bins
366  << " PhiBinSize " << PhiBinSize << " phibin " << phibin << " sector_lo " << sector_lo << " sector_hi " << sector_hi << std::endl;
367  */
368  }
369 
370  return reject_it;
371 }
372 
373 
375 {
376  PHNodeIterator iter(topNode);
377 
378  // Looking for the DST node
379  PHCompositeNode *dstNode = dynamic_cast<PHCompositeNode *>(iter.findFirst("PHCompositeNode", "DST"));
380  if (!dstNode)
381  {
382  std::cout << PHWHERE << "DST Node missing, doing nothing." << std::endl;
384  }
385 
386  // Create the Cluster node if required
387  auto trkrclusters = findNode::getClass<TrkrClusterContainer>(dstNode, "TRKR_CLUSTER");
388  if (!trkrclusters)
389  {
390  PHNodeIterator dstiter(dstNode);
391  PHCompositeNode *DetNode =
392  dynamic_cast<PHCompositeNode *>(dstiter.findFirst("PHCompositeNode", "TRKR"));
393  if (!DetNode)
394  {
395  DetNode = new PHCompositeNode("TRKR");
396  dstNode->addNode(DetNode);
397  }
398 
399  trkrclusters = new TrkrClusterContainerv4;
400  PHIODataNode<PHObject> *TrkrClusterContainerNode =
401  new PHIODataNode<PHObject>(trkrclusters, "TRKR_CLUSTER", "PHObject");
402  DetNode->addNode(TrkrClusterContainerNode);
403  }
404 
405  auto clusterhitassoc = findNode::getClass<TrkrClusterHitAssoc>(topNode, "TRKR_CLUSTERHITASSOC");
406  if (!clusterhitassoc)
407  {
408  PHNodeIterator dstiter(dstNode);
409  PHCompositeNode *DetNode =
410  dynamic_cast<PHCompositeNode *>(dstiter.findFirst("PHCompositeNode", "TRKR"));
411  if (!DetNode)
412  {
413  DetNode = new PHCompositeNode("TRKR");
414  dstNode->addNode(DetNode);
415  }
416 
417  clusterhitassoc = new TrkrClusterHitAssocv3;
418  PHIODataNode<PHObject> *newNode = new PHIODataNode<PHObject>(clusterhitassoc, "TRKR_CLUSTERHITASSOC", "PHObject");
419  DetNode->addNode(newNode);
420  }
421 
423 }
424 
426 {
427  // int print_layer = 18;
428 
429  if (Verbosity() > 1000)
430  std::cout << "TpcSimpleClusterizer::Process_Event" << std::endl;
431 
432  PHNodeIterator iter(topNode);
433  PHCompositeNode *dstNode = static_cast<PHCompositeNode *>(iter.findFirst("PHCompositeNode", "DST"));
434  if (!dstNode)
435  {
436  std::cout << PHWHERE << "DST Node missing, doing nothing." << std::endl;
438  }
439 
440  // get node containing the digitized hits
441  m_hits = findNode::getClass<TrkrHitSetContainer>(topNode, "TRKR_HITSET");
442  if (!m_hits)
443  {
444  std::cout << PHWHERE << "ERROR: Can't find node TRKR_HITSET" << std::endl;
446  }
447 
448  // get node for clusters
449  m_clusterlist = findNode::getClass<TrkrClusterContainer>(topNode, "TRKR_CLUSTER");
450  if (!m_clusterlist)
451  {
452  std::cout << PHWHERE << " ERROR: Can't find TRKR_CLUSTER." << std::endl;
454  }
455 
456  // get node for cluster hit associations
457  m_clusterhitassoc = findNode::getClass<TrkrClusterHitAssoc>(topNode, "TRKR_CLUSTERHITASSOC");
458  if (!m_clusterhitassoc)
459  {
460  std::cout << PHWHERE << " ERROR: Can't find TRKR_CLUSTERHITASSOC" << std::endl;
462  }
463 
464  PHG4TpcCylinderGeomContainer *geom_container =
465  findNode::getClass<PHG4TpcCylinderGeomContainer>(topNode, "CYLINDERCELLGEOM_SVTX");
466  if (!geom_container)
467  {
468  std::cout << PHWHERE << "ERROR: Can't find node CYLINDERCELLGEOM_SVTX" << std::endl;
470  }
471 
472  m_tGeometry = findNode::getClass<ActsGeometry>(topNode,
473  "ActsGeometry");
474  if(!m_tGeometry)
475  {
476  std::cout << PHWHERE
477  << "ActsGeometry not found on node tree. Exiting"
478  << std::endl;
480  }
481 
482  // The hits are stored in hitsets, where each hitset contains all hits in a given TPC readout (layer, sector, side), so clusters are confined to a hitset
483  // The TPC clustering is more complicated than for the silicon, because we have to deal with overlapping clusters
484 
485  // loop over the TPC HitSet objects
487  const int num_hitsets = std::distance(hitsetrange.first,hitsetrange.second);
488 
489  // create structure to store given thread and associated data
490  struct thread_pair_t
491  {
492  pthread_t thread;
493  thread_data data;
494  };
495 
496  // create vector of thread pairs and reserve the right size upfront to avoid reallocation
497  std::vector<thread_pair_t> threads;
498  threads.reserve( num_hitsets );
499 
500  pthread_attr_t attr;
501  pthread_attr_init(&attr);
502  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
503 
504  if (pthread_mutex_init(&mythreadlock, nullptr) != 0)
505  {
506  printf("\n mutex init failed\n");
507  return 1;
508  }
509 
510  for (TrkrHitSetContainer::ConstIterator hitsetitr = hitsetrange.first;
511  hitsetitr != hitsetrange.second;
512  ++hitsetitr)
513  {
514  TrkrHitSet *hitset = hitsetitr->second;
515  unsigned int layer = TrkrDefs::getLayer(hitsetitr->first);
516  int side = TpcDefs::getSide(hitsetitr->first);
517  unsigned int sector= TpcDefs::getSectorId(hitsetitr->first);
518  PHG4TpcCylinderGeom *layergeom = geom_container->GetLayerCellGeom(layer);
519 
520  // instanciate new thread pair, at the end of thread vector
521  thread_pair_t& thread_pair = threads.emplace_back();
522 
523  thread_pair.data.layergeom = layergeom;
524  thread_pair.data.hitset = hitset;
525  thread_pair.data.layer = layer;
526  thread_pair.data.pedestal = pedestal;
527  thread_pair.data.sector = sector;
528  thread_pair.data.side = side;
529  thread_pair.data.do_assoc = do_hit_assoc;
530  thread_pair.data.tGeometry = m_tGeometry;
531  thread_pair.data.par0_neg = par0_neg;
532  thread_pair.data.par0_pos = par0_pos;
533 
534  unsigned short NPhiBins = (unsigned short) layergeom->get_phibins();
535  unsigned short NPhiBinsSector = NPhiBins/12;
536  unsigned short NZBins = (unsigned short)layergeom->get_zbins();
537  unsigned short NZBinsSide = NZBins/2;
538  unsigned short NZBinsMin = 0;
539  unsigned short PhiOffset = NPhiBinsSector * sector;
540 
541  if (side == 0){
542  NZBinsMin = 0;
543  }
544  else{
545  NZBinsMin = NZBins / 2;
546  }
547 
548  unsigned short ZOffset = NZBinsMin;
549 
550  thread_pair.data.phibins = NPhiBinsSector;
551  thread_pair.data.phioffset = PhiOffset;
552  thread_pair.data.zbins = NZBinsSide;
553  thread_pair.data.zoffset = ZOffset ;
554 
555  int rc = pthread_create(&thread_pair.thread, &attr, ProcessSector, (void *)&thread_pair.data);
556  if (rc) {
557  std::cout << "Error:unable to create thread," << rc << std::endl;
558  }
559  }
560 
561  pthread_attr_destroy(&attr);
562 
563  // wait for completion of all threads
564  for( const auto& thread_pair:threads )
565  {
566  int rc2 = pthread_join(thread_pair.thread, nullptr);
567  if (rc2)
568  { std::cout << "Error:unable to join," << rc2 << std::endl; }
569 
570  // get the hitsetkey from thread data
571  const auto& data( thread_pair.data );
572  const auto hitsetkey = TpcDefs::genHitSetKey( data.layer, data.sector, data.side );
573 
574  // copy clusters to map
575  for( uint32_t index = 0; index < data.cluster_vector.size(); ++index )
576  {
577  // generate cluster key
578  const auto ckey = TrkrDefs::genClusKey( hitsetkey, index );
579 
580  // get cluster
581  auto cluster = data.cluster_vector[index];
582 
583  // insert in map
584  m_clusterlist->addClusterSpecifyKey(ckey, cluster);
585  }
586 
587  // copy hit associations to map
588  for( const auto& [index,hkey]:thread_pair.data.association_vector)
589  {
590  // generate cluster key
591  const auto ckey = TrkrDefs::genClusKey( hitsetkey, index );
592 
593  // add to association table
594  m_clusterhitassoc->addAssoc(ckey,hkey);
595  }
596 
597  }
598 
599  if (Verbosity() > 0)
600  std::cout << "TPC Clusterizer found " << m_clusterlist->size() << " Clusters " << std::endl;
602 }
603 
605 {
607 }