Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
PHG4TpcDigitizer.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file PHG4TpcDigitizer.cc
1 #include "PHG4TpcDigitizer.h"
2 
3 #include <trackbase/TpcDefs.h>
4 #include <trackbase/TrkrDefs.h>
5 #include <trackbase/TrkrHit.h>
6 #include <trackbase/TrkrHitSet.h>
9 #include <trackbase/TrkrHitv2.h>
10 
13 
15 #include <fun4all/SubsysReco.h> // for SubsysReco
16 #
17 #include <phool/PHCompositeNode.h>
18 #include <phool/PHNode.h> // for PHNode
19 #include <phool/PHNodeIterator.h>
20 #include <phool/PHRandomSeed.h>
21 #include <phool/getClass.h>
22 #include <phool/phool.h> // for PHWHERE
23 
24 #include <gsl/gsl_randist.h>
25 #include <gsl/gsl_rng.h> // for gsl_rng_alloc
26 
27 #include <cstdlib> // for exit
28 #include <iostream>
29 #include <limits>
30 #include <memory> // for allocator_tra...
31 
33  : SubsysReco(name)
34  , TpcMinLayer(7)
35  , TpcNLayers(48)
36  , ADCThreshold(2700) // electrons
37  , TpcEnc(670) // electrons
38  , Pedestal(50000) // electrons
39  , ChargeToPeakVolts(20) // mV/fC
40  , ADCSignalConversionGain(std::numeric_limits<float>::signaling_NaN()) // will be assigned in PHG4TpcDigitizer::InitRun
41  , ADCNoiseConversionGain(std::numeric_limits<float>::signaling_NaN()) // will be assigned in PHG4TpcDigitizer::InitRun
42 {
43  unsigned int seed = PHRandomSeed(); // fixed seed is handled in this funtcion
44  std::cout << Name() << " random seed: " << seed << std::endl;
45  RandomGenerator = gsl_rng_alloc(gsl_rng_mt19937);
47 
48  if (Verbosity() > 0)
49  {
50  std::cout << "Creating PHG4TpcDigitizer with name = " << name << std::endl;
51  }
52 }
53 
55 {
56  gsl_rng_free(RandomGenerator);
57 }
58 
60 {
62 
63  // Factor that converts the charge in each z bin into a voltage in each z bin
64  // ChargeToPeakVolts relates TOTAL charge collected to peak voltage, while the cell maker actually distributes the signal
65  // GEM output charge in Z bins using the shaper time response. For 80 ns shaping, the scaleup factor of 2.4 gets the peak voltage right.
66  ADCSignalConversionGain = ChargeToPeakVolts * 1.60e-04 * 2.4; // 20 (or 30) mV/fC * fC/electron * scaleup factor
67  // The noise is by definition the RMS noise width voltage divided by ChargeToPeakVolts
68  ADCNoiseConversionGain = ChargeToPeakVolts * 1.60e-04; // 20 (or 30) mV/fC * fC/electron
69 
71 
72  //-------------
73  // Add Hit Node
74  //-------------
75  PHNodeIterator iter(topNode);
76 
77  // Looking for the DST node
78  PHCompositeNode *dstNode = dynamic_cast<PHCompositeNode *>(iter.findFirst("PHCompositeNode", "DST"));
79  if (!dstNode)
80  {
81  std::cout << PHWHERE << "DST Node missing, doing nothing." << std::endl;
83  }
84  PHNodeIterator iter_dst(dstNode);
85 
87 
88  //----------------
89  // Report Settings
90  //----------------
91 
92  if (Verbosity() > 0)
93  {
94  std::cout << "====================== PHG4TpcDigitizer::InitRun() =====================" << std::endl;
95  for (std::map<int, unsigned int>::iterator tpiter = _max_adc.begin();
96  tpiter != _max_adc.end();
97  ++tpiter)
98  {
99  std::cout << " Max ADC in Layer #" << tpiter->first << " = " << tpiter->second << std::endl;
100  }
101  for (std::map<int, float>::iterator tpiter = _energy_scale.begin();
102  tpiter != _energy_scale.end();
103  ++tpiter)
104  {
105  std::cout << " Energy per ADC in Layer #" << tpiter->first << " = " << 1.0e6 * tpiter->second << " keV" << std::endl;
106  }
107  std::cout << "===========================================================================" << std::endl;
108  }
109 
111 }
112 
114 {
115  DigitizeCylinderCells(topNode);
116 
118 }
119 
121 {
122  // defaults to 8-bit ADC, short-axis MIP placed at 1/4 dynamic range
123 
124  PHG4TpcCylinderGeomContainer *geom_container = findNode::getClass<PHG4TpcCylinderGeomContainer>(topNode, "CYLINDERCELLGEOM_SVTX");
125 
126  if (!geom_container) return;
127 
128  PHG4TpcCylinderGeomContainer::ConstRange layerrange = geom_container->get_begin_end();
129  for (PHG4TpcCylinderGeomContainer::ConstIterator layeriter = layerrange.first;
130  layeriter != layerrange.second;
131  ++layeriter)
132  {
133  int layer = layeriter->second->get_layer();
134  float thickness = (layeriter->second)->get_thickness();
135  float pitch = (layeriter->second)->get_phistep() * (layeriter->second)->get_radius();
136  float length = (layeriter->second)->get_zstep() * _drift_velocity;
137 
138  float minpath = pitch;
139  if (length < minpath) minpath = length;
140  if (thickness < minpath) minpath = thickness;
141  float mip_e = 0.003876 * minpath;
142 
143  if (_max_adc.find(layer) == _max_adc.end())
144  {
145 // cppcheck-suppress stlFindInsert
146  _max_adc[layer] = 255;
147  _energy_scale[layer] = mip_e / 64;
148  }
149  }
150 
151  return;
152 }
153 
155 {
156  unsigned int print_layer = 18; // to print diagnostic output for layer 47
157 
158  // Digitizes the Tpc cells that were created in PHG4CylinderCellTpcReco
159  // These contain as edep the number of electrons out of the GEM stack, distributed between Z bins by shaper response and ADC clock window
160  // - i.e. all of the phi and Z bins in a cluster have edep values that add up to the primary charge in the layer times 2000
161 
162  // NOTES:
163  // Modified by ADF June 2018 to do the following:
164  // Add noise to cells before digitizing
165  // Digitize the first adc time bin to exceed the threshold, and the 4 bins after that
166  // If the adc value is still above the threshold after 5 bins, repeat for the next 5 bins
167 
168  // Electron production:
169  // A MIP produces 32 electrons in 1.25 cm of Ne:CF4 gas
170  // The nominal GEM gain is 2000 => 64,000 electrons per MIP through 1.25 cm gas
171  // Thus a MIP produces a charge value out of the GEM stack of 64000/6.242x10^18 = 10.2 fC
172 
173  // SAMPA:
174  // See https://indico.cern.ch/event/489996/timetable/#all.detailed
175  // "SAMPA Chip: the New ASIC for the ALICE Tpc and MCH Upgrades", M Bregant
176  // The SAMPA has a maximum output voltage of 2200 mV (but the pedestal is about 200 mV)
177  // The SAMPA shaper is set to 80 ns peaking time
178  // The ADC Digitizes the SAMPA shaper output into 1024 channels
179  // Conversion gains of 20 mV/fC or 30 mV/fC are possible - 1 fC charge input produces a peak voltage out of
180  // the shaper of 20 or 30 mV
181  // At 30 mV/fC, the input signal saturates at 2.2 V / 30 mV/fC = 73 fC (say 67 with pedestal not at zero)
182  // At 20 mV/fC, the input signal saturates at 2.2 V / 20 mV/fC = 110 fC (say 100 fC with pedestal not at zero) - assume 20 mV/fC
183  // The equivalent noise charge RMS at 20 mV/fC was measured (w/o detector capacitance) at 490 electrons
184  // - note: this appears to be just the pedestal RMS voltage spread divided by the conversion gain, so it is a bit of a
185  // funny number (see below)
186  // - it is better to think of noise and signal in terms of voltage at the input of the ADC
187  // Bregant's slides say 670 electrons ENC for the full chip with 18 pf detector, as in ALICE - should use that number
188 
189  // Signal:
190  // To normalize the signal in each cell, take the entire charge on the pad and multiply by 20 mV/fC to get the adc
191  // input AT THE PEAK of the shaper
192  // The cell contents should thus be multipied by the normalization given by:
193  // V_peak = Q_pad (electrons) * 1.6e-04 fC/electron * 20 mV/fC
194  // From the sims, for 80 ns and 18.8 MHz, if we take the input charge and spread it a across the shaping time (which is how it has always been done, and is
195  // not the best way to think about it, because the ADC does not see charge it sees voltage out of a charge integrating
196  // preamp followed by a shaper), to get
197  // the voltage at the ADC input right, then the values of Q_(pad,z) have to be scaled up by 2.4
198  // V_(pad,z) = 2.4 * Q_(pad,z) (electrons) * 1.6e-04 fC/electron * 20 mV/fC = Q_(pad,z) * 7.68e-03 (in mV)
199  // ADU_(pad,z) = V_(pad,z) * (1024 ADU / 2200 mV) = V_(pad,z) * 0.465
200  // Remember that Q_(pad,z) is the GEM output charge
201 
202  // Noise:
203  // The ENC is defined as the RMS spread of the ADC pedestal distribution coming out from the SAMPA
204  // divided by the corresponding conversion gain.
205  // The full range of the ADC input is 2.2V (which will become 1024 adc counts, i.e. 1024 ADU's).
206  // If you see the RMS of the pedestal in adc counts as 1 at the gain of 20mV/fC, the ENC would be defined by:
207  // (2200 [mV]) * (1/1024) / (20[mV/fC]) / (1.6*10^-4 [fC]) = 671 [electrons]
208  // The RMS noise voltage would be:
209  // V_RMS = ENC (electrons) *1.6e-04 fC/electron * 20 mV/fC = ENC (electrons) * 3.2e-03 (in mV)
210  // The ADC readout would be: ADU = V_RMS * (1024 ADU / 2200 mV) = V_RMS * 0.465
211 
212  // The cells that we need to digitize here contain as the energy "edep", which is the number of electrons out of the GEM stack
213  // distributed over the pads and ADC time bins according to the output time distribution of the SAMPA shaper
214  // - not what really happens, see above
215  // We convert to volts at the input to the ADC and add noise generated with the RMS value of the noise voltage at the ADC input
216  // We assume the pedestal is zero, for simplicity, so the noise fluctuates above and below zero
217 
218  // Note that tbin = 0 corresponds to -105.5 cm, tbin 248 corresponds to 0 cm, and tbin 497 corresponds to +105.5 cm
219  // increasing time should be bins (497 -> 249) and (0 -> 248)
220 
221  //----------
222  // Get Nodes
223  //----------
224 
225  PHG4TpcCylinderGeomContainer *geom_container =
226  findNode::getClass<PHG4TpcCylinderGeomContainer>(topNode, "CYLINDERCELLGEOM_SVTX");
227  if (!geom_container)
228  {
229  std::cout << PHWHERE << "ERROR: Can't find node CYLINDERCELLGEOM_SVTX" << std::endl;
230  }
231 
232  // Get the TrkrHitSetContainer node
233  TrkrHitSetContainer *trkrhitsetcontainer = findNode::getClass<TrkrHitSetContainer>(topNode, "TRKR_HITSET");
234  if (!trkrhitsetcontainer)
235  {
236  std::cout << "Could not locate TRKR_HITSET node, quit! " << std::endl;
237  exit(1);
238  }
239 
240  TrkrHitTruthAssoc *hittruthassoc = findNode::getClass<TrkrHitTruthAssoc>(topNode, "TRKR_HITTRUTHASSOC");
241  if (!hittruthassoc)
242  {
243  std::cout << PHWHERE << " Could not locate TRKR_HITTRUTHASSOC node, quit! " << std::endl;
244  exit(1);
245  }
246 
247 
248  //-------------
249  // Digitization
250  //-------------
251 
252  // Loop over all TPC layers
253  for(unsigned int layer = TpcMinLayer; layer < TpcMinLayer+TpcNLayers; ++layer)
254  {
255  // we need the geometry object for this layer
256  PHG4TpcCylinderGeom *layergeom = geom_container->GetLayerCellGeom(layer);
257  if (!layergeom) exit(1);
258 
259  int nphibins = layergeom->get_phibins();
260  if(Verbosity() > 1)
261  std::cout << " nphibins " << nphibins << std::endl;
262 
263  for(unsigned int side = 0; side < 2; ++side)
264  {
265 
266  if(Verbosity() > 1)
267  std::cout << "TPC layer " << layer << " side " << side << std::endl;
268 
269  // for this layer and side, use a vector of a vector of cells for each phibin
270  phi_sorted_hits.clear();
271  for (int iphi = 0; iphi < nphibins; iphi++)
272  {
273  phi_sorted_hits.push_back(std::vector<TrkrHitSet::ConstIterator>());
274  }
275 
276  // Loop over all hitsets containing signals for this layer and add them to phi_sorted_hits for their phibin
277  TrkrHitSetContainer::ConstRange hitset_range = trkrhitsetcontainer->getHitSets(TrkrDefs::TrkrId::tpcId, layer);
278  for (TrkrHitSetContainer::ConstIterator hitset_iter = hitset_range.first;
279  hitset_iter != hitset_range.second;
280  ++hitset_iter)
281  {
282 
283  // we have an iterator to one TrkrHitSet for the Tpc from the trkrHitSetContainer
284  // get the hitset key
285  TrkrDefs::hitsetkey hitsetkey = hitset_iter->first;
286  unsigned int this_side = TpcDefs::getSide(hitsetkey);
287  // skip this hitset if it is not on this side
288  if(this_side != side) continue;
289 
290  if (Verbosity() > 2)
291  if (layer == print_layer)
292  std::cout << "new: PHG4TpcDigitizer: processing signal hits for layer " << layer
293  << " hitsetkey " << hitsetkey << " side " << side << std::endl;
294 
295  // get all of the hits from this hitset
296  TrkrHitSet *hitset = hitset_iter->second;
297  TrkrHitSet::ConstRange hit_range = hitset->getHits();
298  for (TrkrHitSet::ConstIterator hit_iter = hit_range.first;
299  hit_iter != hit_range.second;
300  ++hit_iter)
301  {
302  // Fill the vector of signal hits for each phibin
303  unsigned int phibin = TpcDefs::getPad(hit_iter->first);
304  phi_sorted_hits[phibin].push_back(hit_iter);
305  }
306  // For this hitset we now have the signal hits sorted into vectors for each phi
307  }
308 
309  // Process one phi bin at a time
310  if(Verbosity() > 1) std::cout << " phi_sorted_hits size " << phi_sorted_hits.size() << std::endl;
311  for (unsigned int iphi = 0; iphi < phi_sorted_hits.size(); iphi++)
312  {
313  // Make a fixed length vector to indicate whether each time bin is signal or noise
314  int ntbins = layergeom->get_zbins();
315  is_populated.clear();
316  is_populated.assign(ntbins, 2); // mark all as noise only, for now
317 
318  // add an empty vector of hits for each t bin
319  t_sorted_hits.clear();
320  for (int it = 0; it < ntbins; it++)
321  {
322  t_sorted_hits.push_back(std::vector<TrkrHitSet::ConstIterator>());
323  }
324 
325  // add a signal hit from phi_sorted_hits for each t bin that has one
326  for (unsigned int it = 0; it < phi_sorted_hits[iphi].size(); it++)
327  {
328  int tbin = TpcDefs::getTBin(phi_sorted_hits[iphi][it]->first);
329  is_populated[tbin] = 1; // this bin is a associated with a hit
330  t_sorted_hits[tbin].push_back(phi_sorted_hits[iphi][it]);
331 
332  if (Verbosity() > 2)
333  if (layer == print_layer)
334  {
335  TrkrDefs::hitkey hitkey = phi_sorted_hits[iphi][it]->first;
336  std::cout << "iphi " << iphi << " adding existing signal hit to t vector for layer " << layer
337  << " side " << side
338  << " tbin " << tbin << " hitkey " << hitkey
339  << " pad " << TpcDefs::getPad(hitkey)
340  << " t bin " << TpcDefs::getTBin(hitkey)
341  << " energy " << (phi_sorted_hits[iphi][it]->second)->getEnergy()
342  << std::endl;
343  }
344  }
345 
346  adc_input.clear();
347  adc_hitid.clear();
348  // initialize entries to zero for each t bin
349  adc_input.assign(ntbins, 0.0);
350  adc_hitid.assign(ntbins, 0);
351 
352  // Now for this phibin we process all bins ordered by t into hits with noise
353  //======================================================
354  // For this step we take the edep value and convert it to mV at the ADC input
355  // See comments above for how to do this for signal and noise
356 
357  for (int it = 0; it < ntbins; it++)
358  {
359  if (is_populated[it] == 1)
360  {
361  // This tbin has a hit, add noise
362  float signal_with_noise = add_noise_to_bin( (t_sorted_hits[it][0]->second)->getEnergy()) ;
363  adc_input[it] = signal_with_noise ;
364  adc_hitid[it] = t_sorted_hits[it][0]->first;
365 
366  if (Verbosity() > 2)
367  if (layer == print_layer)
368  std::cout << "existing signal hit: layer " << layer << " iphi " << iphi << " it " << it
369  << " edep " << (t_sorted_hits[it][0]->second)->getEnergy()
370  << " adc gain " << ADCSignalConversionGain
371  << " signal with noise " << signal_with_noise
372  << " adc_input " << adc_input[it] << std::endl;
373  }
374  else if (is_populated[it] == 2)
375  {
376  if(!skip_noise)
377  {
378  // This t bin does not have a filled cell, add noise
379  float noise = add_noise_to_bin(0.0);
380  adc_input[it]= noise;
381  adc_hitid[it] = 0; // there is no hit, just add a placeholder in the vector for now, replace it later
382 
383  if (Verbosity() > 2)
384  if (layer == print_layer)
385  std::cout << "noise hit: layer " << layer << " side " << side << " iphi " << iphi << " it " << it
386  << " adc gain " << ADCSignalConversionGain
387  << " noise " << noise
388  << " adc_input " << adc_input[it] << std::endl;
389  }
390  }
391  else
392  {
393  // Cannot happen
394  std::cout << "Impossible value of is_populated, it = " << it
395  << " is_populated = " << is_populated[it] << std::endl;
396  exit(-1);
397  }
398  }
399 
400  // Now we can digitize the entire stream of t bins for this phi bin
401  int binpointer = 0;
402 
403  // Since we now store the local z of the hit as time of arrival at the readout plane,
404  // there is no difference between north and south
405  // The first to arrive is always bin 0
406 
407  for (int it = 0; it < ntbins; it++)
408  {
409  if (it < binpointer) continue;
410 
411  // optionally do not trigger on bins with no signal
412  if( (is_populated[it] == 2) && skip_noise)
413  {
414  binpointer++;
415  continue;
416  }
417 
418  // convert threshold in "equivalent electrons" to mV
419  if (adc_input[it] > ADCThreshold_mV)
420  {
421  // digitize this bin and the following 4 bins
422 
423  if (Verbosity() > 2)
424  if (layer == print_layer)
425  std::cout << std::endl
426  << "Hit above threshold of "
427  << ADCThreshold * ADCNoiseConversionGain << " for phibin " << iphi
428  << " it " << it << " with adc_input " << adc_input[it]
429  << " digitize this and 4 following bins: " << std::endl;
430 
431  for (int itup = 0; itup < 5; itup++)
432  {
433  if (it + itup < ntbins && it + itup >= 0) // stay within the bin limits
434  {
435  float input = 0;
436  if( (is_populated[it+itup] == 2) && skip_noise)
437  {
438  input = add_noise_to_bin(0.0); // no noise added to this bin previously because skip_noise is true
439  }
440  else
441  {
442  input = adc_input[it + itup];
443  }
444  // input voltage x 1024 channels over 2200 mV max range
445  unsigned int adc_output = (unsigned int) (input * 1024.0 / 2200.0);
446 
447  if (input < 0) adc_output = 0;
448  if (input > 1023) adc_output = 1023;
449 
450  // Get the hitkey
452  TrkrHit *hit = nullptr;
453 
454  if (Verbosity() > 2)
455  if (layer == print_layer)
456  std::cout << " Digitizing: iphi " << iphi << " it+itup " << it + itup
457  << " adc_hitid " << adc_hitid[it + itup]
458  << " is_populated " << is_populated[it + itup]
459  << " adc_input " << adc_input[it + itup]
460  << " ADCThreshold " << ADCThreshold * ADCNoiseConversionGain
461  << " adc_output " << adc_output
462  << " hitkey " << hitkey
463  << " side " << side
464  << " binpointer " << binpointer
465  << std::endl;
466 
467  if (is_populated[it+itup] == 1)
468  {
469  // this is a signal hit, it already exists
470  hit = t_sorted_hits[it+itup][0]->second; // pointer valid only for signal hits
471  }
472  else
473  {
474  // Hit does not exist yet, have to make one
475  // we need the hitset key, requires (layer, sector, side)
476  unsigned int sector = 12 * iphi / nphibins;
478  auto hitset_iter = trkrhitsetcontainer->findOrAddHitSet(hitsetkey);
479 
480  hit = new TrkrHitv2();
481  hitset_iter->second->addHitSpecificKey(hitkey, hit);
482 
483  if (Verbosity() > 2)
484  if (layer == print_layer)
485  std::cout << " adding noise TrkrHit for iphi " << iphi
486  << " tbin " << it + itup
487  << " side " << side
488  << " created new hit with hitkey " << hitkey
489  << " energy " << adc_input[it + itup] << " adc " << adc_output
490  << " binpointer " << binpointer
491  << std::endl;
492 
493  }
494 
495  hit->setAdc(adc_output);
496 
497  } // end boundary check
498  binpointer++; // skip this bin in future
499  } // end itup loop
500 
501  } // adc threshold if
502  else
503  {
504  // set adc value to zero if there is a hit
505  // we need the hitset key, requires (layer, sector, side)
506  unsigned int sector = 12 * iphi / nphibins;
508  auto hitset = trkrhitsetcontainer->findHitSet(hitsetkey);
509  if(hitset)
510  {
511  // Get the hitkey
513  TrkrHit *hit = nullptr;
514  hit = hitset->getHit(hitkey);
515  if (hit)
516  {
517  hit->setAdc(0);
518  }
519  }
520  // bin below threshold, move on
521  binpointer++;
522  } // end adc threshold if/else
523  } // end time bin loop
524  } // end phibins loop
525  } // end loop over sides
526  } // end loop over TPC layers
527 
528  //======================================================
529  if (Verbosity() > 5)
530  {
531  std::cout << "From PHG4TpcDigitizer: hitsetcontainer dump at end before cleaning:" << std::endl;
532  }
533  std::vector<std::pair<TrkrDefs::hitsetkey, TrkrDefs::hitkey>> delete_hitkey_list;
534 
535  // Clean up undigitized hits - we want all hitsets for the Tpc
536  // This loop is pretty efficient because the remove methods all take a specified hitset as input
537  TrkrHitSetContainer::ConstRange hitset_range_now = trkrhitsetcontainer->getHitSets(TrkrDefs::TrkrId::tpcId);
538  for (TrkrHitSetContainer::ConstIterator hitset_iter = hitset_range_now.first;
539  hitset_iter != hitset_range_now.second;
540  ++hitset_iter)
541  {
542  // we have an iterator to one TrkrHitSet for the Tpc from the trkrHitSetContainer
543  TrkrDefs::hitsetkey hitsetkey = hitset_iter->first;
544  const unsigned int layer = TrkrDefs::getLayer(hitsetkey);
545  const int sector = TpcDefs::getSectorId(hitsetkey);
546  const int side = TpcDefs::getSide(hitsetkey);
547  if (Verbosity() > 5)
548  std::cout << "PHG4TpcDigitizer: hitset with key: " << hitsetkey << " in layer " << layer << " with sector " << sector << " side " << side << std::endl;
549 
550  // get all of the hits from this hitset
551  TrkrHitSet *hitset = hitset_iter->second;
552  TrkrHitSet::ConstRange hit_range = hitset->getHits();
553  for (TrkrHitSet::ConstIterator hit_iter = hit_range.first;
554  hit_iter != hit_range.second;
555  ++hit_iter)
556  {
557  TrkrDefs::hitkey hitkey = hit_iter->first;
558  TrkrHit *tpchit = hit_iter->second;
559 
560  if (Verbosity() > 5)
561  std::cout << " layer " << layer << " hitkey " << hitkey << " pad " << TpcDefs::getPad(hitkey)
562  << " t bin " << TpcDefs::getTBin(hitkey)
563  << " adc " << tpchit->getAdc() << std::endl;
564 
565  if (tpchit->getAdc() == 0)
566  {
567  if (Verbosity() > 20)
568  {
569  std::cout << " -- this hit not digitized - delete it" << std::endl;
570  }
571  // screws up the iterator to delete it here, store the hitkey for later deletion
572  delete_hitkey_list.push_back(std::make_pair(hitsetkey, hitkey));
573  }
574  }
575  }
576 
577  // delete all undigitized hits
578  for (unsigned int i = 0; i < delete_hitkey_list.size(); i++)
579  {
580  TrkrHitSet *hitset = trkrhitsetcontainer->findHitSet(delete_hitkey_list[i].first);
581  const unsigned int layer = TrkrDefs::getLayer(delete_hitkey_list[i].first);
582  hitset->removeHit(delete_hitkey_list[i].second);
583  if (Verbosity() > 20)
584  if (layer == print_layer)
585  std::cout << "removed hit with hitsetkey " << delete_hitkey_list[i].first
586  << " and hitkey " << delete_hitkey_list[i].second << std::endl;
587 
588  // should also delete all entries with this hitkey from the TrkrHitTruthAssoc map
589  //hittruthassoc->removeAssoc(delete_hitkey_list[i].first, delete_hitkey_list[i].second); // Slow! Commented out by ADF 9/6/2022
590  }
591 
592  // Final hitset dump
593  if (Verbosity() > 5)
594  std::cout << "From PHG4TpcDigitizer: hitsetcontainer dump at end after cleaning:" << std::endl;
595  // We want all hitsets for the Tpc
596  TrkrHitSetContainer::ConstRange hitset_range_final = trkrhitsetcontainer->getHitSets(TrkrDefs::TrkrId::tpcId);
597  for (TrkrHitSetContainer::ConstIterator hitset_iter = hitset_range_final.first;
598  hitset_iter != hitset_range_now.second;
599  ++hitset_iter)
600  {
601  // we have an itrator to one TrkrHitSet for the Tpc from the trkrHitSetContainer
602  TrkrDefs::hitsetkey hitsetkey = hitset_iter->first;
603  const unsigned int layer = TrkrDefs::getLayer(hitsetkey);
604  if (layer != print_layer) continue;
605  const int sector = TpcDefs::getSectorId(hitsetkey);
606  const int side = TpcDefs::getSide(hitsetkey);
607  if (Verbosity() > 5 && layer == print_layer)
608  std::cout << "PHG4TpcDigitizer: hitset with key: " << hitsetkey << " in layer " << layer << " with sector " << sector << " side " << side << std::endl;
609 
610  // get all of the hits from this hitset
611  TrkrHitSet *hitset = hitset_iter->second;
612  TrkrHitSet::ConstRange hit_range = hitset->getHits();
613  for (TrkrHitSet::ConstIterator hit_iter = hit_range.first;
614  hit_iter != hit_range.second;
615  ++hit_iter)
616  {
617  TrkrDefs::hitkey hitkey = hit_iter->first;
618  TrkrHit *tpchit = hit_iter->second;
619  if (Verbosity() > 5)
620  std::cout << " LAYER " << layer << " hitkey " << hitkey << " pad " << TpcDefs::getPad(hitkey) << " t bin " << TpcDefs::getTBin(hitkey)
621  << " adc " << tpchit->getAdc() << std::endl;
622 
623  if (tpchit->getAdc() == 0)
624  {
625  std::cout << " Oops! -- this hit not digitized and not deleted!" << std::endl;
626  }
627  }
628  }
629 
630  //hittruthassoc->identify();
631 
632  return;
633 }
634 
636 {
637  // add noise to the signal and return adc input voltage
638  float adc_input_voltage = signal * ADCSignalConversionGain; // mV, see comments above
639  float noise_voltage = ( Pedestal + added_noise() ) * ADCNoiseConversionGain; // mV - from definition of noise charge and pedestal charge
640  adc_input_voltage += noise_voltage;
641 
642  return adc_input_voltage;
643 }
644 
646 {
647  float noise = gsl_ran_gaussian(RandomGenerator, TpcEnc);
648 
649  return noise;
650 }