Belle II Software  release-08-01-10
PXDDigitizerModule.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 #include <pxd/modules/pxdSimulation/PXDDigitizerModule.h>
10 #include <vxd/geometry/GeoCache.h>
11 #include <vxd/geometry/GeoTools.h>
12 #include <pxd/reconstruction/PXDGainCalibrator.h>
13 #include <pxd/reconstruction/PXDPixelMasker.h>
14 
15 #include <framework/logging/Logger.h>
16 #include <framework/gearbox/Unit.h>
17 #include <framework/gearbox/Const.h>
18 #include <framework/datastore/DataStore.h>
19 #include <framework/datastore/StoreArray.h>
20 #include <framework/datastore/RelationArray.h>
21 #include <framework/datastore/RelationIndex.h>
22 
23 #include <mdst/dataobjects/MCParticle.h>
24 #include <pxd/dataobjects/PXDTrueHit.h>
25 #include <pxd/dataobjects/PXDDigit.h>
26 #include <cmath>
27 
28 #include <TRandom.h>
29 
30 using namespace std;
31 using namespace Belle2;
32 using namespace Belle2::PXD;
33 
34 //-----------------------------------------------------------------
35 // Register the Module
36 //-----------------------------------------------------------------
37 REG_MODULE(PXDDigitizer);
38 
39 //-----------------------------------------------------------------
40 // Implementation
41 //-----------------------------------------------------------------
42 
43 PXDDigitizerModule::PXDDigitizerModule() : Module()
44  , m_noiseFraction(0), m_eToADU(0), m_chargeThreshold(0), m_chargeThresholdElectrons(0)
45  , m_currentHit(nullptr), m_currentParticle(0), m_currentTrueHit(0), m_currentSensor(nullptr)
46  , m_currentSensorInfo(nullptr), m_nGates(0), m_pxdIntegrationTime(0), m_timePerGate(0)
47  , m_triggerGate(0), m_gated(false)
48 {
49  //Set module properties
50  setDescription("Digitize PXDSimHits");
52  addParam("ElectronicEffects", m_applyNoise, "Apply electronic effects?",
53  true);
54  addParam("ElectronicNoise", m_elNoise,
55  "Noise added by the electronics, set in ENC", 150.0);
56 
57  addParam("MCParticles", m_storeMCParticlesName,
58  "MCParticle collection name", string(""));
59  addParam("Digits", m_storeDigitsName, "Digits collection name", string(""));
60  addParam("SimHits", m_storeSimHitsName, "SimHit collection name",
61  string(""));
62  addParam("TrueHits", m_storeTrueHitsName, "TrueHit collection name",
63  string(""));
64 
65  addParam("PoissonSmearing", m_applyPoisson,
66  "Apply Poisson smearing of electrons collected on pixels?", true);
67  addParam("IntegrationWindow", m_applyWindow, "Use integration window?",
68  true);
69 
70  addParam("SegmentLength", m_segmentLength, "Maximum segment length (in mm)",
71  0.005);
72  addParam("ElectronGroupSize", m_elGroupSize,
73  "Split Signalpoints in smaller groups of N electrons (in e)", 100);
74  addParam("ElectronStepTime", m_elStepTime,
75  "Time step for tracking electron groups in readout plane (in ns)",
76  1.0);
77  addParam("ElectronMaxSteps", m_elMaxSteps,
78  "Maximum number of steps when propagating electrons", 200);
79  addParam("Gq", m_gq, "Gq of a pixel in nA/electron", 0.6);
80  addParam("ADCUnit", m_ADCUnit, "Slope of the linear ADC transfer curve in nA/ADU", 130.0);
81  addParam("PedestalMean", m_pedestalMean, "Mean of pedestals in ADU", 100.0);
82  addParam("PedestalRMS", m_pedestalRMS, "RMS of pedestals in ADU", 30.0);
83  addParam("GatingTime", m_gatingTime,
84  "Time window during which the PXD is not collecting charge in nano seconds", 400.0);
85  addParam("GatingWithoutReadout", m_gatingWithoutReadout,
86  "Digits from gated rows not sent to DHH ", true);
87  addParam("GatingWithoutReadoutTime", m_gatingWithoutReadoutTime,
88  "Time window during which digits from gated rows are not sent to DHH in nano seconds", 1400.0);
89  addParam("HardwareDelay", m_hwdelay,
90  "Constant time delay between bunch crossing and switching on triggergate in nano seconds", 0.0);
91 
92 }
93 
95 {
96  //Register output collections
98  storeDigits.registerInDataStore();
100  storeDigits.registerRelationTo(storeParticles);
102  storeDigits.registerRelationTo(storeTrueHits);
103 
104  m_storePXDTiming.isOptional();
105 
106  //Set default names for the relations
108  DataStore::arrayName<MCParticle>(m_storeMCParticlesName),
109  DataStore::arrayName<PXDSimHit>(m_storeSimHitsName));
111  DataStore::arrayName<PXDTrueHit>(m_storeTrueHitsName),
112  DataStore::arrayName<PXDSimHit>(m_storeSimHitsName));
114  DataStore::arrayName<PXDDigit>(m_storeDigitsName),
115  DataStore::arrayName<MCParticle>(m_storeMCParticlesName));
117  DataStore::arrayName<PXDDigit>(m_storeDigitsName),
118  DataStore::arrayName<PXDTrueHit>(m_storeTrueHitsName));
119 
120  //Convert parameters to correct units
121  m_elNoise *= Unit::e;
122  m_eToADU = m_ADCUnit / m_gq;
125 
126  // Get number of PXD gates
127  auto gTools = VXD::GeoCache::getInstance().getGeoTools();
128  m_nGates = gTools->getNumberOfPXDReadoutGates();
129 
130  // Active integration time (per gate) in ns
131  m_pxdIntegrationTime = 20000.0;
132  // Readout time per gate (sample - clear - cycle of rolling shutter) in ns
134 
135  B2DEBUG(20, "PXDDigitizer Parameters (in system units, *=calculated +=set in xml):");
136  B2DEBUG(20, " --> ElectronicEffects: " << (m_applyNoise ? "true" : "false"));
137  B2DEBUG(20, " --> ElectronicNoise: " << m_elNoise << " e-");
138  B2DEBUG(20, " --> +ChargeThreshold: " << "set in xml by sensor, nominal 4 ADU");
139  B2DEBUG(20, " --> *NoiseFraction: " << "set in xml by sensor, nominal 1.0e-5");
140  B2DEBUG(20,
141  " --> MCParticles: " << DataStore::arrayName<MCParticle>(m_storeMCParticlesName));
142  B2DEBUG(20,
143  " --> SimHits: " << DataStore::arrayName<PXDSimHit>(m_storeSimHitsName));
144  B2DEBUG(20,
145  " --> Digits: " << DataStore::arrayName<PXDDigit>(m_storeDigitsName));
146  B2DEBUG(20,
147  " --> TrueHits: " << DataStore::arrayName<PXDTrueHit>(m_storeTrueHitsName));
148  B2DEBUG(20, " --> MCSimHitRel: " << m_relMCParticleSimHitName);
149  B2DEBUG(20, " --> DigitMCRel: " << m_relDigitMCParticleName);
150  B2DEBUG(20, " --> TrueSimRel: " << m_relTrueHitSimHitName);
151  B2DEBUG(20, " --> DigitTrueRel: " << m_relDigitTrueHitName);
152  B2DEBUG(20, " --> PoissonSmearing: " << (m_applyPoisson ? "true" : "false"));
153  B2DEBUG(20, " --> +IntegrationWindow: " << (m_applyWindow ? "true" : "false") << ", size defined in xml");
154  B2DEBUG(20, " --> SegmentLength: " << m_segmentLength << " cm");
155  B2DEBUG(20, " --> ElectronGroupSize: " << m_elGroupSize << " e-");
156  B2DEBUG(20, " --> ElectronStepTime: " << m_elStepTime << " ns");
157  B2DEBUG(20, " --> ElectronMaxSteps: " << m_elMaxSteps);
158  B2DEBUG(20, " --> ADU unit: " << m_eToADU << " e-/ADU");
159 }
160 
162 {
163  //Fill map with all possible sensors This is too slow to be done every event so
164  //we fill it once and only clear the content of the sensors per event, not
165  //the whole map.
166  // NOTE: Some VXDIDs will be added to the map on the way if we have multiple
167  // frames, but they will stay and will be cleared appropriately, so this is not
168  // a problem, and after a few events the performance will stabilize.
169  m_sensors.clear();
171  for (VxdID layer : geo.getLayers(SensorInfo::PXD)) {
172  for (VxdID ladder : geo.getLadders(layer)) {
173  for (VxdID sensor : geo.getSensors(ladder)) {
174  m_sensors[sensor] = Sensor();
175  }
176  }
177  }
178 }
179 
181 {
182 
183  //Check for injection vetos
184  if (m_storePXDTiming.isValid()) {
185  m_gated = (*m_storePXDTiming).isGated();
186  m_triggerGate = (*m_storePXDTiming).getTriggerGate();
187  m_gatingStartTimes = (*m_storePXDTiming).getGatingStartTimes();
188  m_gatedChannelIntervals.clear();
189 
190  B2DEBUG(20, "Reading from PXDInjectionBGTiming: Event has TriggerGate " << m_triggerGate << " and is gated " << m_gated);
191 
193  for (auto gatingStartTime : m_gatingStartTimes) {
194  B2DEBUG(20, "Gating start time " << gatingStartTime << "ns ");
195 
196  // Gated readout can only come from gating in the time period from t=0us to t=20us.
197  if (gatingStartTime >= 0) {
198  // Compute the gate where gating started. Note the wrap around of the rolling shutter
199  int firstGated = (m_triggerGate + int(gatingStartTime / m_timePerGate)) % m_nGates ;
200  // Compute the gate where gating stopped. Note the wrap around of the rolling shutter
201  int lastGated = (m_triggerGate + int((gatingStartTime + m_gatingWithoutReadoutTime) / m_timePerGate)) % m_nGates ;
202 
203  if (lastGated >= firstGated) {
204  // Gated channels in the same frame
205  B2DEBUG(20, "Gated channel interval from " << firstGated << " to " << lastGated);
206  m_gatedChannelIntervals.push_back(pair<int, int>(firstGated, lastGated));
207  } else {
208  // Gated channels in two consecutive frames
209  B2DEBUG(20, "Gated channel interval from " << firstGated << " to " << m_nGates);
210  B2DEBUG(20, "Gated channel interval from " << 0 << " to " << lastGated);
211  m_gatedChannelIntervals.push_back(pair<int, int>(firstGated, m_nGates));
212  m_gatedChannelIntervals.push_back(pair<int, int>(0, lastGated));
213  }
214  }
215  }
216  }
217  } else {
218  m_gated = false;
219  m_triggerGate = gRandom->Integer(m_nGates);
220  m_gatingStartTimes.clear();
221  m_gatedChannelIntervals.clear();
222  }
223 
224  //Clear sensors and process SimHits
225  for (Sensors::value_type& sensor : m_sensors) {
226  sensor.second.clear();
227  }
228  m_currentSensor = 0;
230  //Clear return value
231  setReturnValue(0);
232 
236 
237  RelationArray mcParticlesToSimHits(storeMCParticles, storeSimHits,
239  RelationArray mcParticlesToTrueHits(storeMCParticles, storeTrueHits); // not used here
240  RelationArray trueHitsToSimHits(storeTrueHits, storeSimHits,
242 
244  storeDigits.clear();
245 
246  RelationArray relDigitMCParticle(storeDigits, storeMCParticles,
248  relDigitMCParticle.clear();
249 
250  RelationArray relDigitTrueHit(storeDigits, storeTrueHits,
252  relDigitTrueHit.clear();
253 
254  unsigned int nSimHits = storeSimHits.getEntries();
255  if (nSimHits == 0)
256  return;
257 
258  RelationIndex<MCParticle, PXDSimHit> relMCParticleSimHit(storeMCParticles,
259  storeSimHits, m_relMCParticleSimHitName);
260  RelationIndex<PXDTrueHit, PXDSimHit> relTrueHitSimHit(storeTrueHits,
261  storeSimHits, m_relTrueHitSimHitName);
262 
263  //Check sensor info and set pointers to current sensor
264  for (unsigned int i = 0; i < nSimHits; ++i) {
265  m_currentHit = storeSimHits[i];
266  if (!m_currentHit->getElectrons()) continue;
268  relMCParticleSimHit.getFirstElementTo(m_currentHit);
269  if (mcRel) {
270  m_currentParticle = mcRel->indexFrom;
271  if (mcRel->weight < 0) {
272  //This simhit is from a particle which was not saved by the simulation
273  //so we do not take it into account for relations. Otherwise we might
274  //end up adding positive and negative weights together
275  m_currentParticle = -1;
276  }
277  } else {
278  // Don't warn if this is a background SimHit
280  B2WARNING(
281  "Could not find MCParticle which produced PXDSimhit " << i);
282  m_currentParticle = -1;
283  }
285  relTrueHitSimHit.getFirstElementTo(m_currentHit);
286  //We only care about true hits from particles which have not been ignored
287  if (trueRel && trueRel->weight > 0) {
288  m_currentTrueHit = trueRel->indexFrom;
289  } else {
290  m_currentTrueHit = -1;
291  }
292 
293  VxdID sensorID = m_currentHit->getSensorID();
294  if (!m_currentSensorInfo || sensorID != m_currentSensorInfo->getID()) {
295  m_currentSensorInfo = dynamic_cast<const SensorInfo*>(&VXD::GeoCache::get(sensorID));
296  if (!m_currentSensorInfo)
297  B2FATAL(
298  "SensorInformation for Sensor " << sensorID << " not found, make sure that the geometry is set up correctly");
299  m_currentSensor = &m_sensors[sensorID];
300  B2DEBUG(20, "Sensor Parameters for Sensor " << sensorID << ": " << endl
301  << " --> Width: " << m_currentSensorInfo->getWidth() << endl
302  << " --> Length: " << m_currentSensorInfo->getLength() << endl
303  << " --> uPitch: " << m_currentSensorInfo->getUPitch() << endl
304  << " --> vPitch " << m_currentSensorInfo->getVPitch(-m_currentSensorInfo->getLength() / 2.0)
305  << ", " << m_currentSensorInfo->getVPitch(m_currentSensorInfo->getLength() / 2.0) << endl
306  << " --> Thickness: " << m_currentSensorInfo->getThickness() << endl
307  << " --> BulkDoping: " << m_currentSensorInfo->getBulkDoping() << endl
308  << " --> BackVoltage: " << m_currentSensorInfo->getBackVoltage() << endl
309  << " --> TopVoltage: " << m_currentSensorInfo->getTopVoltage() << endl
310  << " --> SourceBorder: " << m_currentSensorInfo->getSourceBorder(-0.4 * m_currentSensorInfo->getLength())
312  << " --> ClearBorder: " << m_currentSensorInfo->getClearBorder(-0.4 * m_currentSensorInfo->getLength())
313  << ", " << m_currentSensorInfo->getClearBorder(+0.4 * m_currentSensorInfo->getLength()) << endl
314  << " --> DrainBorder: " << m_currentSensorInfo->getDrainBorder(-0.4 * m_currentSensorInfo->getLength())
315  << ", " << m_currentSensorInfo->getDrainBorder(+0.4 * m_currentSensorInfo->getLength()) << endl
316  << " --> GateDepth: " << m_currentSensorInfo->getGateDepth() << endl
317  << " --> DoublePixel: " << m_currentSensorInfo->getDoublePixel() << endl);
318 
319  }
320  B2DEBUG(10,
321  "Processing hit " << i << " in Sensor " << sensorID << ", related to MCParticle " << m_currentParticle);
322  processHit();
323  }
324 
325  addNoiseDigits();
326  saveDigits();
327  //Return number of created digits
328  setReturnValue(storeDigits.getEntries());
329 }
330 
332 {
333  if (m_applyWindow) {
334  //Ignore a hit which is outside of the active frame time of the hit gate
335  float currentHitTime = m_currentHit->getGlobalTime();
336 
337  // First we have to now the current hit gate
338  const ROOT::Math::XYZVector startPoint = m_currentHit->getPosIn();
339  const ROOT::Math::XYZVector stopPoint = m_currentHit->getPosOut();
340  auto row = m_currentSensorInfo->getVCellID(0.5 * (stopPoint.Y() + startPoint.Y()));
341 
343  row = 767 - row;
344  int gate = row / 4;
345 
346  // Compute the distance in unit of gates to the trigger gate. Do it in
347  // direction of PXD rolling shutter.
348  int distanceToTriggerGate = 0;
349  if (gate >= m_triggerGate) distanceToTriggerGate = gate - m_triggerGate;
350  else distanceToTriggerGate = 192 - m_triggerGate + gate;
351 
352  // The triggergate is switched ON at t0=0ns. Compute the integration time window for the current hit gate.
353  // Time intervals of subsequent readout gates are shifted by timePerGate.
354  float gateMinTime = -m_pxdIntegrationTime + m_timePerGate + m_hwdelay + distanceToTriggerGate * m_timePerGate ;
355  float gateMaxTime = gateMinTime + m_pxdIntegrationTime;
356 
357  B2DEBUG(30,
358  "Checking if hit is in timeframe " << gateMinTime << " <= " << currentHitTime <<
359  " <= " << gateMaxTime);
360 
361  if (currentHitTime
362  < gateMinTime
363  || currentHitTime
364  > gateMaxTime)
365  return;
366 
367  if (m_gated) {
368  //Ignore simHits when the PXD is gated, i.e. PXD does not collect any charge.
369  for (auto gatingStartTime : m_gatingStartTimes) {
370  B2DEBUG(30,
371  "Checking if hit is in gated timeframe " << gatingStartTime << " <= " << currentHitTime <<
372  " <= " << gatingStartTime + m_gatingTime);
373 
374  if (currentHitTime
375  > gatingStartTime
376  && currentHitTime
377  < gatingStartTime + m_gatingTime)
378  return;
379  }
380  }
381  }
382 
383  //Get step length and direction
384  const ROOT::Math::XYZVector startPoint = m_currentHit->getPosIn();
385  ROOT::Math::XYZVector stopPoint = m_currentHit->getPosOut();
386  double dx = stopPoint.X() - startPoint.X();
387  double dy = stopPoint.Y() - startPoint.Y();
388  double dz = stopPoint.Z() - startPoint.Z();
389  double trackLength2 = dx * dx + dy * dy + dz * dz;
390 
391  // Set magnetic field to save calls to getBField()
392  m_currentBField = m_currentSensorInfo->getBField(0.5 * (startPoint + stopPoint));
393 
394  if (m_currentHit->getPDGcode() == Const::photon.getPDGCode() || trackLength2 <= 0.01 * Unit::um * Unit::um) {
395  //Photons deposit the energy at the end of their step
396  driftCharge(stopPoint, m_currentHit->getElectrons());
397  } else {
398  //Otherwise, split into segments of (default) max. 5µm and
399  //drift the charges from the center of each segment
401  double lastFraction {0};
402  double lastElectrons {0};
403 
404  for (auto& segment : segments) {
405  //Simhit returns step fraction and cumulative electrons. We want the
406  //center of these steps and electrons in this step
407  const double f = (segment.first + lastFraction) * 0.5;
408  const double e = segment.second - lastElectrons;
409  //Update last values
410  std::tie(lastFraction, lastElectrons) = segment;
411 
412  //And drift charge from that position
413  stopPoint.SetXYZ(startPoint.X() + f * dx, startPoint.Y() + f * dy, startPoint.Z() + f * dz);
414  driftCharge(stopPoint, e);
415  }
416  }
417 }
418 
419 inline double pol3(double x, const double* c)
420 {
421  return c[0] + x * (c[1] + x * (c[2] + x * c[3]));
422 };
423 
424 void PXDDigitizerModule::driftCharge(const ROOT::Math::XYZVector& r, double electrons)
425 {
426  //Get references to current sensor/info for ease of use
427  const SensorInfo& info = *m_currentSensorInfo;
428  Sensor& sensor = *m_currentSensor;
429 
430  static const double cx[] = {0, 2.611995e-01, -1.948316e+01, 9.167064e+02};
431  static const double cu[] = {4.223817e-04, -1.041434e-03, 5.139596e-02, -1.180229e+00};
432  static const double cv[] = {4.085681e-04, -8.615459e-04, 5.706439e-02, -1.774319e+00};
433  static const double ct[] = {2.823743e-04, -1.138627e-06, 4.310888e-09, -1.559542e-11}; // temp range [250, 350] degrees with m_elStepTime = 1 ns
434 
435  double dz = 0.5 * info.getThickness() - info.getGateDepth() - r.Z(), adz = fabs(dz);
436 
437  double dx = fabs(m_currentBField.Y()) > Unit::T ? copysign(pol3(adz, cx),
438  dz) : 0; // two configurations: with default B field (B~1.5T) and B=0T
439  double sigmaDrift_u = pol3(adz, cu);
440  double sigmaDrift_v = pol3(adz, cv);
441  double sigmaDiffus = pol3(info.getTemperature() - 300.0,
442  ct); // sqrt(2 * Const::uTherm * info.getElectronMobility(0) * m_elStepTime);
443 
444  int nGroups = (int)(electrons / m_elGroupSize) + 1;
445  double groupCharge = electrons / nGroups;
446  while (nGroups--) {
447  double du = gRandom->Gaus(0.0, sigmaDrift_u), dv = gRandom->Gaus(0.0, sigmaDrift_v);
448  double uPos = r.X() + du + dx;
449  double vPos = r.Y() + dv;
450  int step = 0;
451  for (; step < m_elMaxSteps; ++step) {
452  int id = info.getTrappedID(uPos, vPos);
453  //Check if cloud inside of IG region
454  if (id >= 0) {
455  // Trapped in the internal gate region
456  sensor[Digit(id % 250, id / 250)].add(groupCharge, m_currentParticle, m_currentTrueHit);
457  break;
458  }
459  //Random walk with drift
460  dv = gRandom->Gaus(0.0, sigmaDiffus), du = gRandom->Gaus(0.0, sigmaDiffus);
461  uPos += du;
462  vPos += dv;
463  }
464  if (step == m_elMaxSteps) {
465  // cloud is not trapped but save it anyway into the closest pixel
466  int iu = info.getUCellID(uPos, vPos, 1), iv = info.getVCellID(vPos, 1);
467  sensor[Digit(iu, iv)].add(groupCharge, m_currentParticle, m_currentTrueHit);
468  }
469  }
470 }
471 
472 
473 double PXDDigitizerModule::addNoise(double charge)
474 {
475  if (charge <= 0) {
476  //Noise Pixel, add chargeThreshold electrons;
477  charge = 1.1 * m_chargeThresholdElectrons;
478  } else {
479  if (m_applyPoisson) {
480  // For big charge assume Gaussian distr.
481  if (charge > (1000. * Unit::e))
482  charge = gRandom->Gaus(charge, sqrt(charge));
483  else
484  // Otherwise Poisson distr.
485  charge = gRandom->Poisson(charge);
486  }
487  if (m_applyNoise) {
488  charge += gRandom->Gaus(0., m_elNoise);
489  }
490  }
491  return charge;
492 }
493 
495 {
496  if (!m_applyNoise)
497  return;
498 
499  for (Sensors::value_type& sensor : m_sensors) {
500  Sensor& s = sensor.second;
501 
502  //Calculate the number of pixels on an empty sensor which will exceed the noise cut
503  const SensorInfo& info =
504  dynamic_cast<const SensorInfo&>(VXD::GeoCache::get(sensor.first));
505  int nU = info.getUCells();
506  int nV = info.getVCells();
507  int nPixels = gRandom->Poisson(info.getNoiseFraction() * nU * nV);
508 
509  // Existing digits will get noise in PXDDIgitizer::AddNoise.
510  // Here we add zero charge to nPixels digits.
511  // In part, this will create some new zero-charge digits.
512  // If we happen to select an existing (fired) digit, it remains unchanged.
513  for (int i = 0; i < nPixels; ++i) {
514  Digit d(gRandom->Integer(nU), gRandom->Integer(nV));
515  s[d].add(0.0);
516  }
517  }
518 }
519 
521 {
522  return std::any_of(m_gatedChannelIntervals.begin(), m_gatedChannelIntervals.end(),
523  [gate](auto interval) {return gate >= interval.first && gate < interval.second;});
524 }
525 
527 {
531  RelationArray relDigitMCParticle(storeDigits, storeMCParticles,
533  RelationArray relDigitTrueHit(storeDigits, storeTrueHits,
535 
536 
537  for (Sensors::value_type& sensor : m_sensors) {
538  auto sensorID = sensor.first;
539  const SensorInfo& info =
540  dynamic_cast<const SensorInfo&>(VXD::GeoCache::get(sensorID));
541  m_chargeThreshold = info.getChargeThreshold();
543  for (Sensor::value_type& digitAndValue : sensor.second) {
544  const Digit& d = digitAndValue.first;
545  const DigitValue& v = digitAndValue.second;
546 
547  //Add Noise where applicable
548  double charge = addNoise(v.charge());
549 
550  // Draw a pedestal value
551  double pedestal = std::max(gRandom->Gaus(m_pedestalMean, m_pedestalRMS), 0.0);
552 
553  // Get gain correction
554  double gain = PXDGainCalibrator::getInstance().getGainCorrection(sensorID, d.u(), d.v());
555 
556  // Add pedestal to charge
557  charge = round(charge * gain / m_eToADU + pedestal);
558 
559  // Clipping of ADC codes at 255
560  charge = std::min(charge, 255.0);
561 
562  // Subtraction of integer pedestal in DHP
563  charge = charge - round(pedestal);
564 
565  // Zero Suppression in DHP
566  if (charge <= m_chargeThreshold)
567  continue;
568 
569  // Check if the readout digits is coming from a gated row
571  int row = d.v();
572  if (sensorID.getLayerNumber() == 1)
573  row = 767 - row;
574  int gate = row / 4;
575  if (checkIfGated(gate)) continue;
576  }
577 
578  // Check if the readout digit is coming from a masked or dead area
579  if (PXD::PXDPixelMasker::getInstance().pixelDead(sensorID, d.u(), d.v())
580  || !PXD::PXDPixelMasker::getInstance().pixelOK(sensorID, d.u(), d.v())) {
581  continue;
582  }
583 
584  //Add the digit to datastore
585  int digIndex = storeDigits.getEntries();
586  storeDigits.appendNew(
587  PXDDigit(sensorID, d.u(), d.v(), charge));
588 
589  //If the digit has any relations to MCParticles, add the Relation
590  if (v.particles().size() > 0) {
591  relDigitMCParticle.add(digIndex, v.particles().begin(),
592  v.particles().end());
593  }
594  //If the digit has any truehits to TrueHit, add the Relation
595  if (v.truehits().size() > 0) {
596  relDigitTrueHit.add(digIndex, v.truehits().begin(),
597  v.truehits().end());
598  }
599  }
600  }
601 }
602 
603 
int getPDGCode() const
PDG code.
Definition: Const.h:464
static const ParticleType photon
photon particle
Definition: Const.h:664
static std::string relationName(const std::string &fromName, const std::string &toName, std::string const &namedRelation="")
Return storage name for a relation between two arrays of the given names.
Definition: DataStore.h:180
Base class for Modules.
Definition: Module.h:72
void setDescription(const std::string &description)
Sets the description of the module.
Definition: Module.cc:214
void setPropertyFlags(unsigned int propertyFlags)
Sets the flags for the module properties.
Definition: Module.cc:208
void setReturnValue(int value)
Sets the return value for this module as integer.
Definition: Module.cc:220
@ c_ParallelProcessingCertified
This module can be run in parallel processing mode safely (All I/O must be done through the data stor...
Definition: Module.h:80
The PXD digit class.
Definition: PXDDigit.h:27
Class representing the charge and particle contributions for one pixel.
const relations_map & truehits() const
Return the map containing all truehit contributions to the pixel charge.
double charge() const
Return the charge collected in the pixel.
const relations_map & particles() const
Return the map containing all particle contributions to the pixel charge.
Class to represent the coordinates of one pixel.
double m_pedestalRMS
RMS pedestal in ADU.
void processHit()
Process one PXDSimHit by dividing the step in smaller steps and drifting the charge.
double m_pxdIntegrationTime
Integration time for each gate of the PXD in ns.
double m_pedestalMean
Mean pedestal in ADU.
bool m_gatingWithoutReadout
Digits from gated rows not sent to DHH.
void initialize() override final
Initialize the module and check the parameters.
std::string m_relTrueHitSimHitName
Name of the relation between PXDTrueHits and PXDSimHits.
void saveDigits()
Save all digits to the datastore.
int m_currentParticle
Index of the particle which caused the current hit.
double m_ADCUnit
Slope of the linear ADC transfer curve in nA/ADU.
Sensors m_sensors
Structure containing all existing sensors.
double m_elStepTime
Timeframe for one random walk step.
std::string m_relDigitMCParticleName
Name of the relation between PXDDigits and MCParticles.
int m_elMaxSteps
Maximum number of random walks before abort.
bool m_applyWindow
Wether or not to apply a time window cut.
std::string m_storeTrueHitsName
Name of the collection for the PXDTrueHits.
double m_timePerGate
Time needed to sample and clear a readout gate
double m_gatingWithoutReadoutTime
Time window during which digits from gated rows are not sent to DHH.
void driftCharge(const ROOT::Math::XYZVector &position, double electrons)
Drift the charge inside the silicon.
const PXDSimHit * m_currentHit
Pointer to the PXDSimhit currently digitized.
std::vector< float > m_gatingStartTimes
Vector of start times for gating.
std::string m_storeMCParticlesName
Name of the collection for the MCParticles.
bool m_applyNoise
Wether or not to apply noise.
void addNoiseDigits()
Add pure noise digits to the Sensors.
void event() override final
Digitize one event.
int m_nGates
Number of readout gates (or total number of Switcher channels)
double m_chargeThreshold
Zero-suppression threshold in ADU.
bool m_applyPoisson
Wether or not to apply poission fluctuation of charge.
Sensor * m_currentSensor
Pointer to the sensor in which the current hit occured.
std::vector< std::pair< int, int > > m_gatedChannelIntervals
Vector of gated readout channels
ROOT::Math::XYZVector m_currentBField
Current magnetic field.
double m_elNoise
Amount of noise to apply.
std::string m_storeDigitsName
Name of the collection for the PXDDigits.
const SensorInfo * m_currentSensorInfo
Pointer to the SensorInfo of the current sensor.
std::string m_relDigitTrueHitName
Name of the relation between PXDDigits and PXDTrueHits.
double m_gatingTime
Time window during which the PXD is not collecting charge.
void beginRun() override final
Initialize the list of existing PXD Sensors.
double m_eToADU
ENC equivalent of 1 ADU.
bool checkIfGated(int gate)
Check if gate was read while in gated mode.
double m_gq
g_q of a pixel in nA/electrons.
StoreObjPtr< PXDInjectionBGTiming > m_storePXDTiming
Input array for timings.
double m_hwdelay
Hardware delay between time of bunch crossing and switching on triggergate in ns.
int m_elGroupSize
Max number of electrons per random walk.
int m_currentTrueHit
Index of the TrueHit the current hit belongs to.
std::string m_relMCParticleSimHitName
Name of the relation between MCParticles and PXDSimHits.
double addNoise(double charge)
Calculate the noise contribution to one pixel with given charge.
std::string m_storeSimHitsName
Name of the collection for the PXDSimhits.
float getGainCorrection(VxdID id, unsigned int uid, unsigned int vid) const
Get gain correction.
static PXDGainCalibrator & getInstance()
Main (and only) way to access the PXDGainCalibrator.
bool pixelOK(VxdID id, unsigned int uid, unsigned int vid) const
Check whether a pixel on a given sensor is OK or not.
static PXDPixelMasker & getInstance()
Main (and only) way to access the PXDPixelMasker.
Specific implementation of SensorInfo for PXD Sensors which provides additional pixel specific inform...
Definition: SensorInfo.h:23
double getTopVoltage() const
Return the voltage at the top of the sensor.
Definition: SensorInfo.h:101
double getGateDepth() const
Return the gate depth for the sensor.
Definition: SensorInfo.h:123
double getSourceBorder(double v) const
Return the distance between the source side of the pixel and the start of the Gate for a pixel at v.
Definition: SensorInfo.h:105
double getDrainBorder(double v) const
Return the distance between the drain side of the pixel and the start of the Gate for a pixel at v.
Definition: SensorInfo.h:117
double getBulkDoping() const
Return the bulk doping of the Silicon sensor.
Definition: SensorInfo.h:97
bool getDoublePixel() const
Return true if the Sensor is a double pixel structure: every other pixel is mirrored along v.
Definition: SensorInfo.h:125
double getClearBorder(double v) const
Return the distance between the clear side of the pixel and the start of the Gate for a pixel at v.
Definition: SensorInfo.h:111
const ROOT::Math::XYZVector getBField(const ROOT::Math::XYZVector &point) const
Get B field value from the field map.
Definition: SensorInfo.cc:45
double getBackVoltage() const
Return the voltage at the backside of the sensor.
Definition: SensorInfo.h:99
Low-level class to create/modify relations between StoreArrays.
Definition: RelationArray.h:62
void add(index_type from, index_type to, weight_type weight=1.0)
Add a new element to the relation.
void clear() override
Clear all elements from the relation.
Provides access to fast ( O(log n) ) bi-directional lookups on a specified relation.
Definition: RelationIndex.h:76
const Element * getFirstElementTo(const TO &to) const
Return a pointer to the first relation Element of the given object.
virtual unsigned short getBackgroundTag() const
Get background tag.
Definition: SimHitBase.h:46
bool registerInDataStore(DataStore::EStoreFlags storeFlags=DataStore::c_WriteOut)
Register the object/array in the DataStore.
Accessor to arrays stored in the data store.
Definition: StoreArray.h:113
T * appendNew()
Construct a new T object at the end of the array.
Definition: StoreArray.h:246
int getEntries() const
Get the number of objects in the array.
Definition: StoreArray.h:216
void clear() override
Delete all entries in this array.
Definition: StoreArray.h:207
bool registerRelationTo(const StoreArray< TO > &toArray, DataStore::EDurability durability=DataStore::c_Event, DataStore::EStoreFlags storeFlags=DataStore::c_WriteOut, const std::string &namedRelation="") const
Register a relation to the given StoreArray.
Definition: StoreArray.h:140
static const double mm
[millimeters]
Definition: Unit.h:70
static const double e
Standard of [electric charge].
Definition: Unit.h:53
static const double um
[micrometers]
Definition: Unit.h:71
static const double ns
Standard of [time].
Definition: Unit.h:48
static const double T
[tesla]
Definition: Unit.h:120
float getGlobalTime() const override
Return the time of the electron deposition.
Definition: VXDSimHit.h:78
int getPDGcode() const
Return the PDG code of the particle causing the electron deposition.
Definition: VXDSimHit.h:68
ROOT::Math::XYZVector getPosOut() const
Return the end point of the electron deposition in local coordinates.
Definition: VXDSimHit.h:72
std::vector< std::pair< float, float > > getElectronsConstantDistance(double length) const
Get the electron deposition along constant stepsize.
Definition: VXDSimhit.cc:32
float getElectrons() const
Return the number of created electrons.
Definition: VXDSimhit.cc:15
ROOT::Math::XYZVector getPosIn() const
Return the start point of the electron deposition in local coordinates.
Definition: VXDSimHit.h:70
VxdID getSensorID() const
Return the sensorID of the sensor the electron was deposited in.
Definition: VXDSimHit.h:66
Class to faciliate easy access to sensor information of the VXD like coordinate transformations or pi...
Definition: GeoCache.h:39
const std::set< Belle2::VxdID > getLayers(SensorInfoBase::SensorType sensortype=SensorInfoBase::VXD)
Return a set of all known Layers.
Definition: GeoCache.cc:176
const std::set< Belle2::VxdID > & getSensors(Belle2::VxdID ladder) const
Return a set of all sensor IDs belonging to a given ladder.
Definition: GeoCache.cc:204
static GeoCache & getInstance()
Return a reference to the singleton instance.
Definition: GeoCache.cc:214
const GeoTools * getGeoTools()
Return a raw pointer to a GeoTools object.
Definition: GeoCache.h:147
static const SensorInfoBase & get(Belle2::VxdID id)
Return a reference to the SensorInfo of a given SensorID.
Definition: GeoCache.h:139
const std::set< Belle2::VxdID > & getLadders(Belle2::VxdID layer) const
Return a set of all ladder IDs belonging to a given layer.
Definition: GeoCache.cc:193
double getUPitch(double v=0) const
Return the pitch of the sensor.
double getWidth(double v=0) const
Return the width of the sensor.
VxdID getID() const
Return the ID of the Sensor.
double getVPitch(double v=0) const
Return the pitch of the sensor.
double getThickness() const
Return the thickness of the sensor.
int getVCellID(double v, bool clamp=false) const
Return the corresponding pixel/strip ID of a given v coordinate.
double getLength() const
Return the length of the sensor.
Class to uniquely identify a any structure of the PXD and SVD.
Definition: VxdID.h:33
baseType getLayerNumber() const
Get the layer id.
Definition: VxdID.h:96
void addParam(const std::string &name, T &paramVariable, const std::string &description, const T &defaultValue)
Adds a new parameter to the module.
Definition: Module.h:560
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:650
double sqrt(double a)
sqrt for double
Definition: beamHelpers.h:28
Namespace to encapsulate code needed for simulation and reconstrucion of the PXD.
std::map< Digit, DigitValue > Sensor
Map of all hits in one Sensor.
Abstract base class for different kinds of events.
RelationElement::index_type indexFrom
index of the element from which the relation points.
RelationElement::weight_type weight
weight of the relation.