Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-04-06 12:26:19

0001 //----------------------------------------------------------------------------
0002 //! \class PixelThresholdClusterizer
0003 //! \brief A specific threshold-based pixel clustering algorithm
0004 //!
0005 //! General logic of PixelThresholdClusterizer:
0006 //!
0007 //! The clusterization is performed on a matrix with size
0008 //! equal to the size of the pixel detector, each cell containing
0009 //! the ADC count of the corresponding pixel.
0010 //! The matrix is reset after each clusterization.
0011 //!
0012 //! The search starts from seed pixels, i.e. pixels with sufficiently
0013 //! large amplitudes, found at the time of filling of the matrix
0014 //! and stored in a SiPixelArrayBuffer.
0015 //!
0016 //! Translate the pixel charge to electrons, we are suppose to
0017 //! do the calibrations ADC->electrons here.
0018 //! Modify the thresholds to be in electrons, convert adc to electrons. d.k. 20/3/06
0019 //! Get rid of the noiseVector. d.k. 28/3/06
0020 //----------------------------------------------------------------------------
0021 
0022 // Our own includes
0023 #include "PixelThresholdClusterizer.h"
0024 #include "SiPixelArrayBuffer.h"
0025 #include "CondFormats/SiPixelObjects/interface/SiPixelGainCalibrationOffline.h"
0026 // Geometry
0027 #include "Geometry/CommonDetUnit/interface/PixelGeomDetUnit.h"
0028 #include "Geometry/CommonTopologies/interface/PixelTopology.h"
0029 //#include "Geometry/CommonTopologies/RectangularPixelTopology.h"
0030 
0031 // STL
0032 #include <stack>
0033 #include <vector>
0034 #include <iostream>
0035 #include <atomic>
0036 #include <algorithm>
0037 #include <limits>
0038 
0039 //----------------------------------------------------------------------------
0040 //! Constructor:
0041 //!  Initilize the buffer to hold pixels from a detector module.
0042 //!  This is a vector of 44k ints, stays valid all the time.
0043 //----------------------------------------------------------------------------
0044 PixelThresholdClusterizer::PixelThresholdClusterizer(edm::ParameterSet const& conf)
0045     :  // Get thresholds in electrons
0046       thePixelThreshold(conf.getParameter<int>("ChannelThreshold")),
0047       theSeedThreshold(conf.getParameter<int>("SeedThreshold")),
0048       theClusterThreshold(conf.getParameter<int>("ClusterThreshold")),
0049       theClusterThreshold_L1(conf.getParameter<int>("ClusterThreshold_L1")),
0050       theConversionFactor(conf.getParameter<int>("VCaltoElectronGain")),
0051       theConversionFactor_L1(conf.getParameter<int>("VCaltoElectronGain_L1")),
0052       theOffset(conf.getParameter<int>("VCaltoElectronOffset")),
0053       theOffset_L1(conf.getParameter<int>("VCaltoElectronOffset_L1")),
0054       theElectronPerADCGain(conf.getParameter<double>("ElectronPerADCGain")),
0055       doPhase2Calibration(conf.getParameter<bool>("Phase2Calibration")),
0056       dropDuplicates(conf.getParameter<bool>("DropDuplicates")),
0057       thePhase2ReadoutMode(conf.getParameter<int>("Phase2ReadoutMode")),
0058       thePhase2DigiBaseline(conf.getParameter<double>("Phase2DigiBaseline")),
0059       thePhase2KinkADC(conf.getParameter<int>("Phase2KinkADC")),
0060       theNumOfRows(0),
0061       theNumOfCols(0),
0062       theDetid(0),
0063       // Get the constants for the miss-calibration studies
0064       doMissCalibrate(conf.getParameter<bool>("MissCalibrate")),
0065       doSplitClusters(conf.getParameter<bool>("SplitClusters")) {
0066   theBuffer.setSize(theNumOfRows, theNumOfCols);
0067   theFakePixels.clear();
0068   thePixelOccurrence.clear();
0069 }
0070 /////////////////////////////////////////////////////////////////////////////
0071 PixelThresholdClusterizer::~PixelThresholdClusterizer() {}
0072 
0073 // Configuration descriptions
0074 void PixelThresholdClusterizer::fillPSetDescription(edm::ParameterSetDescription& desc) {
0075   desc.add<int>("ChannelThreshold", 1000);
0076   desc.add<bool>("MissCalibrate", true);
0077   desc.add<bool>("SplitClusters", false);
0078   desc.add<int>("VCaltoElectronGain", 65);
0079   desc.add<int>("VCaltoElectronGain_L1", 65);
0080   desc.add<int>("VCaltoElectronOffset", -414);
0081   desc.add<int>("VCaltoElectronOffset_L1", -414);
0082   desc.add<int>("SeedThreshold", 1000);
0083   desc.add<int>("ClusterThreshold_L1", 4000);
0084   desc.add<int>("ClusterThreshold", 4000);
0085   desc.add<double>("ElectronPerADCGain", 135.);
0086   desc.add<bool>("DropDuplicates", true);
0087   desc.add<bool>("Phase2Calibration", false);
0088   desc.add<int>("Phase2ReadoutMode", -1);
0089   desc.add<double>("Phase2DigiBaseline", 1200.);
0090   desc.add<int>("Phase2KinkADC", 8);
0091 }
0092 
0093 //----------------------------------------------------------------------------
0094 //!  Prepare the Clusterizer to work on a particular DetUnit.  Re-init the
0095 //!  size of the panel/plaquette (so update nrows and ncols),
0096 //----------------------------------------------------------------------------
0097 bool PixelThresholdClusterizer::setup(const PixelGeomDetUnit* pixDet) {
0098   // Cache the topology.
0099   const PixelTopology& topol = pixDet->specificTopology();
0100 
0101   // Get the new sizes.
0102   int nrows = topol.nrows();     // rows in x
0103   int ncols = topol.ncolumns();  // cols in y
0104 
0105   theNumOfRows = nrows;  // Set new sizes
0106   theNumOfCols = ncols;
0107 
0108   if (nrows > theBuffer.rows() || ncols > theBuffer.columns()) {  // change only when a larger is needed
0109     if (nrows != theNumOfRows || ncols != theNumOfCols)
0110       edm::LogWarning("setup()") << "pixel buffer redefined to" << nrows << " * " << ncols;
0111     //theNumOfRows = nrows;  // Set new sizes
0112     //theNumOfCols = ncols;
0113     // Resize the buffer
0114     theBuffer.setSize(nrows, ncols);  // Modify
0115   }
0116 
0117   theFakePixels.resize(nrows * ncols, false);
0118 
0119   thePixelOccurrence.resize(nrows * ncols, 0);
0120 
0121   return true;
0122 }
0123 
0124 #include "PixelThresholdClusterizer.icc"
0125 
0126 //----------------------------------------------------------------------------
0127 //!  \brief Clear the internal buffer array.
0128 //!
0129 //!  Pixels which are not part of recognized clusters are NOT ERASED
0130 //!  during the cluster finding.  Erase them now.
0131 //!
0132 //!  TO DO: ask Danek... wouldn't it be faster to simply memcopy() zeros into
0133 //!  the whole buffer array?
0134 //----------------------------------------------------------------------------
0135 void PixelThresholdClusterizer::clear_buffer(DigiIterator begin, DigiIterator end) {
0136   for (DigiIterator di = begin; di != end; ++di) {
0137     theBuffer.set_adc(di->row(), di->column(), 0);  // reset pixel adc to 0
0138   }
0139 }
0140 
0141 void PixelThresholdClusterizer::clear_buffer(ClusterIterator begin, ClusterIterator end) {
0142   for (ClusterIterator ci = begin; ci != end; ++ci) {
0143     for (int i = 0; i < ci->size(); ++i) {
0144       const SiPixelCluster::Pixel pixel = ci->pixel(i);
0145 
0146       theBuffer.set_adc(pixel.x, pixel.y, 0);  // reset pixel adc to 0
0147     }
0148   }
0149 }
0150 
0151 //----------------------------------------------------------------------------
0152 //! \brief Copy adc counts from PixelDigis into the buffer, identify seeds.
0153 //----------------------------------------------------------------------------
0154 void PixelThresholdClusterizer::copy_to_buffer(DigiIterator begin, DigiIterator end) {
0155 #ifdef PIXELREGRESSION
0156   static std::atomic<int> s_ic = 0;
0157   in ic = ++s_ic;
0158   if (ic == 1) {
0159     // std::cout << (doMissCalibrate ? "VI from db" : "VI linear") << std::endl;
0160   }
0161 #endif
0162 
0163   //If called with empty/invalid DetSet, warn the user
0164   if (end <= begin) {
0165     edm::LogWarning("PixelThresholdClusterizer") << " copy_to_buffer called with empty or invalid range" << std::endl;
0166     return;
0167   }
0168 
0169   int electron[end - begin];  // pixel charge in electrons
0170   memset(electron, 0, (end - begin) * sizeof(int));
0171 
0172   if (doPhase2Calibration) {
0173     int i = 0;
0174     for (DigiIterator di = begin; di != end; ++di) {
0175       electron[i] = calibrate(di->adc(), di->column(), di->row());
0176       i++;
0177     }
0178     assert(i == (end - begin));
0179   }
0180 
0181   else {
0182     if (doMissCalibrate) {
0183       if (theLayer == 1) {
0184         (*theSiPixelGainCalibrationService_)
0185             .calibrate(theDetid, begin, end, theConversionFactor_L1, theOffset_L1, electron);
0186       } else {
0187         (*theSiPixelGainCalibrationService_).calibrate(theDetid, begin, end, theConversionFactor, theOffset, electron);
0188       }
0189     } else {
0190       int i = 0;
0191       const float gain = theElectronPerADCGain;  // default: 1 ADC = 135 electrons
0192       for (DigiIterator di = begin; di != end; ++di) {
0193         auto adc = di->adc();
0194         const float pedestal = 0.;  //
0195         electron[i] = int(adc * gain + pedestal);
0196         ++i;
0197       }
0198       assert(i == (end - begin));
0199     }
0200   }
0201 
0202   int i = 0;
0203 #ifdef PIXELREGRESSION
0204   static std::atomic<int> eqD = 0;
0205 #endif
0206   for (DigiIterator di = begin; di != end; ++di) {
0207     int row = di->row();
0208     int col = di->column();
0209     // VV: do not calibrate a fake pixel, it already has a unit of 10e-:
0210     int adc = (di->flag() != 0) ? di->adc() * 10 : electron[i];  // this is in electrons
0211     i++;
0212 
0213 #ifdef PIXELREGRESSION
0214     int adcOld = calibrate(di->adc(), col, row);
0215     //assert(adc==adcOld);
0216     if (adc != adcOld)
0217       std::cout << "VI " << eqD << ' ' << ic << ' ' << end - begin << ' ' << i << ' ' << di->adc() << ' ' << adc << ' '
0218                 << adcOld << std::endl;
0219     else
0220       ++eqD;
0221 #endif
0222 
0223     if (adc < 100)
0224       adc = 100;  // put all negative pixel charges into the 100 elec bin
0225     /* This is semi-random good number. The exact number (in place of 100) is irrelevant from the point
0226        of view of the final cluster charge since these are typically >= 20000.
0227     */
0228 
0229     thePixelOccurrence[theBuffer.index(row, col)]++;  // increment the occurrence counter
0230     uint8_t occurrence =
0231         (!dropDuplicates) ? 1 : thePixelOccurrence[theBuffer.index(row, col)];  // get the occurrence counter
0232 
0233     switch (occurrence) {
0234       // the 1st occurrence (standard treatment)
0235       case 1:
0236         if (adc >= thePixelThreshold) {
0237           theBuffer.set_adc(row, col, adc);
0238           // VV: add pixel to the fake list. Only when running on digi collection
0239           if (di->flag() != 0)
0240             theFakePixels[row * theNumOfCols + col] = true;
0241           if (adc >= theSeedThreshold)
0242             theSeeds.push_back(SiPixelCluster::PixelPos(row, col));
0243         }
0244         break;
0245 
0246       // the 2nd occurrence (duplicate pixel: reset the buffer to 0 and remove from the list of seed pixels)
0247       case 2:
0248         theBuffer.set_adc(row, col, 0);
0249         std::remove(theSeeds.begin(), theSeeds.end(), SiPixelCluster::PixelPos(row, col));
0250         break;
0251 
0252         // in case a pixel appears more than twice, nothing needs to be done because it was already removed at the 2nd occurrence
0253     }
0254   }
0255   assert(i == (end - begin));
0256 }
0257 
0258 void PixelThresholdClusterizer::copy_to_buffer(ClusterIterator begin, ClusterIterator end) {
0259   // loop over clusters
0260   for (ClusterIterator ci = begin; ci != end; ++ci) {
0261     // loop over pixels
0262     for (int i = 0; i < ci->size(); ++i) {
0263       const SiPixelCluster::Pixel pixel = ci->pixel(i);
0264 
0265       int row = pixel.x;
0266       int col = pixel.y;
0267       int adc = pixel.adc;
0268       if (adc >= thePixelThreshold) {
0269         theBuffer.add_adc(row, col, adc);
0270         if (adc >= theSeedThreshold)
0271           theSeeds.push_back(SiPixelCluster::PixelPos(row, col));
0272       }
0273     }
0274   }
0275 }
0276 
0277 //----------------------------------------------------------------------------
0278 // Calibrate adc counts to electrons
0279 //-----------------------------------------------------------------
0280 int PixelThresholdClusterizer::calibrate(int adc, int col, int row) {
0281   int electrons = 0;
0282 
0283   if (doPhase2Calibration) {
0284     const float gain = theElectronPerADCGain;
0285     int p2rm = (thePhase2ReadoutMode < -1 ? -1 : thePhase2ReadoutMode);
0286 
0287     if (p2rm == -1) {
0288       electrons = int(adc * gain);
0289     } else {
0290       if (adc < thePhase2KinkADC) {
0291         electrons = int((adc + 0.5) * gain);
0292       } else {
0293         const int dualslopeparam = (thePhase2ReadoutMode < 10 ? thePhase2ReadoutMode : 10);
0294         const int dualslope = int(dualslopeparam <= 1 ? 1. : pow(2, dualslopeparam - 1));
0295         adc -= thePhase2KinkADC;
0296         adc *= dualslope;
0297         adc += thePhase2KinkADC;
0298         electrons = int((adc + 0.5 * dualslope) * gain);
0299       }
0300       electrons += int(thePhase2DigiBaseline);
0301     }
0302 
0303     return electrons;
0304   }
0305 
0306   if (doMissCalibrate) {
0307     // do not perform calibration if pixel is dead!
0308 
0309     if (!theSiPixelGainCalibrationService_->isDead(theDetid, col, row) &&
0310         !theSiPixelGainCalibrationService_->isNoisy(theDetid, col, row)) {
0311       // Linear approximation of the TANH response
0312       // Pixel(0,0,0)
0313       //const float gain = 2.95; // 1 ADC = 2.95 VCALs (1/0.339)
0314       //const float pedestal = -83.; // -28/0.339
0315       // Roc-0 average
0316       //const float gain = 1./0.357; // 1 ADC = 2.80 VCALs
0317       //const float pedestal = -28.2 * gain; // -79.
0318 
0319       float DBgain = theSiPixelGainCalibrationService_->getGain(theDetid, col, row);
0320       float pedestal = theSiPixelGainCalibrationService_->getPedestal(theDetid, col, row);
0321       float DBpedestal = pedestal * DBgain;
0322 
0323       // Roc-6 average
0324       //const float gain = 1./0.313; // 1 ADC = 3.19 VCALs
0325       //const float pedestal = -6.2 * gain; // -19.8
0326       //
0327       float vcal = adc * DBgain - DBpedestal;
0328 
0329       // atanh calibration
0330       // Roc-6 average
0331       //const float p0 = 0.00492;
0332       //const float p1 = 1.998;
0333       //const float p2 = 90.6;
0334       //const float p3 = 134.1;
0335       // Roc-6 average
0336       //const float p0 = 0.00382;
0337       //const float p1 = 0.886;
0338       //const float p2 = 112.7;
0339       //const float p3 = 113.0;
0340       //float vcal = ( atanh( (adc-p3)/p2) + p1)/p0;
0341 
0342       if (theLayer == 1) {
0343         electrons = int(vcal * theConversionFactor_L1 + theOffset_L1);
0344       } else {
0345         electrons = int(vcal * theConversionFactor + theOffset);
0346       }
0347     }
0348   } else {  // No misscalibration in the digitizer
0349     // Simple (default) linear gain
0350     const float gain = theElectronPerADCGain;  // default: 1 ADC = 135 electrons
0351     const float pedestal = 0.;                 //
0352     electrons = int(adc * gain + pedestal);
0353   }
0354 
0355   return electrons;
0356 }
0357 
0358 //----------------------------------------------------------------------------
0359 //!  \brief The actual clustering algorithm: group the neighboring pixels around the seed.
0360 //----------------------------------------------------------------------------
0361 SiPixelCluster PixelThresholdClusterizer::make_cluster(const SiPixelCluster::PixelPos& pix,
0362                                                        edmNew::DetSetVector<SiPixelCluster>::FastFiller& output) {
0363   //First we acquire the seeds for the clusters
0364   uint16_t seed_adc;
0365   std::stack<SiPixelCluster::PixelPos, std::vector<SiPixelCluster::PixelPos> > dead_pixel_stack;
0366 
0367   //The individual modules have been loaded into a buffer.
0368   //After each pixel has been considered by the clusterizer, we set the adc count to 1
0369   //to mark that we have already considered it.
0370   //The only difference between dead/noisy pixels and standard ones is that for dead/noisy pixels,
0371   //We consider the charge of the pixel to always be zero.
0372 
0373   /*  this is not possible as dead and noisy pixel cannot make it into a seed...
0374   if ( doMissCalibrate &&
0375        (theSiPixelGainCalibrationService_->isDead(theDetid,pix.col(),pix.row()) ||
0376     theSiPixelGainCalibrationService_->isNoisy(theDetid,pix.col(),pix.row())) )
0377     {
0378       std::cout << "IMPOSSIBLE" << std::endl;
0379       seed_adc = 0;
0380       theBuffer.set_adc(pix, 1);
0381     }
0382     else {
0383   */
0384   // Note: each ADC value is limited here to 65535 (std::numeric_limits<uint16_t>::max),
0385   //       as it is later stored as uint16_t in SiPixelCluster and PixelClusterizerBase/AccretionCluster
0386   //       (reminder: ADC values here may be expressed in number of electrons)
0387   seed_adc = std::min(theBuffer(pix.row(), pix.col()), int(std::numeric_limits<uint16_t>::max()));
0388   theBuffer.set_adc(pix, 1);
0389   //  }
0390 
0391   AccretionCluster acluster, cldata;
0392   acluster.add(pix, seed_adc);
0393   cldata.add(pix, seed_adc);
0394 
0395   //Here we search all pixels adjacent to all pixels in the cluster.
0396   bool dead_flag = false;
0397   while (!acluster.empty()) {
0398     //This is the standard algorithm to find and add a pixel
0399     auto curInd = acluster.top();
0400     acluster.pop();
0401     for (auto c = std::max(0, int(acluster.y[curInd]) - 1);
0402          c < std::min(int(acluster.y[curInd]) + 2, theBuffer.columns());
0403          ++c) {
0404       for (auto r = std::max(0, int(acluster.x[curInd]) - 1);
0405            r < std::min(int(acluster.x[curInd]) + 2, theBuffer.rows());
0406            ++r) {
0407         if (theBuffer(r, c) >= thePixelThreshold) {
0408           SiPixelCluster::PixelPos newpix(r, c);
0409           auto const newpix_adc = std::min(theBuffer(r, c), int(std::numeric_limits<uint16_t>::max()));
0410           if (!acluster.add(newpix, newpix_adc))
0411             goto endClus;
0412           // VV: no fake pixels in cluster, leads to non-contiguous clusters
0413           if (!theFakePixels[r * theNumOfCols + c]) {
0414             cldata.add(newpix, newpix_adc);
0415           }
0416           theBuffer.set_adc(newpix, 1);
0417         }
0418 
0419         /* //Commenting out the addition of dead pixels to the cluster until further testing -- dfehling 06/09
0420           //Check on the bounds of the module; this is to keep the isDead and isNoisy modules from returning errors
0421           else if(r>= 0 && c >= 0 && (r <= (theNumOfRows-1.)) && (c <= (theNumOfCols-1.))){
0422           //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.
0423           if((theSiPixelGainCalibrationService_->isDead(theDetid,c,r) || theSiPixelGainCalibrationService_->isNoisy(theDetid,c,r)) && theBuffer(r,c) != 1){
0424 
0425           //If a pixel is dead or noisy, check to see if we want to split the clusters or not.
0426           //Push it into a dead pixel stack in case we want to split the clusters.  Otherwise add it to the cluster.
0427           //If we are splitting the clusters, we will iterate over the dead pixel stack later.
0428 
0429           SiPixelCluster::PixelPos newpix(r,c);
0430           if(!doSplitClusters){
0431 
0432           cluster.add(newpix, std::min(theBuffer(r, c), int(std::numeric_limits<uint16_t>::max())));}
0433           else if(doSplitClusters){
0434           dead_pixel_stack.push(newpix);
0435           dead_flag = true;}
0436 
0437           theBuffer.set_adc(newpix, 1);
0438           }
0439 
0440           }
0441           */
0442       }
0443     }
0444 
0445   }  // while accretion
0446 endClus:
0447   SiPixelCluster cluster(cldata.isize, cldata.adc, cldata.x, cldata.y, cldata.xmin, cldata.ymin);
0448   //Here we split the cluster, if the flag to do so is set and we have found a dead or noisy pixel.
0449 
0450   if (dead_flag && doSplitClusters) {
0451     // Set separate cluster threshold for L1 (needed for phase1)
0452     auto clusterThreshold = theClusterThreshold;
0453     if (theLayer == 1)
0454       clusterThreshold = theClusterThreshold_L1;
0455 
0456     //Set the first cluster equal to the existing cluster.
0457     SiPixelCluster first_cluster = cluster;
0458     bool have_second_cluster = false;
0459     while (!dead_pixel_stack.empty()) {
0460       //consider each found dead pixel
0461       SiPixelCluster::PixelPos deadpix = dead_pixel_stack.top();
0462       dead_pixel_stack.pop();
0463       theBuffer.set_adc(deadpix, 1);
0464 
0465       //Clusterize the split cluster using the dead pixel as a seed
0466       SiPixelCluster second_cluster = make_cluster(deadpix, output);
0467 
0468       //If both clusters would normally have been found by the clusterizer, put them into output
0469       if (second_cluster.charge() >= clusterThreshold && first_cluster.charge() >= clusterThreshold) {
0470         output.push_back(second_cluster);
0471         have_second_cluster = true;
0472       }
0473 
0474       //We also want to keep the merged cluster in data and let the RecHit algorithm decide which set to keep
0475       //This loop adds the second cluster to the first.
0476       const std::vector<SiPixelCluster::Pixel>& branch_pixels = second_cluster.pixels();
0477       for (unsigned int i = 0; i < branch_pixels.size(); i++) {
0478         auto const temp_x = branch_pixels[i].x;
0479         auto const temp_y = branch_pixels[i].y;
0480         auto const temp_adc = branch_pixels[i].adc;
0481         SiPixelCluster::PixelPos newpix(temp_x, temp_y);
0482         cluster.add(newpix, temp_adc);
0483       }
0484     }
0485 
0486     //Remember to also add the first cluster if we added the second one.
0487     if (first_cluster.charge() >= clusterThreshold && have_second_cluster) {
0488       output.push_back(first_cluster);
0489       std::push_heap(output.begin(), output.end(), [](SiPixelCluster const& cl1, SiPixelCluster const& cl2) {
0490         return cl1.minPixelRow() < cl2.minPixelRow();
0491       });
0492     }
0493   }
0494 
0495   return cluster;
0496 }