CMS 3D CMS Logo

PixelThresholdClusterizer.cc
Go to the documentation of this file.
1 //----------------------------------------------------------------------------
20 //----------------------------------------------------------------------------
21 
22 // Our own includes
24 #include "SiPixelArrayBuffer.h"
26 // Geometry
29 //#include "Geometry/CommonTopologies/RectangularPixelTopology.h"
30 
31 // STL
32 #include <stack>
33 #include <vector>
34 #include <iostream>
35 #include <atomic>
36 using namespace std;
37 
38 //----------------------------------------------------------------------------
42 //----------------------------------------------------------------------------
44  (edm::ParameterSet const& conf) :
45  // Get thresholds in electrons
46  thePixelThreshold( conf.getParameter<int>("ChannelThreshold") ),
47  theSeedThreshold( conf.getParameter<int>("SeedThreshold") ),
48  theClusterThreshold( conf.getParameter<int>("ClusterThreshold") ),
49  theClusterThreshold_L1( conf.getParameter<int>("ClusterThreshold_L1") ),
50  theConversionFactor( conf.getParameter<int>("VCaltoElectronGain") ),
51  theConversionFactor_L1( conf.getParameter<int>("VCaltoElectronGain_L1") ),
52  theOffset( conf.getParameter<int>("VCaltoElectronOffset") ),
53  theOffset_L1( conf.getParameter<int>("VCaltoElectronOffset_L1") ),
54  theElectronPerADCGain( conf.getParameter<double>("ElectronPerADCGain") ),
55  doPhase2Calibration( conf.getParameter<bool>("Phase2Calibration") ),
56  thePhase2ReadoutMode( conf.getParameter<int>("Phase2ReadoutMode") ),
57  thePhase2DigiBaseline( conf.getParameter<double>("Phase2DigiBaseline") ),
58  thePhase2KinkADC( conf.getParameter<int>("Phase2KinkADC") ),
59  theNumOfRows(0), theNumOfCols(0), theDetid(0),
60  // Get the constants for the miss-calibration studies
61  doMissCalibrate( conf.getParameter<bool>("MissCalibrate") ),
62  doSplitClusters( conf.getParameter<bool>("SplitClusters") )
63 {
64  theBuffer.setSize( theNumOfRows, theNumOfCols );
65 }
68 
69 
70 // Configuration descriptions
71 void
73  desc.add<int>("ChannelThreshold", 1000);
74  desc.add<bool>("MissCalibrate", true);
75  desc.add<bool>("SplitClusters", false);
76  desc.add<int>("VCaltoElectronGain", 65);
77  desc.add<int>("VCaltoElectronGain_L1", 65);
78  desc.add<int>("VCaltoElectronOffset", -414);
79  desc.add<int>("VCaltoElectronOffset_L1", -414);
80  desc.add<int>("SeedThreshold", 1000);
81  desc.add<int>("ClusterThreshold_L1", 4000);
82  desc.add<int>("ClusterThreshold", 4000);
83  desc.add<double>("ElectronPerADCGain", 135.);
84  desc.add<bool>("Phase2Calibration", false);
85  desc.add<int>("Phase2ReadoutMode", -1);
86  desc.add<double>("Phase2DigiBaseline", 1200.);
87  desc.add<int>("Phase2KinkADC", 8);
88 }
89 
90 //----------------------------------------------------------------------------
93 //----------------------------------------------------------------------------
95 {
96  // Cache the topology.
97  const PixelTopology & topol = pixDet->specificTopology();
98 
99  // Get the new sizes.
100  int nrows = topol.nrows(); // rows in x
101  int ncols = topol.ncolumns(); // cols in y
102 
103  theNumOfRows = nrows; // Set new sizes
104  theNumOfCols = ncols;
105 
106  if ( nrows > theBuffer.rows() ||
107  ncols > theBuffer.columns() )
108  { // change only when a larger is needed
109  //if( nrows != theNumOfRows || ncols != theNumOfCols ) {
110  //cout << " PixelThresholdClusterizer: pixel buffer redefined to "
111  // << nrows << " * " << ncols << endl;
112  //theNumOfRows = nrows; // Set new sizes
113  //theNumOfCols = ncols;
114  // Resize the buffer
115  theBuffer.setSize(nrows,ncols); // Modify
116  }
117 
118  return true;
119 }
120 //----------------------------------------------------------------------------
126 //----------------------------------------------------------------------------
127 template<typename T>
129  const PixelGeomDetUnit * pixDet,
130  const TrackerTopology* tTopo,
131  const std::vector<short>& badChannels,
133 
134  typename T::const_iterator begin = input.begin();
135  typename T::const_iterator end = input.end();
136 
137  // Do not bother for empty detectors
138  //if (begin == end) cout << " PixelThresholdClusterizer::clusterizeDetUnit - No digis to clusterize";
139 
140  // Set up the clusterization on this DetId.
141  if ( !setup(pixDet) )
142  return;
143 
144  theDetid = input.detId();
145 
146  // Set separate cluster threshold for L1 (needed for phase1)
147  auto clusterThreshold = theClusterThreshold;
148  theLayer = (DetId(theDetid).subdetId()==1) ? tTopo->pxbLayer(theDetid) : 0;
149  if (theLayer==1) clusterThreshold = theClusterThreshold_L1;
150 
151  // Copy PixelDigis to the buffer array; select the seed pixels
152  // on the way, and store them in theSeeds.
153  copy_to_buffer(begin, end);
154 
155  assert(output.empty());
156  // Loop over all seeds. TO DO: wouldn't using iterators be faster?
157  // edm::LogError("PixelThresholdClusterizer") << "Starting clusterizing" << endl;
158  for (unsigned int i = 0; i < theSeeds.size(); i++)
159  {
160 
161  // Gavril : The charge of seeds that were already inlcuded in clusters is set to 1 electron
162  // so we don't want to call "make_cluster" for these cases
163  if ( theBuffer(theSeeds[i]) >= theSeedThreshold )
164  { // Is this seed still valid?
165  // Make a cluster around this seed
166  SiPixelCluster && cluster = make_cluster( theSeeds[i] , output);
167 
168  // Check if the cluster is above threshold
169  // (TO DO: one is signed, other unsigned, gcc warns...)
170  if ( cluster.charge() >= clusterThreshold)
171  {
172  // std::cout << "putting in this cluster " << i << " " << cluster.charge() << " " << cluster.pixelADC().size() << endl;
173  // sort by row (x)
174  output.push_back( std::move(cluster) );
175  std::push_heap(output.begin(),output.end(),[](SiPixelCluster const & cl1,SiPixelCluster const & cl2) { return cl1.minPixelRow() < cl2.minPixelRow();});
176  }
177  }
178  }
179  // sort by row (x) maybe sorting the seed would suffice....
180  std::sort_heap(output.begin(),output.end(),[](SiPixelCluster const & cl1,SiPixelCluster const & cl2) { return cl1.minPixelRow() < cl2.minPixelRow();});
181 
182  // Erase the seeds.
183  theSeeds.clear();
184 
185  // Need to clean unused pixels from the buffer array.
186  clear_buffer(begin, end);
187 
188 }
189 
190 //----------------------------------------------------------------------------
198 //----------------------------------------------------------------------------
200 {
201  for(DigiIterator di = begin; di != end; ++di )
202  {
203  theBuffer.set_adc( di->row(), di->column(), 0 ); // reset pixel adc to 0
204  }
205 }
206 
208 {
209  for(ClusterIterator ci = begin; ci != end; ++ci )
210  {
211  for(int i = 0; i < ci->size(); ++i)
212  {
213  const SiPixelCluster::Pixel pixel = ci->pixel(i);
214 
215  theBuffer.set_adc( pixel.x, pixel.y, 0 ); // reset pixel adc to 0
216  }
217  }
218 }
219 
220 //----------------------------------------------------------------------------
222 //----------------------------------------------------------------------------
224 {
225 #ifdef PIXELREGRESSION
226  static std::atomic<int> s_ic=0;
227  in ic = ++s_ic;
228  if (ic==1) {
229  // std::cout << (doMissCalibrate ? "VI from db" : "VI linear") << std::endl;
230  }
231 #endif
232  int electron[end-begin]; // pixel charge in electrons
233  memset(electron, 0, sizeof(electron));
234 
235  if (doPhase2Calibration) {
236  int i = 0;
237  for(DigiIterator di = begin; di != end; ++di) {
238  electron[i] = calibrate(di->adc(), di->column(), di->row());
239  i++;
240  }
241  assert(i==(end-begin));
242  }
243 
244  else {
245  if ( doMissCalibrate ) {
246  if (theLayer==1) {
247  (*theSiPixelGainCalibrationService_).calibrate(theDetid,begin,end,theConversionFactor_L1, theOffset_L1,electron);
248  } else {
249  (*theSiPixelGainCalibrationService_).calibrate(theDetid,begin,end,theConversionFactor, theOffset, electron);
250  }
251  } else {
252  int i=0;
253  const float gain = theElectronPerADCGain; // default: 1 ADC = 135 electrons
254  for(DigiIterator di = begin; di != end; ++di) {
255  auto adc = di->adc();
256  const float pedestal = 0.; //
257  electron[i] = int(adc * gain + pedestal);
258  ++i;
259  }
260  assert(i==(end-begin));
261  }
262  }
263 
264  int i=0;
265 #ifdef PIXELREGRESSION
266  static std::atomic<int> eqD=0;
267 #endif
268  for(DigiIterator di = begin; di != end; ++di) {
269  int row = di->row();
270  int col = di->column();
271  int adc = electron[i++]; // this is in electrons
272 
273 #ifdef PIXELREGRESSION
274  int adcOld = calibrate(di->adc(),col,row);
275  //assert(adc==adcOld);
276  if (adc!=adcOld) std::cout << "VI " << eqD <<' '<< ic <<' '<< end-begin <<' '<< i <<' '<< di->adc() <<' ' << adc <<' '<< adcOld << std::endl; else ++eqD;
277 #endif
278 
279  if(adc<100) adc=100; // put all negative pixel charges into the 100 elec bin
280  /* This is semi-random good number. The exact number (in place of 100) is irrelevant from the point
281  of view of the final cluster charge since these are typically >= 20000.
282  */
283 
284  if ( adc >= thePixelThreshold) {
285  theBuffer.set_adc( row, col, adc);
286  if ( adc >= theSeedThreshold) theSeeds.push_back( SiPixelCluster::PixelPos(row,col) );
287  }
288  }
289  assert(i==(end-begin));
290 
291 }
292 
294 {
295  // loop over clusters
296  for(ClusterIterator ci = begin; ci != end; ++ci) {
297  // loop over pixels
298  for(int i = 0; i < ci->size(); ++i) {
299  const SiPixelCluster::Pixel pixel = ci->pixel(i);
300 
301  int row = pixel.x;
302  int col = pixel.y;
303  int adc = pixel.adc;
304  if ( adc >= thePixelThreshold) {
305  theBuffer.add_adc( row, col, adc);
306  if ( adc >= theSeedThreshold) theSeeds.push_back( SiPixelCluster::PixelPos(row,col) );
307  }
308  }
309  }
310 }
311 
312 //----------------------------------------------------------------------------
313 // Calibrate adc counts to electrons
314 //-----------------------------------------------------------------
316 {
317  int electrons = 0;
318 
319  if (doPhase2Calibration) {
320 
321  const float gain = theElectronPerADCGain;
322  int p2rm = (thePhase2ReadoutMode < -1 ? -1 : thePhase2ReadoutMode);
323 
324  if (p2rm == -1) {
325  electrons = int(adc * gain);
326  }
327  else {
328  if (adc < thePhase2KinkADC) {
329  electrons = int((adc - 0.5) * gain);
330  }
331  else {
332  const int dualslopeparam = (thePhase2ReadoutMode < 10 ? thePhase2ReadoutMode : 10);
333  const int dualslope = int(dualslopeparam <= 1 ? 1. : pow(2, dualslopeparam-1));
334  adc -= (thePhase2KinkADC-1);
335  adc *= dualslope;
336  adc += (thePhase2KinkADC-1);
337  electrons = int((adc - 0.5 * dualslope) * gain);
338  }
339  electrons += int(thePhase2DigiBaseline);
340  }
341 
342  return electrons;
343 
344  }
345 
346  if ( doMissCalibrate )
347  {
348  // do not perform calibration if pixel is dead!
349 
350  if ( !theSiPixelGainCalibrationService_->isDead(theDetid,col,row) &&
351  !theSiPixelGainCalibrationService_->isNoisy(theDetid,col,row) )
352  {
353 
354  // Linear approximation of the TANH response
355  // Pixel(0,0,0)
356  //const float gain = 2.95; // 1 ADC = 2.95 VCALs (1/0.339)
357  //const float pedestal = -83.; // -28/0.339
358  // Roc-0 average
359  //const float gain = 1./0.357; // 1 ADC = 2.80 VCALs
360  //const float pedestal = -28.2 * gain; // -79.
361 
362  float DBgain = theSiPixelGainCalibrationService_->getGain(theDetid, col, row);
363  float pedestal = theSiPixelGainCalibrationService_->getPedestal(theDetid, col, row);
364  float DBpedestal = pedestal * DBgain;
365 
366  // Roc-6 average
367  //const float gain = 1./0.313; // 1 ADC = 3.19 VCALs
368  //const float pedestal = -6.2 * gain; // -19.8
369  //
370  float vcal = adc * DBgain - DBpedestal;
371 
372  // atanh calibration
373  // Roc-6 average
374  //const float p0 = 0.00492;
375  //const float p1 = 1.998;
376  //const float p2 = 90.6;
377  //const float p3 = 134.1;
378  // Roc-6 average
379  //const float p0 = 0.00382;
380  //const float p1 = 0.886;
381  //const float p2 = 112.7;
382  //const float p3 = 113.0;
383  //float vcal = ( atanh( (adc-p3)/p2) + p1)/p0;
384 
385  if (theLayer==1) {
386  electrons = int( vcal * theConversionFactor_L1 + theOffset_L1);
387  } else {
388  electrons = int( vcal * theConversionFactor + theOffset);
389  }
390 
391  }
392  }
393  else
394  { // No misscalibration in the digitizer
395  // Simple (default) linear gain
396  const float gain = theElectronPerADCGain; // default: 1 ADC = 135 electrons
397  const float pedestal = 0.; //
398  electrons = int(adc * gain + pedestal);
399  }
400 
401  return electrons;
402 }
403 
404 //----------------------------------------------------------------------------
406 //----------------------------------------------------------------------------
410 {
411 
412  //First we acquire the seeds for the clusters
413  int seed_adc;
414  stack<SiPixelCluster::PixelPos, vector<SiPixelCluster::PixelPos> > dead_pixel_stack;
415 
416  //The individual modules have been loaded into a buffer.
417  //After each pixel has been considered by the clusterizer, we set the adc count to 1
418  //to mark that we have already considered it.
419  //The only difference between dead/noisy pixels and standard ones is that for dead/noisy pixels,
420  //We consider the charge of the pixel to always be zero.
421 
422  /* this is not possible as dead and noisy pixel cannot make it into a seed...
423  if ( doMissCalibrate &&
424  (theSiPixelGainCalibrationService_->isDead(theDetid,pix.col(),pix.row()) ||
425  theSiPixelGainCalibrationService_->isNoisy(theDetid,pix.col(),pix.row())) )
426  {
427  std::cout << "IMPOSSIBLE" << std::endl;
428  seed_adc = 0;
429  theBuffer.set_adc(pix, 1);
430  }
431  else {
432  */
433  seed_adc = theBuffer(pix.row(), pix.col());
434  theBuffer.set_adc( pix, 1);
435  // }
436 
437  AccretionCluster acluster;
438  acluster.add(pix, seed_adc);
439 
440  //Here we search all pixels adjacent to all pixels in the cluster.
441  bool dead_flag = false;
442  while ( ! acluster.empty())
443  {
444  //This is the standard algorithm to find and add a pixel
445  auto curInd = acluster.top(); acluster.pop();
446  for ( auto c = std::max(0,int(acluster.y[curInd])-1); c < std::min(int(acluster.y[curInd])+2,theBuffer.columns()) ; ++c) {
447  for ( auto r = std::max(0,int(acluster.x[curInd])-1); r < std::min(int(acluster.x[curInd])+2,theBuffer.rows()); ++r) {
448  if ( theBuffer(r,c) >= thePixelThreshold) {
449  SiPixelCluster::PixelPos newpix(r,c);
450  if (!acluster.add( newpix, theBuffer(r,c))) goto endClus;
451  theBuffer.set_adc( newpix, 1);
452  }
453 
454 
455  /* //Commenting out the addition of dead pixels to the cluster until further testing -- dfehling 06/09
456  //Check on the bounds of the module; this is to keep the isDead and isNoisy modules from returning errors
457  else if(r>= 0 && c >= 0 && (r <= (theNumOfRows-1.)) && (c <= (theNumOfCols-1.))){
458  //Check for dead/noisy pixels check that the buffer is not -1 (already considered). Check whether we want to split clusters separated by dead pixels or not.
459  if((theSiPixelGainCalibrationService_->isDead(theDetid,c,r) || theSiPixelGainCalibrationService_->isNoisy(theDetid,c,r)) && theBuffer(r,c) != 1){
460 
461  //If a pixel is dead or noisy, check to see if we want to split the clusters or not.
462  //Push it into a dead pixel stack in case we want to split the clusters. Otherwise add it to the cluster.
463  //If we are splitting the clusters, we will iterate over the dead pixel stack later.
464 
465  SiPixelCluster::PixelPos newpix(r,c);
466  if(!doSplitClusters){
467 
468  cluster.add(newpix, theBuffer(r,c));}
469  else if(doSplitClusters){
470  dead_pixel_stack.push(newpix);
471  dead_flag = true;}
472 
473  theBuffer.set_adc(newpix, 1);
474  }
475 
476  }
477  */
478 
479 
480 
481  }
482  }
483 
484  } // while accretion
485  endClus:
486  SiPixelCluster cluster(acluster.isize,acluster.adc, acluster.x,acluster.y, acluster.xmin,acluster.ymin);
487  //Here we split the cluster, if the flag to do so is set and we have found a dead or noisy pixel.
488 
489  if (dead_flag && doSplitClusters)
490  {
491  // Set separate cluster threshold for L1 (needed for phase1)
492  auto clusterThreshold = theClusterThreshold;
493  if (theLayer==1) clusterThreshold = theClusterThreshold_L1;
494 
495  //Set the first cluster equal to the existing cluster.
496  SiPixelCluster first_cluster = cluster;
497  bool have_second_cluster = false;
498  while ( !dead_pixel_stack.empty() )
499  {
500  //consider each found dead pixel
501  SiPixelCluster::PixelPos deadpix = dead_pixel_stack.top(); dead_pixel_stack.pop();
502  theBuffer.set_adc(deadpix, 1);
503 
504  //Clusterize the split cluster using the dead pixel as a seed
505  SiPixelCluster second_cluster = make_cluster(deadpix, output);
506 
507  //If both clusters would normally have been found by the clusterizer, put them into output
508  if ( second_cluster.charge() >= clusterThreshold &&
509  first_cluster.charge() >= clusterThreshold )
510  {
511  output.push_back( second_cluster );
512  have_second_cluster = true;
513  }
514 
515  //We also want to keep the merged cluster in data and let the RecHit algorithm decide which set to keep
516  //This loop adds the second cluster to the first.
517  const std::vector<SiPixelCluster::Pixel>& branch_pixels = second_cluster.pixels();
518  for ( unsigned int i = 0; i<branch_pixels.size(); i++)
519  {
520  int temp_x = branch_pixels[i].x;
521  int temp_y = branch_pixels[i].y;
522  int temp_adc = branch_pixels[i].adc;
523  SiPixelCluster::PixelPos newpix(temp_x, temp_y);
524  cluster.add(newpix, temp_adc);}
525  }
526 
527  //Remember to also add the first cluster if we added the second one.
528  if ( first_cluster.charge() >= clusterThreshold && have_second_cluster)
529  {
530  output.push_back( first_cluster );
531  std::push_heap(output.begin(),output.end(),[](SiPixelCluster const & cl1,SiPixelCluster const & cl2) { return cl1.minPixelRow() < cl2.minPixelRow();});
532  }
533  }
534 
535  return cluster;
536 }
537 
void clusterizeDetUnitT(const T &input, const PixelGeomDetUnit *pixDet, const TrackerTopology *tTopo, const std::vector< short > &badChannels, edmNew::DetSetVector< SiPixelCluster >::FastFiller &output)
Cluster pixels. This method operates on a matrix of pixels and finds the largest contiguous cluster a...
T getParameter(std::string const &) const
void push_back(data_type const &d)
virtual int nrows() const =0
static void fillPSetDescription(edm::ParameterSetDescription &desc)
PixelThresholdClusterizer(edm::ParameterSet const &conf)
SiPixelCluster make_cluster(const SiPixelCluster::PixelPos &pix, edmNew::DetSetVector< SiPixelCluster >::FastFiller &output)
The actual clustering algorithm: group the neighboring pixels around the seed.
def setup(process, global_tag, zero_tesla=False)
Definition: GeneralSetup.py:2
int charge() const
static std::string const input
Definition: EdmProvDump.cc:48
int minPixelRow() const
constexpr int subdetId() const
get the contents of the subdetector field (not cast into any detector&#39;s numbering enum) ...
Definition: DetId.h:41
edm::DetSet< PixelDigi >::const_iterator DigiIterator
#define end
Definition: vmac.h:39
void add(const PixelPos &pix, int adc)
T min(T a, T b)
Definition: MathUtil.h:58
constexpr int adc(sample_type sample)
get the ADC sample (12 bits)
ParameterDescriptionBase * add(U const &iLabel, T const &value)
bool add(SiPixelCluster::PixelPos const &p, UShort const iadc)
constexpr int col() const
unsigned int pxbLayer(const DetId &id) const
Definition: DetId.h:18
edmNew::DetSet< SiPixelCluster >::const_iterator ClusterIterator
void clear_buffer(DigiIterator begin, DigiIterator end)
Clear the internal buffer array.
virtual const PixelTopology & specificTopology() const
Returns a reference to the pixel proxy topology.
Pixel cluster – collection of neighboring pixels above threshold.
#define begin
Definition: vmac.h:32
virtual int ncolumns() const =0
col
Definition: cuy.py:1010
bool setup(const PixelGeomDetUnit *pixDet)
Private helper methods:
int calibrate(int adc, int col, int row)
long double T
Power< A, B >::type pow(const A &a, const B &b)
Definition: Power.h:40
constexpr int row() const
def move(src, dest)
Definition: eostools.py:511
const std::vector< Pixel > pixels() const
void copy_to_buffer(DigiIterator begin, DigiIterator end)
Copy adc counts from PixelDigis into the buffer, identify seeds.