Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-05 08:18:16

0001 #include "PHG4TpcDigitizer.h"
0002 
0003 #include <trackbase/TpcDefs.h>
0004 #include <trackbase/TrkrDefs.h>
0005 #include <trackbase/TrkrHit.h>
0006 #include <trackbase/TrkrHitSet.h>
0007 #include <trackbase/TrkrHitSetContainer.h>
0008 #include <trackbase/TrkrHitTruthAssoc.h>
0009 #include <trackbase/TrkrHitv2.h>
0010 
0011 #include <g4detectors/PHG4TpcCylinderGeom.h>
0012 #include <g4detectors/PHG4TpcCylinderGeomContainer.h>
0013 
0014 #include <fun4all/Fun4AllReturnCodes.h>
0015 #include <fun4all/SubsysReco.h>  // for SubsysReco
0016 #
0017 #include <phool/PHCompositeNode.h>
0018 #include <phool/PHNode.h>  // for PHNode
0019 #include <phool/PHNodeIterator.h>
0020 #include <phool/PHRandomSeed.h>
0021 #include <phool/getClass.h>
0022 #include <phool/phool.h>  // for PHWHERE
0023 
0024 #include <gsl/gsl_randist.h>
0025 #include <gsl/gsl_rng.h>  // for gsl_rng_alloc
0026 
0027 #include <cstdlib>  // for exit
0028 #include <iostream>
0029 #include <limits>
0030 #include <memory>  // for allocator_tra...
0031 
0032 PHG4TpcDigitizer::PHG4TpcDigitizer(const std::string &name)
0033   : SubsysReco(name)
0034   , TpcMinLayer(7)
0035   , TpcNLayers(48)
0036   , ADCThreshold(2700)                                                    // electrons
0037   , TpcEnc(670)                                                           // electrons
0038   , Pedestal(50000)                                                       // electrons
0039   , ChargeToPeakVolts(20)                                                 // mV/fC
0040   , ADCSignalConversionGain(std::numeric_limits<float>::signaling_NaN())  // will be assigned in PHG4TpcDigitizer::InitRun
0041   , ADCNoiseConversionGain(std::numeric_limits<float>::signaling_NaN())   // will be assigned in PHG4TpcDigitizer::InitRun
0042 {
0043   unsigned int seed = PHRandomSeed();  // fixed seed is handled in this funtcion
0044   std::cout << Name() << " random seed: " << seed << std::endl;
0045   RandomGenerator = gsl_rng_alloc(gsl_rng_mt19937);
0046   gsl_rng_set(RandomGenerator, seed);
0047 
0048   if (Verbosity() > 0)
0049   {
0050     std::cout << "Creating PHG4TpcDigitizer with name = " << name << std::endl;
0051   }
0052 }
0053 
0054 PHG4TpcDigitizer::~PHG4TpcDigitizer()
0055 {
0056   gsl_rng_free(RandomGenerator);
0057 }
0058 
0059 int PHG4TpcDigitizer::InitRun(PHCompositeNode *topNode)
0060 {
0061   ADCThreshold += Pedestal;
0062 
0063   // Factor that converts the charge in each z bin into a voltage in each z bin
0064   // ChargeToPeakVolts relates TOTAL charge collected to peak voltage, while the cell maker actually distributes the signal
0065   // 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.
0066   ADCSignalConversionGain = ChargeToPeakVolts * 1.60e-04 * 2.4;  // 20 (or 30) mV/fC * fC/electron * scaleup factor
0067   // The noise is by definition the RMS noise width voltage divided by ChargeToPeakVolts
0068   ADCNoiseConversionGain = ChargeToPeakVolts * 1.60e-04;  // 20 (or 30) mV/fC * fC/electron
0069 
0070   ADCThreshold_mV = ADCThreshold *  ADCNoiseConversionGain;
0071 
0072   //-------------
0073   // Add Hit Node
0074   //-------------
0075   PHNodeIterator iter(topNode);
0076 
0077   // Looking for the DST node
0078   PHCompositeNode *dstNode = dynamic_cast<PHCompositeNode *>(iter.findFirst("PHCompositeNode", "DST"));
0079   if (!dstNode)
0080   {
0081     std::cout << PHWHERE << "DST Node missing, doing nothing." << std::endl;
0082     return Fun4AllReturnCodes::ABORTRUN;
0083   }
0084   PHNodeIterator iter_dst(dstNode);
0085 
0086   CalculateCylinderCellADCScale(topNode);
0087 
0088   //----------------
0089   // Report Settings
0090   //----------------
0091 
0092   if (Verbosity() > 0)
0093   {
0094     std::cout << "====================== PHG4TpcDigitizer::InitRun() =====================" << std::endl;
0095     for (auto & tpiter : _max_adc)
0096     {
0097       std::cout << " Max ADC in Layer #" << tpiter.first << " = " << tpiter.second << std::endl;
0098     }
0099     for (auto & tpiter : _energy_scale)
0100     {
0101       std::cout << " Energy per ADC in Layer #" << tpiter.first << " = " << 1.0e6 * tpiter.second << " keV" << std::endl;
0102     }
0103     std::cout << "===========================================================================" << std::endl;
0104   }
0105 
0106   return Fun4AllReturnCodes::EVENT_OK;
0107 }
0108 
0109 int PHG4TpcDigitizer::process_event(PHCompositeNode *topNode)
0110 {
0111   DigitizeCylinderCells(topNode);
0112 
0113   return Fun4AllReturnCodes::EVENT_OK;
0114 }
0115 
0116 void PHG4TpcDigitizer::CalculateCylinderCellADCScale(PHCompositeNode *topNode)
0117 {
0118   // defaults to 8-bit ADC, short-axis MIP placed at 1/4 dynamic range
0119 
0120   PHG4TpcCylinderGeomContainer *geom_container = findNode::getClass<PHG4TpcCylinderGeomContainer>(topNode, "CYLINDERCELLGEOM_SVTX");
0121 
0122   if (!geom_container) { return;
0123 }
0124 
0125   PHG4TpcCylinderGeomContainer::ConstRange layerrange = geom_container->get_begin_end();
0126   for (PHG4TpcCylinderGeomContainer::ConstIterator layeriter = layerrange.first;
0127        layeriter != layerrange.second;
0128        ++layeriter)
0129   {
0130     int layer = layeriter->second->get_layer();
0131     float thickness = (layeriter->second)->get_thickness();
0132     float pitch = (layeriter->second)->get_phistep() * (layeriter->second)->get_radius();
0133     float length = (layeriter->second)->get_zstep() * _drift_velocity;
0134 
0135     float minpath = pitch;
0136     if (length < minpath) { minpath = length;
0137 }
0138     if (thickness < minpath) { minpath = thickness;
0139 }
0140     float mip_e = 0.003876 * minpath;
0141 
0142     if (_max_adc.find(layer) == _max_adc.end())
0143     {
0144 // cppcheck-suppress stlFindInsert
0145       _max_adc[layer] = 255;
0146       _energy_scale[layer] = mip_e / 64;
0147     }
0148   }
0149 
0150   return;
0151 }
0152 
0153 void PHG4TpcDigitizer::DigitizeCylinderCells(PHCompositeNode *topNode)
0154 {
0155   unsigned int print_layer = 18;  // to print diagnostic output for layer 47
0156 
0157   // Digitizes the Tpc cells that were created in PHG4CylinderCellTpcReco
0158   // These contain as edep the number of electrons out of the GEM stack, distributed between Z bins by shaper response and ADC clock window
0159   // - 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
0160 
0161   // NOTES:
0162   // Modified by ADF June 2018 to do the following:
0163   //   Add noise to cells before digitizing
0164   //   Digitize the first adc time bin to exceed the threshold, and the 4 bins after that
0165   //   If the adc value is still above the threshold after 5 bins, repeat for the next 5 bins
0166 
0167   // Electron production:
0168   // A MIP produces 32 electrons in 1.25 cm of Ne:CF4 gas
0169   // The nominal GEM gain is 2000 => 64,000 electrons per MIP through 1.25 cm gas
0170   // Thus a MIP produces a charge value out of the GEM stack of 64000/6.242x10^18 = 10.2 fC
0171 
0172   // SAMPA:
0173   // See https://indico.cern.ch/event/489996/timetable/#all.detailed 
0174   //      "SAMPA Chip: the New ASIC for the ALICE Tpc and MCH Upgrades", M Bregant
0175   // The SAMPA has a maximum output voltage of 2200 mV (but the pedestal is about 200 mV)
0176   // The SAMPA shaper is set to 80 ns peaking time
0177   // The ADC Digitizes the SAMPA shaper output into 1024 channels
0178   // Conversion gains of 20 mV/fC or 30 mV/fC are possible - 1 fC charge input produces a peak voltage out of 
0179   // the shaper of 20 or 30 mV
0180   //   At 30 mV/fC, the input signal saturates at 2.2 V / 30 mV/fC = 73 fC (say 67 with pedestal not at zero)
0181   //   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
0182   // The equivalent noise charge RMS at 20 mV/fC was measured (w/o detector capacitance) at 490 electrons
0183   //      - note: this appears to be just the pedestal RMS voltage spread divided by the conversion gain, so it is a bit of a 
0184   //         funny number (see below)
0185   //      - it is better to think of noise and signal in terms of voltage at the input of the ADC
0186   // Bregant's slides say 670 electrons ENC for the full chip with 18 pf detector, as in ALICE - should use that number
0187 
0188   // Signal:
0189   // To normalize the signal in each cell, take the entire charge on the pad and multiply by 20 mV/fC to get the adc 
0190   //       input AT THE PEAK of the shaper
0191   // The cell contents should thus be multipied by the normalization given by:
0192   // V_peak = Q_pad (electrons) * 1.6e-04 fC/electron * 20 mV/fC
0193   // 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
0194   // not the best way to think about it, because the ADC does not see charge it sees voltage out of a charge integrating 
0195   //     preamp followed by a shaper), to get
0196   // the voltage at the ADC input right, then the values of Q_(pad,z) have to be scaled up by 2.4
0197   // 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)
0198   // ADU_(pad,z) = V_(pad,z) * (1024 ADU / 2200 mV) = V_(pad,z) * 0.465
0199   // Remember that Q_(pad,z) is the GEM output charge
0200 
0201   // Noise:
0202   // The ENC is defined as the RMS spread of the ADC pedestal distribution coming out from the SAMPA 
0203   //      divided by the corresponding conversion gain.
0204   // The full range of the ADC input is 2.2V (which will become 1024 adc counts, i.e. 1024 ADU's).
0205   // 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:
0206   //             (2200 [mV]) * (1/1024) / (20[mV/fC]) / (1.6*10^-4 [fC]) = 671 [electrons]
0207   // The RMS noise voltage would be: 
0208   //     V_RMS = ENC (electrons) *1.6e-04 fC/electron * 20 mV/fC = ENC (electrons) * 3.2e-03 (in mV)
0209   // The ADC readout would be:  ADU = V_RMS * (1024 ADU / 2200 mV) = V_RMS * 0.465
0210 
0211   // The cells that we need to digitize here contain as the energy "edep", which is the number of electrons out of the GEM stack
0212   // distributed over the pads and ADC time bins according to the output time distribution of the SAMPA shaper 
0213   //      - not what really happens, see above
0214   // 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
0215   // We assume the pedestal is zero, for simplicity, so the noise fluctuates above and below zero
0216 
0217   // Note that tbin = 0 corresponds to -105.5 cm, tbin 248 corresponds to 0 cm, and tbin 497 corresponds to +105.5 cm
0218   // increasing time should be bins (497 -> 249) and (0 -> 248)
0219 
0220   //----------
0221   // Get Nodes
0222   //----------
0223 
0224   PHG4TpcCylinderGeomContainer *geom_container =
0225       findNode::getClass<PHG4TpcCylinderGeomContainer>(topNode, "CYLINDERCELLGEOM_SVTX");
0226   if (!geom_container)
0227   {
0228     std::cout << PHWHERE << "ERROR: Can't find node CYLINDERCELLGEOM_SVTX" << std::endl;
0229   }
0230 
0231   // Get the TrkrHitSetContainer node
0232   TrkrHitSetContainer *trkrhitsetcontainer = findNode::getClass<TrkrHitSetContainer>(topNode, "TRKR_HITSET");
0233   if (!trkrhitsetcontainer)
0234   {
0235     std::cout << "Could not locate TRKR_HITSET node, quit! " << std::endl;
0236     exit(1);
0237   }
0238 
0239   TrkrHitTruthAssoc *hittruthassoc = findNode::getClass<TrkrHitTruthAssoc>(topNode, "TRKR_HITTRUTHASSOC");
0240   if (!hittruthassoc)
0241   {
0242     std::cout << PHWHERE << " Could not locate TRKR_HITTRUTHASSOC node, quit! " << std::endl;
0243     exit(1);
0244   }
0245 
0246 
0247   //-------------
0248   // Digitization
0249   //-------------
0250 
0251   // Loop over all TPC layers 
0252   for(unsigned int layer = TpcMinLayer; layer < TpcMinLayer+TpcNLayers; ++layer)
0253     {
0254       // we need the geometry object for this layer
0255       PHG4TpcCylinderGeom *layergeom = geom_container->GetLayerCellGeom(layer);
0256       if (!layergeom) { exit(1);
0257 }
0258 
0259       int nphibins = layergeom->get_phibins();
0260       if(Verbosity() > 1) { 
0261         std::cout << "    nphibins " << nphibins << std::endl;
0262 }
0263       
0264       for(unsigned int side = 0; side < 2; ++side)
0265     {
0266       
0267       if(Verbosity() > 1) { 
0268         std::cout << "TPC layer " << layer << " side " << side << std::endl;
0269 }
0270       
0271       // for this layer and side, use a vector of a vector of cells for each phibin
0272       phi_sorted_hits.clear();
0273       for (int iphi = 0; iphi < nphibins; iphi++)
0274         {
0275           phi_sorted_hits.emplace_back();
0276         }
0277       
0278       // Loop over all hitsets containing signals for this layer and add them to phi_sorted_hits for their phibin
0279       TrkrHitSetContainer::ConstRange hitset_range = trkrhitsetcontainer->getHitSets(TrkrDefs::TrkrId::tpcId, layer);
0280       for (TrkrHitSetContainer::ConstIterator hitset_iter = hitset_range.first;
0281            hitset_iter != hitset_range.second;
0282            ++hitset_iter)
0283         {
0284 
0285           // we have an iterator to one TrkrHitSet for the Tpc from the trkrHitSetContainer
0286           // get the hitset key
0287           TrkrDefs::hitsetkey hitsetkey = hitset_iter->first;
0288           unsigned int this_side = TpcDefs::getSide(hitsetkey);   
0289           // skip this hitset if it is not on this side
0290           if(this_side != side) { continue; 
0291 }
0292 
0293           if (Verbosity() > 2) {
0294         if (layer == print_layer) {
0295           std::cout << "new: PHG4TpcDigitizer:  processing signal hits for layer " << layer 
0296                 << " hitsetkey " << hitsetkey << " side " << side << std::endl;
0297 }
0298 }
0299       
0300           // get all of the hits from this hitset
0301           TrkrHitSet *hitset = hitset_iter->second;
0302           TrkrHitSet::ConstRange hit_range = hitset->getHits();
0303           for (TrkrHitSet::ConstIterator hit_iter = hit_range.first;
0304            hit_iter != hit_range.second;
0305            ++hit_iter)
0306         {
0307           // Fill the vector of signal hits for each phibin
0308           unsigned int phibin = TpcDefs::getPad(hit_iter->first);
0309           phi_sorted_hits[phibin].push_back(hit_iter);
0310         }
0311           // For this hitset we now have the signal hits sorted into vectors for each phi
0312         }
0313       
0314       // Process one phi bin at a time
0315       if(Verbosity() > 1) { std::cout << "    phi_sorted_hits size " <<  phi_sorted_hits.size() << " ntbins " << layergeom->get_zbins() << std::endl;
0316 }
0317 
0318       for (unsigned int iphi = 0; iphi < phi_sorted_hits.size(); iphi++)
0319         {
0320           // Make a fixed length vector to indicate whether each time bin is signal or noise
0321           int ntbins = layergeom->get_zbins();
0322           is_populated.clear();
0323           is_populated.assign(ntbins, 2);  // mark all as noise only, for now
0324           
0325           // add an empty vector of hits for each t bin
0326           t_sorted_hits.clear();
0327           for (int it = 0; it < ntbins; it++)
0328         {
0329           t_sorted_hits.emplace_back();
0330         }
0331           
0332           // add a signal hit from phi_sorted_hits for each t bin that has one
0333           for (unsigned int it = 0; it < phi_sorted_hits[iphi].size(); it++)
0334         {
0335           int tbin = TpcDefs::getTBin(phi_sorted_hits[iphi][it]->first);
0336           is_populated[tbin] = 1;  // this bin is a associated with a hit
0337           t_sorted_hits[tbin].push_back(phi_sorted_hits[iphi][it]);
0338           
0339           if (Verbosity() > 2) {
0340             if (layer == print_layer)
0341               {
0342             TrkrDefs::hitkey hitkey = phi_sorted_hits[iphi][it]->first;
0343             std::cout << "iphi " << iphi << " adding existing signal hit to t vector for layer " << layer 
0344                   << " side " << side
0345                   << " tbin " << tbin << "  hitkey " << hitkey
0346                   << " pad " << TpcDefs::getPad(hitkey)
0347                   << " t bin " << TpcDefs::getTBin(hitkey) 
0348                   << "  energy " << (phi_sorted_hits[iphi][it]->second)->getEnergy()
0349                   << std::endl;
0350               }
0351 }
0352         }
0353           
0354           adc_input.clear();
0355           adc_hitid.clear();
0356           // initialize entries to zero for each t bin
0357           adc_input.assign(ntbins, 0.0); 
0358           adc_hitid.assign(ntbins, 0); 
0359 
0360           // Now for this phibin we process all bins ordered by t into hits with noise
0361           //======================================================
0362           // For this step we take the edep value and convert it to mV at the ADC input
0363           // See comments above for how to do this for signal and noise
0364           
0365           for (int it = 0; it < ntbins; it++)
0366         {
0367           if (is_populated[it] == 1)
0368             {
0369               // This tbin has a hit, add noise
0370               float signal_with_noise =  add_noise_to_bin( (t_sorted_hits[it][0]->second)->getEnergy()) ;
0371               adc_input[it] = signal_with_noise ;
0372               adc_hitid[it] = t_sorted_hits[it][0]->first;
0373 
0374               if (Verbosity() > 2) {
0375             if (layer == print_layer) {
0376               std::cout << "existing signal hit: layer " << layer << " iphi " << iphi << " it " << it 
0377                     << " edep " << (t_sorted_hits[it][0]->second)->getEnergy()
0378                     << " adc gain " << ADCSignalConversionGain 
0379                     << " signal with noise " <<  signal_with_noise
0380                     << " adc_input " << adc_input[it] << std::endl;
0381 }
0382 }
0383             }
0384           else if (is_populated[it] == 2)
0385             {
0386               if(!skip_noise)
0387             {
0388               // This t bin does not have a filled cell, add noise
0389               float noise =  add_noise_to_bin(0.0);
0390               adc_input[it]= noise;
0391               adc_hitid[it] = 0;              // there is no hit, just add a placeholder in the vector for now, replace it later
0392               
0393               if (Verbosity() > 2) {
0394                 if (layer == print_layer) {
0395                   std::cout << "noise hit: layer " << layer << " side " << side << " iphi " << iphi << " it " << it
0396                     << " adc gain " << ADCSignalConversionGain
0397                     << " noise " << noise 
0398                     << " adc_input " << adc_input[it] << std::endl;
0399 }
0400 }
0401             }
0402             }
0403           else
0404             {
0405               // Cannot happen
0406               std::cout << "Impossible value of is_populated, it = " << it 
0407                 << " is_populated = " << is_populated[it] << std::endl;
0408               exit(-1);
0409             }
0410         }
0411           
0412           // Now we can digitize the entire stream of t bins for this phi bin
0413           int binpointer = 0;
0414           
0415           // Since we now store the local z of the hit as time of arrival at the readout plane, 
0416           // there is no difference between north and south
0417           // The first to arrive is always bin 0
0418           
0419           for (int it = 0; it < ntbins; it++)
0420         {
0421           if (it < binpointer) { continue;
0422 }
0423 
0424           // optionally do not trigger on bins with no signal
0425           if( (is_populated[it] == 2) && skip_noise) 
0426             {
0427               binpointer++;
0428               continue;       
0429             }
0430 
0431           // convert threshold in "equivalent electrons" to mV
0432           if (adc_input[it] > ADCThreshold_mV)  
0433             {
0434               // digitize this bin and the following 4 bins
0435               
0436               if (Verbosity() > 2) {
0437             if (layer == print_layer) { 
0438               std::cout << std::endl
0439                     << "Hit above threshold of " 
0440                     << ADCThreshold * ADCNoiseConversionGain << " for phibin " << iphi
0441                     << " it " << it << " with adc_input " << adc_input[it] 
0442                     << " digitize this and 4 following bins: " << std::endl;
0443 }
0444 }
0445               
0446               for (int itup = 0; itup < 5; itup++)
0447             {
0448               if (it + itup < ntbins && it + itup >= 0)  // stay within the bin limits
0449                 {
0450                   float input = 0;
0451                   if( (is_populated[it+itup] == 2) && skip_noise)
0452                 {
0453                   input =  add_noise_to_bin(0.0);  // no noise added to this bin previously because skip_noise is true
0454                 }
0455                   else
0456                 {
0457                   input = adc_input[it + itup];
0458                 }   
0459                   // input voltage x 1024 channels over 2200 mV max range
0460                   unsigned int adc_output  = (unsigned int) (input * 1024.0 / 2200.0);  
0461 
0462                   if (input < 0) { adc_output = 0;
0463 }
0464                   if (adc_output > 1023) { adc_output = 1023;
0465 }
0466                   
0467                   // Get the hitkey
0468                   TrkrDefs::hitkey hitkey = TpcDefs::genHitKey(iphi, it + itup);
0469                   TrkrHit *hit = nullptr;
0470                   
0471                   if (Verbosity() > 2) {
0472                 if (layer == print_layer) { 
0473                   std::cout << "    Digitizing:  iphi " << iphi << "  it+itup " << it + itup 
0474                         << " adc_hitid " << adc_hitid[it + itup]
0475                         << " is_populated " << is_populated[it + itup]
0476                         << "  adc_input " << adc_input[it + itup] 
0477                         << " ADCThreshold " << ADCThreshold * ADCNoiseConversionGain
0478                         << " adc_output " << adc_output 
0479                         << " hitkey " << hitkey
0480                         << " side " << side
0481                         << "  binpointer " << binpointer
0482                         << std::endl;             
0483 }
0484 }
0485                   
0486                   if (is_populated[it+itup] == 1)
0487                 {
0488                   // this is a signal hit, it already exists
0489                   hit = t_sorted_hits[it+itup][0]->second;  // pointer valid only for signal hits
0490                 }
0491                   else
0492                 {
0493                   // Hit does not exist yet, have to make one
0494                   // we need the hitset key, requires (layer, sector, side)
0495                   unsigned int sector = 12 * iphi / nphibins;
0496                   TrkrDefs::hitsetkey hitsetkey = TpcDefs::genHitSetKey(layer, sector, side);
0497                   auto hitset_iter = trkrhitsetcontainer->findOrAddHitSet(hitsetkey);
0498                   
0499                   hit = new TrkrHitv2();
0500                   hitset_iter->second->addHitSpecificKey(hitkey, hit);
0501                   
0502                   if (Verbosity() > 2) {
0503                     if (layer == print_layer) { 
0504                       std::cout << "      adding noise TrkrHit for iphi " << iphi 
0505                         << " tbin " << it + itup
0506                         << " side " << side
0507                         << " created new hit with hitkey " << hitkey
0508                         << " energy " << adc_input[it + itup] << " adc " << adc_output 
0509                         << "  binpointer " << binpointer
0510                         << std::endl;
0511 }
0512 }
0513                   
0514                 }
0515                   
0516                   hit->setAdc(adc_output);
0517                   
0518                 }              // end boundary check
0519               binpointer++;  // skip this bin in future
0520             }                // end itup loop
0521               
0522             }  //  adc threshold if
0523           else
0524             {
0525               // set adc value to zero if there is a hit
0526               // we need the hitset key, requires (layer, sector, side)
0527               unsigned int sector = 12 * iphi / nphibins;
0528               TrkrDefs::hitsetkey hitsetkey = TpcDefs::genHitSetKey(layer, sector, side);
0529               auto hitset = trkrhitsetcontainer->findHitSet(hitsetkey);
0530               if(hitset)
0531             {
0532               // Get the hitkey
0533               TrkrDefs::hitkey hitkey = TpcDefs::genHitKey(iphi, it);
0534               TrkrHit *hit = nullptr;
0535               hit = hitset->getHit(hitkey);
0536               if (hit)
0537                 {
0538                   hit->setAdc(0);
0539                 }
0540             }
0541               // bin below threshold, move on
0542               binpointer++;
0543             }  // end adc threshold if/else
0544         }    // end time bin loop
0545         }      // end phibins loop
0546     }  // end loop over sides     
0547     } // end loop over TPC layers
0548   
0549   //======================================================
0550   if (Verbosity() > 5)
0551     {
0552       std::cout << "From PHG4TpcDigitizer: hitsetcontainer dump at end before cleaning:" << std::endl;
0553     }
0554   std::vector<std::pair<TrkrDefs::hitsetkey, TrkrDefs::hitkey>> delete_hitkey_list;
0555   
0556   // Clean up undigitized hits - we want all hitsets for the Tpc
0557   // This loop is pretty efficient because the remove methods all take a specified hitset as input
0558   TrkrHitSetContainer::ConstRange hitset_range_now = trkrhitsetcontainer->getHitSets(TrkrDefs::TrkrId::tpcId);
0559   for (TrkrHitSetContainer::ConstIterator hitset_iter = hitset_range_now.first;
0560        hitset_iter != hitset_range_now.second;
0561        ++hitset_iter)
0562     {
0563       // we have an iterator to one TrkrHitSet for the Tpc from the trkrHitSetContainer
0564       TrkrDefs::hitsetkey hitsetkey = hitset_iter->first;
0565       const unsigned int layer = TrkrDefs::getLayer(hitsetkey);
0566       const int sector = TpcDefs::getSectorId(hitsetkey);
0567       const int side = TpcDefs::getSide(hitsetkey);
0568       if (Verbosity() > 5) {
0569     std::cout << "PHG4TpcDigitizer: hitset with key: " << hitsetkey << " in layer " << layer << " with sector " << sector << " side " << side << std::endl;
0570 }
0571       
0572       // get all of the hits from this hitset
0573       TrkrHitSet *hitset = hitset_iter->second;
0574       TrkrHitSet::ConstRange hit_range = hitset->getHits();
0575       for (TrkrHitSet::ConstIterator hit_iter = hit_range.first;
0576        hit_iter != hit_range.second;
0577        ++hit_iter)
0578     {
0579       TrkrDefs::hitkey hitkey = hit_iter->first;
0580       TrkrHit *tpchit = hit_iter->second;
0581       
0582       if (Verbosity() > 5) {
0583         std::cout << "    layer " << layer << "  hitkey " << hitkey << " pad " << TpcDefs::getPad(hitkey) 
0584               << " t bin " << TpcDefs::getTBin(hitkey)
0585               << " adc " << tpchit->getAdc() << std::endl;
0586 }
0587       
0588       if (tpchit->getAdc() == 0)
0589         {
0590           if (Verbosity() > 20)
0591         {
0592           std::cout << "                       --   this hit not digitized - delete it" << std::endl;
0593         }
0594           // screws up the iterator to delete it here, store the hitkey for later deletion
0595           delete_hitkey_list.emplace_back(hitsetkey, hitkey);
0596         }
0597     }
0598     }
0599   
0600   // delete all undigitized hits
0601   for (auto & i : delete_hitkey_list)
0602     {
0603       TrkrHitSet *hitset = trkrhitsetcontainer->findHitSet(i.first);
0604       const unsigned int layer = TrkrDefs::getLayer(i.first);
0605       hitset->removeHit(i.second);
0606       if (Verbosity() > 20) {
0607     if (layer == print_layer) {
0608       std::cout << "removed hit with hitsetkey " << i.first
0609             << " and hitkey " << i.second << std::endl;
0610 }
0611 }
0612       
0613       // should also delete all entries with this hitkey from the TrkrHitTruthAssoc map
0614       //hittruthassoc->removeAssoc(delete_hitkey_list[i].first, delete_hitkey_list[i].second);   // Slow! Commented out by ADF 9/6/2022
0615     }
0616   
0617   // Final hitset dump
0618   if (Verbosity() > 5) {
0619     std::cout << "From PHG4TpcDigitizer: hitsetcontainer dump at end after cleaning:" << std::endl;
0620 }
0621   // We want all hitsets for the Tpc
0622   TrkrHitSetContainer::ConstRange hitset_range_final = trkrhitsetcontainer->getHitSets(TrkrDefs::TrkrId::tpcId);
0623   for (TrkrHitSetContainer::ConstIterator hitset_iter = hitset_range_final.first;
0624        hitset_iter != hitset_range_now.second;
0625        ++hitset_iter)
0626   {
0627     // we have an itrator to one TrkrHitSet for the Tpc from the trkrHitSetContainer
0628     TrkrDefs::hitsetkey hitsetkey = hitset_iter->first;
0629     const unsigned int layer = TrkrDefs::getLayer(hitsetkey);
0630     if (layer != print_layer) { continue;
0631 }
0632     const int sector = TpcDefs::getSectorId(hitsetkey);
0633     const int side = TpcDefs::getSide(hitsetkey);
0634     if (Verbosity() > 5 && layer == print_layer) {
0635       std::cout << "PHG4TpcDigitizer: hitset with key: " << hitsetkey << " in layer " << layer << " with sector " << sector << " side " << side << std::endl;
0636 }
0637 
0638     // get all of the hits from this hitset
0639     TrkrHitSet *hitset = hitset_iter->second;
0640     TrkrHitSet::ConstRange hit_range = hitset->getHits();
0641     for (TrkrHitSet::ConstIterator hit_iter = hit_range.first;
0642          hit_iter != hit_range.second;
0643          ++hit_iter)
0644     {
0645       TrkrDefs::hitkey hitkey = hit_iter->first;
0646       TrkrHit *tpchit = hit_iter->second;
0647       if (Verbosity() > 5) {
0648         std::cout << "      LAYER " << layer << " hitkey " << hitkey << " pad " << TpcDefs::getPad(hitkey) << " t bin " << TpcDefs::getTBin(hitkey)
0649                   << " adc " << tpchit->getAdc() << std::endl;
0650 }
0651 
0652       if (tpchit->getAdc() == 0)
0653       {
0654         std::cout << "   Oops!                    --   this hit not digitized and not deleted!" << std::endl;
0655       }
0656     }
0657   }
0658 
0659   //hittruthassoc->identify();
0660 
0661   return;
0662 }
0663 
0664 float PHG4TpcDigitizer::add_noise_to_bin(float signal)
0665 {
0666   // add noise to the signal and return adc input voltage
0667   float adc_input_voltage = signal * ADCSignalConversionGain;  // mV, see comments above
0668   float noise_voltage = ( Pedestal + added_noise() ) * ADCNoiseConversionGain;  // mV - from definition of noise charge and pedestal charge
0669   adc_input_voltage += noise_voltage;
0670   
0671   return adc_input_voltage;
0672 }
0673 
0674 float PHG4TpcDigitizer::added_noise()
0675 {
0676   float noise = gsl_ran_gaussian(RandomGenerator, TpcEnc);
0677 
0678   return noise;
0679 }