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)
0037 , TpcEnc(670)
0038 , Pedestal(50000)
0039 , ChargeToPeakVolts(20)
0040 , ADCSignalConversionGain(std::numeric_limits<float>::signaling_NaN())
0041 , ADCNoiseConversionGain(std::numeric_limits<float>::signaling_NaN())
0042 {
0043 unsigned int seed = PHRandomSeed();
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
0064
0065
0066 ADCSignalConversionGain = ChargeToPeakVolts * 1.60e-04 * 2.4;
0067
0068 ADCNoiseConversionGain = ChargeToPeakVolts * 1.60e-04;
0069
0070 ADCThreshold_mV = ADCThreshold * ADCNoiseConversionGain;
0071
0072
0073
0074
0075 PHNodeIterator iter(topNode);
0076
0077
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
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
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
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;
0156
0157
0158
0159
0160
0161
0162
0163
0164
0165
0166
0167
0168
0169
0170
0171
0172
0173
0174
0175
0176
0177
0178
0179
0180
0181
0182
0183
0184
0185
0186
0187
0188
0189
0190
0191
0192
0193
0194
0195
0196
0197
0198
0199
0200
0201
0202
0203
0204
0205
0206
0207
0208
0209
0210
0211
0212
0213
0214
0215
0216
0217
0218
0219
0220
0221
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
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
0249
0250
0251
0252 for(unsigned int layer = TpcMinLayer; layer < TpcMinLayer+TpcNLayers; ++layer)
0253 {
0254
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
0272 phi_sorted_hits.clear();
0273 for (int iphi = 0; iphi < nphibins; iphi++)
0274 {
0275 phi_sorted_hits.emplace_back();
0276 }
0277
0278
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
0286
0287 TrkrDefs::hitsetkey hitsetkey = hitset_iter->first;
0288 unsigned int this_side = TpcDefs::getSide(hitsetkey);
0289
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
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
0308 unsigned int phibin = TpcDefs::getPad(hit_iter->first);
0309 phi_sorted_hits[phibin].push_back(hit_iter);
0310 }
0311
0312 }
0313
0314
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
0321 int ntbins = layergeom->get_zbins();
0322 is_populated.clear();
0323 is_populated.assign(ntbins, 2);
0324
0325
0326 t_sorted_hits.clear();
0327 for (int it = 0; it < ntbins; it++)
0328 {
0329 t_sorted_hits.emplace_back();
0330 }
0331
0332
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;
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
0357 adc_input.assign(ntbins, 0.0);
0358 adc_hitid.assign(ntbins, 0);
0359
0360
0361
0362
0363
0364
0365 for (int it = 0; it < ntbins; it++)
0366 {
0367 if (is_populated[it] == 1)
0368 {
0369
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
0389 float noise = add_noise_to_bin(0.0);
0390 adc_input[it]= noise;
0391 adc_hitid[it] = 0;
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
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
0413 int binpointer = 0;
0414
0415
0416
0417
0418
0419 for (int it = 0; it < ntbins; it++)
0420 {
0421 if (it < binpointer) { continue;
0422 }
0423
0424
0425 if( (is_populated[it] == 2) && skip_noise)
0426 {
0427 binpointer++;
0428 continue;
0429 }
0430
0431
0432 if (adc_input[it] > ADCThreshold_mV)
0433 {
0434
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)
0449 {
0450 float input = 0;
0451 if( (is_populated[it+itup] == 2) && skip_noise)
0452 {
0453 input = add_noise_to_bin(0.0);
0454 }
0455 else
0456 {
0457 input = adc_input[it + itup];
0458 }
0459
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
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
0489 hit = t_sorted_hits[it+itup][0]->second;
0490 }
0491 else
0492 {
0493
0494
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 }
0519 binpointer++;
0520 }
0521
0522 }
0523 else
0524 {
0525
0526
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
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
0542 binpointer++;
0543 }
0544 }
0545 }
0546 }
0547 }
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
0557
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
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
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
0595 delete_hitkey_list.emplace_back(hitsetkey, hitkey);
0596 }
0597 }
0598 }
0599
0600
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
0614
0615 }
0616
0617
0618 if (Verbosity() > 5) {
0619 std::cout << "From PHG4TpcDigitizer: hitsetcontainer dump at end after cleaning:" << std::endl;
0620 }
0621
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
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
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
0660
0661 return;
0662 }
0663
0664 float PHG4TpcDigitizer::add_noise_to_bin(float signal)
0665 {
0666
0667 float adc_input_voltage = signal * ADCSignalConversionGain;
0668 float noise_voltage = ( Pedestal + added_noise() ) * ADCNoiseConversionGain;
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 }