Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
TpcPrototypeClusterizer.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file TpcPrototypeClusterizer.cc
2 
3 #include <tpc/TpcDefs.h>
4 
8 #include <trackbase/TrkrDefs.h> // for hitkey, getLayer
9 #include <trackbase/TrkrHit.h>
10 #include <trackbase/TrkrHitSet.h>
12 
14 #include <fun4all/SubsysReco.h>
15 
18 
19 #include <phool/PHCompositeNode.h>
20 #include <phool/PHIODataNode.h>
21 #include <phool/PHNode.h>
22 #include <phool/PHNodeIterator.h>
23 #include <phool/PHObject.h>
24 #include <phool/getClass.h>
25 #include <phool/phool.h> // for PHWHERE
26 
27 #include <TMatrixFfwd.h> // for TMatrixF
28 #include <TMatrixT.h> // for TMatrixT, ope...
29 #include <TMatrixTUtils.h> // for TMatrixTRow
30 
31 #include <cmath> // for sqrt, cos, sin
32 #include <iostream>
33 #include <map> // for _Rb_tree_cons...
34 #include <string>
35 #include <utility> // for pair
36 #include <vector>
37 
38 using namespace std;
39 
41  : SubsysReco(name)
42  , m_hits(nullptr)
43  , m_clusterlist(nullptr)
44  , m_clusterhitassoc(nullptr)
45  , zz_shaping_correction(0.0754)
46  , pedestal(74.4)
47  , NPhiBinsMax(0)
48  , NPhiBinsMin(0)
49  , NZBinsMax(0)
50  , NZBinsMin(0)
51 {
52 }
53 
54 //===================
55 bool TpcPrototypeClusterizer::is_local_maximum(int phibin, int zbin, std::vector<std::vector<double>> &adcval)
56 {
57  bool retval = true;
58  double centval = adcval[phibin][zbin];
59  //cout << "enter is_local_maximum for phibin " << phibin << " zbin " << zbin << " adcval " << centval << endl;
60 
61  // search contiguous adc values for a larger signal
62  for (int iz = zbin - 4; iz <= zbin + 4; iz++)
63  for (int iphi = phibin - 2; iphi <= phibin + 2; iphi++)
64  {
65  //cout << " is_local_maximum: iphi " << iphi << " iz " << iz << " adcval " << adcval[iphi][iz] << endl;
66  if (iz >= NZBinsMax) continue;
67  if (iz < NZBinsMin) continue;
68 
69  if (iphi >= NPhiBinsMax) continue;
70  if (iphi < NPhiBinsMin) continue;
71 
72  if (adcval[iphi][iz] > centval)
73  {
74  retval = false;
75  }
76  }
77 
78  //if(retval) cout << "********************** success: returning " << retval << endl;
79 
80  return retval;
81 }
82 
83 void TpcPrototypeClusterizer::get_cluster(int phibin, int zbin, int &phiup, int &phidown, int &zup, int &zdown, std::vector<std::vector<double>> &adcval)
84 {
85  // search along phi at the peak in z
86 
87  for (int iphi = phibin + 1; iphi < phibin + 4; iphi++)
88  {
89  if (iphi >= NPhiBinsMax) continue;
90 
91  if (adcval[iphi][zbin] <= 0)
92  break;
93 
94  if (adcval[iphi][zbin] <= adcval[iphi - 1][zbin])
95  phiup++;
96  else
97  break;
98  }
99 
100  for (int iphi = phibin - 1; iphi > phibin - 4; iphi--)
101  {
102  if (iphi < NPhiBinsMin) continue;
103 
104  if (adcval[iphi][zbin] <= 0)
105  break;
106 
107  if (adcval[iphi][zbin] <= adcval[iphi + 1][zbin])
108  phidown++;
109  else
110  break;
111  }
112 
113  // search along z at the peak in phi
114 
115  for (int iz = zbin + 1; iz < zbin + 10; iz++)
116  {
117  if (iz >= NZBinsMax) continue;
118 
119  if (adcval[phibin][iz] <= 0)
120  break;
121 
122  if (adcval[phibin][iz] <= adcval[phibin][iz - 1])
123  zup++;
124  else
125  break;
126  }
127 
128  for (int iz = zbin - 1; iz > zbin - 10; iz--)
129  {
130  if (iz < NZBinsMin) continue;
131 
132  if (adcval[phibin][iz] <= 0)
133  break;
134 
135  if (adcval[phibin][iz] <= adcval[phibin][iz + 1])
136  zdown++;
137  else
138  break;
139  }
140 }
141 
143 {
144  PHNodeIterator iter(topNode);
145 
146  // Looking for the DST node
147  PHCompositeNode *dstNode = dynamic_cast<PHCompositeNode *>(iter.findFirst("PHCompositeNode", "DST"));
148  if (!dstNode)
149  {
150  cout << PHWHERE << "DST Node missing, doing nothing." << endl;
152  }
153 
154  // Create the Cluster node if required
155  TrkrClusterContainer *trkrclusters = findNode::getClass<TrkrClusterContainer>(dstNode, "TRKR_CLUSTER");
156  if (!trkrclusters)
157  {
158  PHNodeIterator dstiter(dstNode);
159  PHCompositeNode *DetNode =
160  dynamic_cast<PHCompositeNode *>(dstiter.findFirst("PHCompositeNode", "TRKR"));
161  if (!DetNode)
162  {
163  DetNode = new PHCompositeNode("TRKR");
164  dstNode->addNode(DetNode);
165  }
166 
167  trkrclusters = new TrkrClusterContainer();
168  PHIODataNode<PHObject> *TrkrClusterContainerNode =
169  new PHIODataNode<PHObject>(trkrclusters, "TRKR_CLUSTER", "PHObject");
170  DetNode->addNode(TrkrClusterContainerNode);
171  }
172 
173  TrkrClusterHitAssoc *clusterhitassoc = findNode::getClass<TrkrClusterHitAssoc>(topNode, "TRKR_CLUSTERHITASSOC");
174  if (!clusterhitassoc)
175  {
176  PHNodeIterator dstiter(dstNode);
177  PHCompositeNode *DetNode =
178  dynamic_cast<PHCompositeNode *>(dstiter.findFirst("PHCompositeNode", "TRKR"));
179  if (!DetNode)
180  {
181  DetNode = new PHCompositeNode("TRKR");
182  dstNode->addNode(DetNode);
183  }
184 
185  clusterhitassoc = new TrkrClusterHitAssoc();
186  PHIODataNode<PHObject> *newNode = new PHIODataNode<PHObject>(clusterhitassoc, "TRKR_CLUSTERHITASSOC", "PHObject");
187  DetNode->addNode(newNode);
188  }
189 
191 }
192 
194 {
195  int print_layer = 47;
196 
197  if (Verbosity() > 1000)
198  std::cout << "TpcPrototypeClusterizer::Process_Event" << std::endl;
199 
200  PHNodeIterator iter(topNode);
201  PHCompositeNode *dstNode = static_cast<PHCompositeNode *>(iter.findFirst("PHCompositeNode", "DST"));
202  if (!dstNode)
203  {
204  cout << PHWHERE << "DST Node missing, doing nothing." << endl;
206  }
207 
208  // get node containing the digitized hits
209  m_hits = findNode::getClass<TrkrHitSetContainer>(topNode, "TRKR_HITSET");
210  if (!m_hits)
211  {
212  cout << PHWHERE << "ERROR: Can't find node TRKR_HITSET" << endl;
214  }
215 
216  // get node for clusters
217  m_clusterlist = findNode::getClass<TrkrClusterContainer>(topNode, "TRKR_CLUSTER");
218  if (!m_clusterlist)
219  {
220  cout << PHWHERE << " ERROR: Can't find TRKR_CLUSTER." << endl;
222  }
223 
224  // get node for cluster hit associations
225  m_clusterhitassoc = findNode::getClass<TrkrClusterHitAssoc>(topNode, "TRKR_CLUSTERHITASSOC");
226  if (!m_clusterhitassoc)
227  {
228  cout << PHWHERE << " ERROR: Can't find TRKR_CLUSTERHITASSOC" << endl;
230  }
231 
232  PHG4CylinderCellGeomContainer *geom_container =
233  findNode::getClass<PHG4CylinderCellGeomContainer>(topNode, "CYLINDERCELLGEOM_SVTX");
234  if (!geom_container)
235  {
236  std::cout << PHWHERE << "ERROR: Can't find node CYLINDERCELLGEOM_SVTX" << std::endl;
238  }
239 
240  // 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
241  // The TPC clustering is more complicated than for the silicon, because we have to deal with overlapping clusters
242 
243  // loop over the TPC HitSet objects
245  for (TrkrHitSetContainer::ConstIterator hitsetitr = hitsetrange.first;
246  hitsetitr != hitsetrange.second;
247  ++hitsetitr)
248  {
249  TrkrHitSet *hitset = hitsetitr->second;
250  if (Verbosity() > 1)
251  cout << "TpcPrototypeClusterizer process hitsetkey " << hitsetitr->first
252  << " layer " << (int) TrkrDefs::getLayer(hitsetitr->first)
253  << " side " << (int) TpcDefs::getSide(hitsetitr->first)
254  << " sector " << (int) TpcDefs::getSectorId(hitsetitr->first)
255  << endl;
256  if (Verbosity() > 2) hitset->identify();
257 
258  // we have a single hitset, get the info that identifies the module
259  int layer = TrkrDefs::getLayer(hitsetitr->first);
260  // int sector = TpcDefs::getSectorId(hitsetitr->first);
261  int side = TpcDefs::getSide(hitsetitr->first);
262 
263  // we will need the geometry object for this layer to get the global position
264  PHG4CylinderCellGeom *layergeom = geom_container->GetLayerCellGeom(layer);
265  int NPhiBins = layergeom->get_phibins();
266  NPhiBinsMin = 0;
267  NPhiBinsMax = NPhiBins;
268 
269  int NZBins = layergeom->get_zbins();
270  if (side == 0)
271  {
272  NZBinsMin = 0;
273  NZBinsMax = NZBins / 2;
274  }
275  else
276  {
277  NZBinsMin = NZBins / 2 + 1;
278  NZBinsMax = NZBins;
279  }
280 
281  // for convenience, create a 2D vector to store adc values in and initialize to zero
282  std::vector<std::vector<double>> adcval(NPhiBins, std::vector<double>(NZBins, 0));
283 
284  TrkrHitSet::ConstRange hitrangei = hitset->getHits();
285  for (TrkrHitSet::ConstIterator hitr = hitrangei.first;
286  hitr != hitrangei.second;
287  ++hitr)
288  {
289  int phibin = TpcDefs::getPad(hitr->first);
290  int zbin = TpcDefs::getTBin(hitr->first);
291  if (hitr->second->getAdc() > 0)
292  adcval[phibin][zbin] = (double) hitr->second->getAdc() - pedestal;
293 
294  if (Verbosity() > 2)
295  // if (layer == print_layer)
296  cout << " add hit in layer " << layer << " with phibin " << phibin << " zbin " << zbin << " adcval " << hitr->second->getAdc() << "->" << adcval[phibin][zbin] << endl;
297  }
298 
299  vector<int> phibinlo;
300  vector<int> phibinhi;
301  vector<int> zbinlo;
302  vector<int> zbinhi;
303  // we want to search the hit list for local maxima in phi-z space and cluster around them
304  for (TrkrHitSet::ConstIterator hitr = hitrangei.first;
305  hitr != hitrangei.second;
306  ++hitr)
307  {
308  int phibin = TpcDefs::getPad(hitr->first);
309  int zbin = TpcDefs::getTBin(hitr->first);
310  if (!is_local_maximum(phibin, zbin, adcval)) continue;
311 
312  int phiup = 0;
313  int phidown = 0;
314  int zup = 0;
315  int zdown = 0;
316  // cluster the hits around this local maximum
317  get_cluster(phibin, zbin, phiup, phidown, zup, zdown, adcval);
318 
319  if (phiup == 0 && phidown == 0 && zup == 0 and zdown == 0) continue; // ignore isolated noise hit
320 
321  // Add this cluster to a vector of clusters for later analysis
322  phibinlo.push_back(phibin - phidown);
323  phibinhi.push_back(phibin + phiup);
324  zbinlo.push_back(zbin - zdown);
325  zbinhi.push_back(zbin + zup);
326 
327  if (Verbosity() > 2)
328  // if (layer == print_layer)
329  cout << " cluster found in layer " << layer << " around hitkey " << hitr->first << " with zbin " << zbin << " zup " << zup << " zdown " << zdown
330  << " phibin " << phibin << " phiup " << phiup << " phidown " << phidown << endl;
331  } // end loop over hits in this hitset
332 
333  // Now we analyze the clusters to get their parameters
334  for (unsigned int iclus = 0; iclus < phibinlo.size(); iclus++)
335  {
336  //cout << "TpcPrototypeClusterizer: process cluster iclus = " << iclus << " in layer " << layer << endl;
337  // loop over the hits in this cluster
338  double zsum = 0.0;
339  double phi_sum = 0.0;
340  double adc_sum = 0.0;
341  double radius = layergeom->get_radius(); // returns center of layer
342  if (Verbosity() > 2)
343  if (layer == print_layer)
344  {
345  cout << "iclus " << iclus << " layer " << layer << " radius " << radius << endl;
346  cout << " z bin range " << zbinlo[iclus] << " to " << zbinhi[iclus] << " phibin range " << phibinlo[iclus] << " to " << phibinhi[iclus] << endl;
347  }
348 
349  std::vector<TrkrDefs::hitkey> hitkeyvec;
350  for (int iphi = phibinlo[iclus]; iphi <= phibinhi[iclus]; iphi++)
351  {
352  for (int iz = zbinlo[iclus]; iz <= zbinhi[iclus]; iz++)
353  {
354  double phi_center = layergeom->get_phicenter(iphi);
355  double z = layergeom->get_zcenter(iz);
356 
357  zsum += z * adcval[iphi][iz];
358  phi_sum += phi_center * adcval[iphi][iz];
359  adc_sum += adcval[iphi][iz];
360 
361  // capture the hitkeys for all non-zero adc values
362  if (adcval[iphi][iz] != 0)
363  {
365  hitkeyvec.push_back(hitkey);
366  }
367  }
368  }
369 
370  if (adc_sum < 10) continue; // skip obvious noise "clusters"
371 
372  // This is the global position
373  double clusphi = phi_sum / adc_sum;
374  double clusz = zsum / adc_sum;
375 
376  // create the cluster entry directly in the node tree
377  TrkrDefs::cluskey ckey = TpcDefs::genClusKey(hitset->getHitSetKey(), iclus);
378  TrkrClusterv1 *clus = static_cast<TrkrClusterv1 *>((m_clusterlist->findOrAddCluster(ckey))->second);
379 
380  double phi_size = (double) (phibinhi[iclus] - phibinlo[iclus] + 1) * radius * layergeom->get_phistep();
381  double z_size = (double) (zbinhi[iclus] - zbinlo[iclus] + 1) * layergeom->get_zstep();
382 
383  // Estimate the errors
384  double dphi2_adc = 0.0;
385  double dphi_adc = 0.0;
386  double dz2_adc = 0.0;
387  double dz_adc = 0.0;
388  for (int iz = zbinlo[iclus]; iz <= zbinhi[iclus]; iz++)
389  {
390  for (int iphi = phibinlo[iclus]; iphi <= phibinhi[iclus]; iphi++)
391  {
392  double dphi = layergeom->get_phicenter(iphi) - clusphi;
393  dphi2_adc += dphi * dphi * adcval[iphi][iz];
394  dphi_adc += dphi * adcval[iphi][iz];
395 
396  double dz = layergeom->get_zcenter(iz) - clusz;
397  dz2_adc += dz * dz * adcval[iphi][iz];
398  dz_adc += dz * adcval[iphi][iz];
399  }
400  }
401  double phi_cov = (dphi2_adc / adc_sum - dphi_adc * dphi_adc / (adc_sum * adc_sum));
402  double z_cov = dz2_adc / adc_sum - dz_adc * dz_adc / (adc_sum * adc_sum);
403 
404  //cout << " layer " << layer << " z_cov " << z_cov << " dz2_adc " << dz2_adc << " adc_sum " << adc_sum << " dz_adc " << dz_adc << endl;
405 
406  // 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:
407  // 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
408  // - N is the number of electrons that drift to the readout plane
409  // We have to convert (sum of adc units for all bins in the cluster) to number of ionization electrons N
410  // 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
411  // 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
412  // 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
413 
414  double phi_err = radius * sqrt(phi_cov / (adc_sum * 0.14));
415  if (phi_err == 0.0) // a single phi bin will cause this
416  phi_err = radius * layergeom->get_phistep() / sqrt(12.0);
417 
418  double z_err = sqrt(z_cov / (adc_sum * 0.14));
419  if (z_err == 0.0)
420  z_err = layergeom->get_zstep() / sqrt(12.0);
421 
422  // This corrects the bias introduced by the asymmetric SAMPA chip shaping - assumes 80 ns shaping time
423  if (clusz < 0)
424  clusz -= zz_shaping_correction;
425  else
426  clusz += zz_shaping_correction;
427 
428  // Fill in the cluster details
429  //================
430  clus->setAdc(adc_sum);
431  clus->setPosition(0, radius * cos(clusphi));
432  clus->setPosition(1, radius * sin(clusphi));
433  clus->setPosition(2, clusz);
434  clus->setGlobal();
435 
436  TMatrixF DIM(3, 3);
437  DIM[0][0] = 0.0;
438  DIM[0][1] = 0.0;
439  DIM[0][2] = 0.0;
440  DIM[1][0] = 0.0;
441  DIM[1][1] = phi_size / radius * phi_size / radius; //cluster_v1 expects polar coordinates covariance
442  DIM[1][2] = 0.0;
443  DIM[2][0] = 0.0;
444  DIM[2][1] = 0.0;
445  DIM[2][2] = z_size * z_size;
446 
447  TMatrixF ERR(3, 3);
448  ERR[0][0] = 0.0;
449  ERR[0][1] = 0.0;
450  ERR[0][2] = 0.0;
451  ERR[1][0] = 0.0;
452  ERR[1][1] = phi_err * phi_err; //cluster_v1 expects rad, arc, z as elementsof covariance
453  ERR[1][2] = 0.0;
454  ERR[2][0] = 0.0;
455  ERR[2][1] = 0.0;
456  ERR[2][2] = z_err * z_err;
457 
458  TMatrixF ROT(3, 3);
459  ROT[0][0] = cos(clusphi);
460  ROT[0][1] = -sin(clusphi);
461  ROT[0][2] = 0.0;
462  ROT[1][0] = sin(clusphi);
463  ROT[1][1] = cos(clusphi);
464  ROT[1][2] = 0.0;
465  ROT[2][0] = 0.0;
466  ROT[2][1] = 0.0;
467  ROT[2][2] = 1.0;
468 
469  TMatrixF ROT_T(3, 3);
470  ROT_T.Transpose(ROT);
471 
472  TMatrixF COVAR_DIM(3, 3);
473  COVAR_DIM = ROT * DIM * ROT_T;
474 
475  clus->setSize(0, 0, COVAR_DIM[0][0]);
476  clus->setSize(0, 1, COVAR_DIM[0][1]);
477  clus->setSize(0, 2, COVAR_DIM[0][2]);
478  clus->setSize(1, 0, COVAR_DIM[1][0]);
479  clus->setSize(1, 1, COVAR_DIM[1][1]);
480  clus->setSize(1, 2, COVAR_DIM[1][2]);
481  clus->setSize(2, 0, COVAR_DIM[2][0]);
482  clus->setSize(2, 1, COVAR_DIM[2][1]);
483  clus->setSize(2, 2, COVAR_DIM[2][2]);
484  //cout << " covar_dim[2][2] = " << COVAR_DIM[2][2] << endl;
485 
486  TMatrixF COVAR_ERR(3, 3);
487  COVAR_ERR = ROT * ERR * ROT_T;
488 
489  clus->setError(0, 0, COVAR_ERR[0][0]);
490  clus->setError(0, 1, COVAR_ERR[0][1]);
491  clus->setError(0, 2, COVAR_ERR[0][2]);
492  clus->setError(1, 0, COVAR_ERR[1][0]);
493  clus->setError(1, 1, COVAR_ERR[1][1]);
494  clus->setError(1, 2, COVAR_ERR[1][2]);
495  clus->setError(2, 0, COVAR_ERR[2][0]);
496  clus->setError(2, 1, COVAR_ERR[2][1]);
497  clus->setError(2, 2, COVAR_ERR[2][2]);
498 
499  /*
500  for(int i=0;i<3;i++)
501  for(int j=0;j<3;j++)
502  cout << " i " << i << " j " << j << " clusphi " << clusphi << " clus error " << clus->getError(i,j)
503  << " clus size " << clus->getSize(i,j) << " ROT " << ROT[i][j] << " ROT_T " << ROT_T[i][j] << " COVAR_ERR " << COVAR_ERR[i][j] << endl;
504  */
505 
506  // Add the hit associations to the TrkrClusterHitAssoc node
507  // we need the cluster key and all associated hit keys (note: the cluster key includes the hitset key)
508  for (unsigned int i = 0; i < hitkeyvec.size(); i++)
509  {
510  m_clusterhitassoc->addAssoc(ckey, hitkeyvec[i]);
511  }
512 
513  } // end loop over clusters for this hitset
514  } // end loop over hitsets
515 
516  if (Verbosity() > 2)
517  {
518  cout << "Dump clusters after TpcPrototypeClusterizer" << endl;
520  }
521 
522  if (Verbosity() > 2)
523  {
524  cout << "Dump cluster hit associations after TpcPrototypeClusterizer" << endl;
526  }
527 
529 }