Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-12-17 09:22:11

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