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)
0038 , TpcEnc(670)
0039 , Pedestal(50000)
0040 , ChargeToPeakVolts(20)
0041 , ADCSignalConversionGain(std::numeric_limits<float>::quiet_NaN())
0042 , ADCNoiseConversionGain(std::numeric_limits<float>::quiet_NaN())
0043 , RandomGenerator(gsl_rng_alloc(gsl_rng_mt19937))
0044 {
0045 unsigned int seed = PHRandomSeed();
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
0066
0067
0068 ADCSignalConversionGain = ChargeToPeakVolts * 1.60e-04 * 2.4;
0069
0070 ADCNoiseConversionGain = ChargeToPeakVolts * 1.60e-04;
0071
0072 ADCThreshold_mV = ADCThreshold * ADCNoiseConversionGain;
0073
0074
0075
0076
0077 PHNodeIterator iter(topNode);
0078
0079
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
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
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
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;
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
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
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
0250
0251
0252
0253 for (unsigned int layer = TpcMinLayer; layer < TpcMinLayer + TpcNLayers; ++layer)
0254 {
0255
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
0276 phi_sorted_hits.clear();
0277 for (int iphi = 0; iphi < nphibins; iphi++)
0278 {
0279 phi_sorted_hits.emplace_back();
0280 }
0281
0282
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
0289
0290 TrkrDefs::hitsetkey hitsetkey = hitset_iter->first;
0291 unsigned int this_side = TpcDefs::getSide(hitsetkey);
0292
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
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
0315 unsigned int phibin = TpcDefs::getPad(hit_iter->first);
0316 phi_sorted_hits[phibin].push_back(hit_iter);
0317 }
0318
0319 }
0320
0321
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
0330 int ntbins = layergeom->get_zbins();
0331 is_populated.clear();
0332 is_populated.assign(ntbins, 2);
0333
0334
0335 t_sorted_hits.clear();
0336 for (int it = 0; it < ntbins; it++)
0337 {
0338 t_sorted_hits.emplace_back();
0339 }
0340
0341
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;
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
0367 adc_input.assign(ntbins, 0.0);
0368 adc_hitid.assign(ntbins, 0);
0369
0370
0371
0372
0373
0374
0375 for (int it = 0; it < ntbins; it++)
0376 {
0377 if (is_populated[it] == 1)
0378 {
0379
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
0401 float noise = add_noise_to_bin(0.0);
0402 adc_input[it] = noise;
0403 adc_hitid[it] = 0;
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
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
0427 int binpointer = 0;
0428
0429
0430
0431
0432
0433 for (int it = 0; it < ntbins; it++)
0434 {
0435 if (it < binpointer)
0436 {
0437 continue;
0438 }
0439
0440
0441 if ((is_populated[it] == 2) && skip_noise)
0442 {
0443 binpointer++;
0444 continue;
0445 }
0446
0447
0448 if (adc_input[it] > ADCThreshold_mV)
0449 {
0450
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)
0467 {
0468 float input = 0;
0469 if ((is_populated[it + itup] == 2) && skip_noise)
0470 {
0471 input = add_noise_to_bin(0.0);
0472 }
0473 else
0474 {
0475 input = adc_input[it + itup];
0476 }
0477
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
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
0510 hit = t_sorted_hits[it + itup][0]->second;
0511 }
0512 else
0513 {
0514
0515
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 }
0541 binpointer++;
0542 }
0543
0544 }
0545 else
0546 {
0547
0548
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
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
0564 binpointer++;
0565 }
0566 }
0567 }
0568 }
0569 }
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
0579
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
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
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
0619 delete_hitkey_list.emplace_back(hitsetkey, hitkey);
0620 }
0621 }
0622 }
0623
0624
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
0640
0641 }
0642
0643
0644 if (Verbosity() > 5)
0645 {
0646 std::cout << "From PHG4TpcDigitizer: hitsetcontainer dump at end after cleaning:" << std::endl;
0647 }
0648
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
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
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
0691
0692 return;
0693 }
0694
0695 float PHG4TpcDigitizer::add_noise_to_bin(float signal)
0696 {
0697
0698 float adc_input_voltage = signal * ADCSignalConversionGain;
0699 float noise_voltage = (Pedestal + added_noise()) * ADCNoiseConversionGain;
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 }