CMS 3D CMS Logo

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