CMS 3D CMS Logo

HGCHEbackDigitizer.cc
Go to the documentation of this file.
7 
8 #include "CLHEP/Random/RandPoissonQ.h"
9 #include "CLHEP/Random/RandGaussQ.h"
10 #include "vdt/vdtMath.h"
11 
12 using namespace hgc_digi;
13 using namespace hgc_digi_utils;
14 
16 public:
18  void runDigitizer(std::unique_ptr<HGCalDigiCollection>& digiColl,
20  const CaloSubdetectorGeometry* theGeom,
21  const std::unordered_set<DetId>& validIds,
22  CLHEP::HepRandomEngine* engine) override;
23  ~HGCHEbackDigitizer() override;
24 
25 private:
26  //calice-like digitization parameters
27  uint32_t algo_;
28  bool scaleByDose_, thresholdFollowsMIP_;
29  float keV2MIP_, noise_MIP_;
30  float nPEperMIP_, nTotalPE_, xTalk_, sdPixels_;
31  std::string doseMapFile_, sipmMapFile_;
33 
34  void runEmptyDigitizer(std::unique_ptr<HGCalDigiCollection>& digiColl,
36  const CaloSubdetectorGeometry* theGeom,
37  const std::unordered_set<DetId>& validIds,
38  CLHEP::HepRandomEngine* engine);
39 
40  void runRealisticDigitizer(std::unique_ptr<HGCalDigiCollection>& digiColl,
42  const CaloSubdetectorGeometry* theGeom,
43  const std::unordered_set<DetId>& validIds,
44  CLHEP::HepRandomEngine* engine);
45 
46  void runCaliceLikeDigitizer(std::unique_ptr<HGCalDigiCollection>& digiColl,
48  const CaloSubdetectorGeometry* theGeom,
49  const std::unordered_set<DetId>& validIds,
50  CLHEP::HepRandomEngine* engine);
51 };
52 
55  algo_ = cfg.getParameter<uint32_t>("algo");
56  sipmMapFile_ = cfg.getParameter<edm::ParameterSet>("noise").getParameter<std::string>("sipmMap");
57  scaleByDose_ = cfg.getParameter<edm::ParameterSet>("noise").getParameter<bool>("scaleByDose");
58  unsigned int scaleByDoseAlgo = cfg.getParameter<edm::ParameterSet>("noise").getParameter<uint32_t>("scaleByDoseAlgo");
59  scaleByDoseFactor_ = cfg.getParameter<edm::ParameterSet>("noise").getParameter<double>("scaleByDoseFactor");
60  doseMapFile_ = cfg.getParameter<edm::ParameterSet>("noise").getParameter<std::string>("doseMap");
61  noise_MIP_ = cfg.getParameter<edm::ParameterSet>("noise").getParameter<double>("noise_MIP");
62  double refIdark = cfg.getParameter<edm::ParameterSet>("noise").getParameter<double>("referenceIdark");
63  xTalk_ = cfg.getParameter<edm::ParameterSet>("noise").getParameter<double>("referenceXtalk");
64  thresholdFollowsMIP_ = cfg.getParameter<bool>("thresholdFollowsMIP");
65  keV2MIP_ = cfg.getParameter<double>("keV2MIP");
66  this->keV2fC_ = 1.0; //keV2MIP_; // hack for HEB
67  this->det_ = DetId::HGCalHSc;
68  nPEperMIP_ = cfg.getParameter<double>("nPEperMIP");
69  nTotalPE_ = cfg.getParameter<double>("nTotalPE");
70  sdPixels_ = cfg.getParameter<double>("sdPixels");
71 
77  //the ADC will be updated on the fly depending on the gain
78  //but the TDC scale needs to be updated to use pe instead of MIP units
79  if (scaleByDose_)
80  this->myFEelectronics_->setTDCfsc(2 * scal_.getNPeInSiPM());
81 }
82 
83 //
84 void HGCHEbackDigitizer::runDigitizer(std::unique_ptr<HGCalDigiCollection>& digiColl,
85  HGCSimHitDataAccumulator& simData,
86  const CaloSubdetectorGeometry* theGeom,
87  const std::unordered_set<DetId>& validIds,
88  CLHEP::HepRandomEngine* engine) {
89  if (algo_ == 0)
90  runEmptyDigitizer(digiColl, simData, theGeom, validIds, engine);
91  else if (algo_ == 1)
92  runCaliceLikeDigitizer(digiColl, simData, theGeom, validIds, engine);
93  else if (algo_ == 2)
94  runRealisticDigitizer(digiColl, simData, theGeom, validIds, engine);
95 }
96 
97 void HGCHEbackDigitizer::runEmptyDigitizer(std::unique_ptr<HGCalDigiCollection>& digiColl,
98  HGCSimHitDataAccumulator& simData,
99  const CaloSubdetectorGeometry* theGeom,
100  const std::unordered_set<DetId>& validIds,
101  CLHEP::HepRandomEngine* engine) {
102  HGCSimHitData chargeColl, toa;
103  // this represents a cell with no signal charge
104  HGCCellInfo zeroData;
105  zeroData.hit_info[0].fill(0.f); //accumulated energy
106  zeroData.hit_info[1].fill(0.f); //time-of-flight
107 
108  for (const auto& id : validIds) {
109  chargeColl.fill(0.f);
110  toa.fill(0.f);
111  HGCSimHitDataAccumulator::iterator it = simData.find(id);
112  HGCCellInfo& cell = (simData.end() == it ? zeroData : it->second);
113  addCellMetadata(cell, theGeom, id);
114 
115  for (size_t i = 0; i < cell.hit_info[0].size(); ++i) {
116  //convert total energy keV->MIP, since converted to keV in accumulator
117  const float totalIniMIPs(cell.hit_info[0][i] * keV2MIP_);
118 
119  //store
120  chargeColl[i] = totalIniMIPs;
121  }
122 
123  //init a new data frame and run shaper
124  HGCalDataFrame newDataFrame(id);
125  this->myFEelectronics_->runShaper(newDataFrame, chargeColl, toa, engine);
126 
127  //prepare the output
128  this->updateOutput(digiColl, newDataFrame);
129  }
130 }
131 
132 void HGCHEbackDigitizer::runRealisticDigitizer(std::unique_ptr<HGCalDigiCollection>& digiColl,
133  HGCSimHitDataAccumulator& simData,
134  const CaloSubdetectorGeometry* theGeom,
135  const std::unordered_set<DetId>& validIds,
136  CLHEP::HepRandomEngine* engine) {
137  //switch to true if you want to print some details
138  constexpr bool debug(false);
139 
140  HGCSimHitData chargeColl, toa;
141  // this represents a cell with no signal charge
142  HGCCellInfo zeroData;
143  zeroData.hit_info[0].fill(0.f); //accumulated energy
144  zeroData.hit_info[1].fill(0.f); //time-of-flight
145 
146  // needed to compute the radiation and geometry scale factors
147  scal_.setGeometry(theGeom);
148 
149  //vanilla reference values are indepenent of the ids and were set by
150  //configuration in the python - no need to recomput them every time
151  //in the digitization loop
152  float scaledPePerMip = nPEperMIP_; //needed to scale according to tile geometry
153  float tunedNoise = nPEperMIP_ * noise_MIP_; //flat noise case
154  float vanillaADCThr = this->myFEelectronics_->getADCThreshold(); //vanilla thrs in MIPs
155  float adcLsb(this->myFEelectronics_->getADClsb());
156  float maxADC(-1); //vanilla will rely on what has been configured by default
157  uint32_t thrADC(thresholdFollowsMIP_ ? std::floor(vanillaADCThr / adcLsb * scaledPePerMip / nPEperMIP_)
158  : std::floor(vanillaADCThr / adcLsb));
159  float nTotalPixels(nTotalPE_);
160  float xTalk(xTalk_);
161  int gainIdx(0);
162 
163  for (const auto& id : validIds) {
164  chargeColl.fill(0.f);
165  toa.fill(0.f);
166  HGCSimHitDataAccumulator::iterator it = simData.find(id);
167  HGCCellInfo& cell = (simData.end() == it ? zeroData : it->second);
168  addCellMetadata(cell, theGeom, id);
169 
170  //in case realistic scenario (noise, fluence, dose, sipm/tile area) are to be used
171  //we update vanilla values with the realistic ones
172  if (id.det() == DetId::HGCalHSc && scaleByDose_) {
173  HGCScintillatorDetId scId(id.rawId());
174  double radius = scal_.computeRadius(scId);
175  auto opChar = scal_.scaleByDose(scId, radius);
176  scaledPePerMip = opChar.s;
177  tunedNoise = opChar.n;
178  gainIdx = opChar.gain;
179  thrADC = opChar.thrADC;
180  adcLsb = scal_.getLSBPerGain()[gainIdx];
181  maxADC = scal_.getMaxADCPerGain()[gainIdx] - 1e-6;
182  nTotalPixels = opChar.ntotalPE;
183  xTalk = opChar.xtalk;
184  }
185 
186  //set mean for poissonian noise
187  float meanN = std::pow(tunedNoise, 2);
188 
189  for (size_t i = 0; i < cell.hit_info[0].size(); ++i) {
190  //convert total energy keV->MIP, since converted to keV in accumulator
191  float totalIniMIPs(cell.hit_info[0][i] * keV2MIP_);
192 
193  //generate the number of photo-electrons from the energy deposit
194  const uint32_t npeS = std::floor(CLHEP::RandPoissonQ::shoot(engine, totalIniMIPs * scaledPePerMip) + 0.5);
195 
196  //generate the noise associated to the dark current
197  const uint32_t npeN = std::floor(CLHEP::RandPoissonQ::shoot(engine, meanN) + 0.5);
198 
199  //total number of pe from signal + noise (not subtracting pedestal)
200  const uint32_t npe = npeS + npeN;
201 
202  //take into account SiPM saturation
203  uint32_t nPixel(npe);
204  if (xTalk >= 0) {
205  const float x = vdt::fast_expf(-((float)npe) / nTotalPixels);
206  if (xTalk_ * x != 1)
207  nPixel = (uint32_t)std::max(nTotalPixels * (1.f - x) / (1.f - xTalk_ * x), 0.f);
208  }
209 
210  //take into account the gain fluctuations of each pixel
211  //FDG: just a note for now, par to be defined
212  //const float nPixelTot = nPixel + sqrt(nPixel) * CLHEP::RandGaussQ::shoot(engine, 0., 0.05);
213 
214  //realistic behavior: subtract the pedestal
215  //Note: for now the saturation effects are ignored...
216  if (scaleByDose_) {
217  float pedestal(meanN);
219  pedestal = 0.f;
220  chargeColl[i] = std::max(nPixel - pedestal, 0.f);
221  }
222  //vanilla simulation: scale back to MIP units... and to calibrated response depending on the thresholdFollowsMIP_ flag
223  else {
224  float totalMIPs = thresholdFollowsMIP_ ? std::max((nPixel - meanN), 0.f) / nPEperMIP_ : nPixel / nPEperMIP_;
225 
226  if (debug && totalIniMIPs > 0) {
227  LogDebug("HGCHEbackDigitizer") << "npeS: " << npeS << " npeN: " << npeN << " npe: " << npe
228  << " meanN: " << meanN << " noise_MIP_: " << noise_MIP_
229  << " nPEperMIP_: " << nPEperMIP_ << " scaledPePerMip: " << scaledPePerMip
230  << " nPixel: " << nPixel;
231  LogDebug("HGCHEbackDigitizer") << "totalIniMIPs: " << totalIniMIPs << " totalMIPs: " << totalMIPs
232  << std::endl;
233  }
234 
235  //store charge
236  chargeColl[i] = totalMIPs;
237  }
238 
239  //update time of arrival
240  toa[i] = cell.hit_info[1][i];
241  if (myFEelectronics_->toaMode() == HGCFEElectronics<HGCalDataFrame>::WEIGHTEDBYE && totalIniMIPs > 0)
242  toa[i] = cell.hit_info[1][i] / totalIniMIPs;
243  }
244 
245  //init a new data frame and run shaper
246  HGCalDataFrame newDataFrame(id);
247  this->myFEelectronics_->runShaper(newDataFrame, chargeColl, toa, engine, thrADC, adcLsb, gainIdx, maxADC);
248 
249  //prepare the output
250  this->updateOutput(digiColl, newDataFrame);
251  }
252 }
253 
254 //
255 void HGCHEbackDigitizer::runCaliceLikeDigitizer(std::unique_ptr<HGCalDigiCollection>& digiColl,
256  HGCSimHitDataAccumulator& simData,
257  const CaloSubdetectorGeometry* theGeom,
258  const std::unordered_set<DetId>& validIds,
259  CLHEP::HepRandomEngine* engine) {
260  //switch to true if you want to print some details
261  constexpr bool debug(false);
262 
263  HGCSimHitData chargeColl, toa;
264 
265  // this represents a cell with no signal charge
266  HGCCellInfo zeroData;
267  zeroData.hit_info[0].fill(0.f); //accumulated energy
268  zeroData.hit_info[1].fill(0.f); //time-of-flight
269 
270  for (const auto& id : validIds) {
271  chargeColl.fill(0.f);
272  HGCSimHitDataAccumulator::iterator it = simData.find(id);
273  HGCCellInfo& cell = (simData.end() == it ? zeroData : it->second);
274  addCellMetadata(cell, theGeom, id);
275 
276  for (size_t i = 0; i < cell.hit_info[0].size(); ++i) {
277  //convert total energy keV->MIP, since converted to keV in accumulator
278  const float totalIniMIPs(cell.hit_info[0][i] * keV2MIP_);
279 
280  //generate random number of photon electrons
281  const uint32_t npe = std::floor(CLHEP::RandPoissonQ::shoot(engine, totalIniMIPs * nPEperMIP_));
282 
283  //number of pixels
284  const float x = vdt::fast_expf(-((float)npe) / nTotalPE_);
285  uint32_t nPixel(0);
286  if (xTalk_ * x != 1)
287  nPixel = (uint32_t)std::max(nTotalPE_ * (1.f - x) / (1.f - xTalk_ * x), 0.f);
288 
289  //update signal
290  if (sdPixels_ != 0)
291  nPixel = (uint32_t)std::max(CLHEP::RandGaussQ::shoot(engine, (double)nPixel, sdPixels_), 0.);
292 
293  //convert to MIP again and saturate
294  float totalMIPs(0.f), xtalk = 0.f;
295  const float peDiff = nTotalPE_ - (float)nPixel;
296  if (peDiff != 0.f) {
297  xtalk = (nTotalPE_ - xTalk_ * ((float)nPixel)) / peDiff;
298  if (xtalk > 0.f && nPEperMIP_ != 0.f)
299  totalMIPs = (nTotalPE_ / nPEperMIP_) * vdt::fast_logf(xtalk);
300  }
301 
302  //add noise (in MIPs)
303  chargeColl[i] = totalMIPs;
304  if (noise_MIP_ != 0)
305  chargeColl[i] += std::max(CLHEP::RandGaussQ::shoot(engine, 0., noise_MIP_), 0.);
306  if (debug && cell.hit_info[0][i] > 0)
307  edm::LogVerbatim("HGCDigitizer") << "[runCaliceLikeDigitizer] xtalk=" << xtalk << " En=" << cell.hit_info[0][i]
308  << " keV -> " << totalIniMIPs << " raw-MIPs -> " << chargeColl[i]
309  << " digi-MIPs";
310  }
311 
312  //init a new data frame and run shaper
313  HGCalDataFrame newDataFrame(id);
314  this->myFEelectronics_->runShaper(newDataFrame, chargeColl, toa, engine);
315 
316  //prepare the output
317  this->updateOutput(digiColl, newDataFrame);
318  }
319 }
320 
321 //
323 
Log< level::Info, true > LogVerbatim
void runRealisticDigitizer(std::unique_ptr< HGCalDigiCollection > &digiColl, hgc::HGCSimHitDataAccumulator &simData, const CaloSubdetectorGeometry *theGeom, const std::unordered_set< DetId > &validIds, CLHEP::HepRandomEngine *engine)
T getParameter(std::string const &) const
Definition: ParameterSet.h:303
void runEmptyDigitizer(std::unique_ptr< HGCalDigiCollection > &digiColl, hgc::HGCSimHitDataAccumulator &simData, const CaloSubdetectorGeometry *theGeom, const std::unordered_set< DetId > &validIds, CLHEP::HepRandomEngine *engine)
void setSipmMap(const std::string &)
void setGeometry(const CaloSubdetectorGeometry *)
void updateOutput(std::unique_ptr< DColl > &coll, const DFr &rawDataFrame)
prepares the output according to the number of time samples to produce
std::unordered_map< uint32_t, HGCCellInfo > HGCSimHitDataAccumulator
constexpr int pow(int x)
Definition: conifer.h:24
std::unique_ptr< HGCFEElectronics< DFr > > myFEelectronics_
void setReferenceCrossTalk(double xtalk)
HGCalSciNoiseMap scal_
std::array< HGCSimData_t, nSamples > HGCSimHitData
HGCHEbackDigitizer(const edm::ParameterSet &ps)
DetId::Detector det() const
std::array< double, GAINRANGE_N > getLSBPerGain()
std::array< HGCSimHitData, 2 > hit_info
double f[11][100]
void addCellMetadata(HGCCellInfo &info, const HGCalGeometry *geom, const DetId &detid)
void setFluenceScaleFactor(double val)
void setReferenceDarkCurrent(double idark)
#define debug
Definition: HDRShower.cc:19
derives from HGCalRadiation map to parse fluence/dose parameters, provides Sci-specific functions the...
void setDoseMap(const std::string &, const unsigned int)
Readout digi for HGC.
Definition: HGCDataFrame.h:14
void runDigitizer(std::unique_ptr< HGCalDigiCollection > &digiColl, hgc::HGCSimHitDataAccumulator &simData, const CaloSubdetectorGeometry *theGeom, const std::unordered_set< DetId > &validIds, CLHEP::HepRandomEngine *engine) override
double computeRadius(const HGCScintillatorDetId &)
bool ignoreAutoPedestalSubtraction()
void runCaliceLikeDigitizer(std::unique_ptr< HGCalDigiCollection > &digiColl, hgc::HGCSimHitDataAccumulator &simData, const CaloSubdetectorGeometry *theGeom, const std::unordered_set< DetId > &validIds, CLHEP::HepRandomEngine *engine)
SiPMonTileCharacteristics scaleByDose(const HGCScintillatorDetId &, const double, const int aimMIPtoADC=15, const GainRange_t gainPreChoice=GainRange_t::AUTO)
float fast_expf(float x)
#define DEFINE_EDM_PLUGIN(factory, type, name)
std::array< double, GAINRANGE_N > getMaxADCPerGain()
models the behavior of the front-end electronics
DetId::Detector det_
float fast_logf(float x)
#define LogDebug(id)