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