File indexing completed on 2026-08-31 08:22:29
0001 #include "PHG4GeantinoIonization.h"
0002
0003 #include <g4main/PHG4Hit.h>
0004 #include <g4main/PHG4HitContainer.h>
0005 #include <g4main/PHG4Particle.h>
0006 #include <g4main/PHG4TruthInfoContainer.h>
0007
0008 #include <fun4all/Fun4AllReturnCodes.h>
0009
0010 #include <phparameter/PHParameters.h>
0011 #include <phparameter/PHParametersContainer.h>
0012
0013 #include <pdbcalbase/PdbParameterMapContainer.h>
0014
0015 #include <phool/PHCompositeNode.h>
0016 #include <phool/PHDataNode.h>
0017 #include <phool/PHNodeIterator.h>
0018 #include <phool/getClass.h>
0019 #include <phool/phool.h>
0020
0021 #include <cmath>
0022 #include <cstddef>
0023 #include <iostream>
0024
0025 namespace
0026 {
0027
0028
0029 constexpr double neonMipDedx = 1.56;
0030 constexpr double argonMipDedx = 2.44;
0031 constexpr double cf4MipDedx = 7.00;
0032 constexpr double nitrogenMipDedx = 2.127;
0033 constexpr double isobutaneMipDedx = 5.93;
0034
0035
0036
0037
0038 double mvtxMipDedx = 0.00384;
0039 double inttMipDedx = 0.00387;
0040
0041
0042
0043 constexpr double tpotMipDedx =
0044 1e-6 * (0.9 * argonMipDedx + 0.1 * isobutaneMipDedx);
0045 }
0046
0047 PHG4GeantinoIonization::PHG4GeantinoIonization(const std::string& name)
0048 : SubsysReco(name)
0049 , m_detectorConfigs{{
0050 {DetectorId::mvtx, "MVTX", "G4HIT_MVTX", true},
0051 {DetectorId::intt, "INTT", "G4HIT_INTT", true},
0052 {DetectorId::tpc, "TPC", "G4HIT_TPC", true},
0053 {DetectorId::tpot, "MICROMEGAS", "G4HIT_MICROMEGAS", true}}}
0054 {
0055 }
0056
0057 int PHG4GeantinoIonization::InitRun(PHCompositeNode* topNode)
0058 {
0059 if (!m_detectorConfigs[2].enabled)
0060 {
0061 return Fun4AllReturnCodes::EVENT_OK;
0062 }
0063
0064
0065
0066
0067 auto* tpcParamsContainer =
0068 findNode::getClass<PHParametersContainer>(topNode, "G4GEO_TPC");
0069 if (!tpcParamsContainer)
0070 {
0071
0072
0073
0074 auto* tpcPdbParams =
0075 findNode::getClass<PdbParameterMapContainer>(topNode, "G4GEOPARAM_TPC");
0076 if (!tpcPdbParams)
0077 {
0078 std::cout << PHWHERE
0079 << " Missing both G4GEO_TPC and G4GEOPARAM_TPC"
0080 << std::endl;
0081 return Fun4AllReturnCodes::ABORTRUN;
0082 }
0083
0084 PHNodeIterator topIter(topNode);
0085 auto* parNode = dynamic_cast<PHCompositeNode*>(
0086 topIter.findFirst("PHCompositeNode", "PAR"));
0087 if (!parNode)
0088 {
0089 std::cout << PHWHERE << " Missing PAR node" << std::endl;
0090 return Fun4AllReturnCodes::ABORTRUN;
0091 }
0092
0093 PHNodeIterator parIter(parNode);
0094 auto* parTpcNode = dynamic_cast<PHCompositeNode*>(
0095 parIter.findFirst("PHCompositeNode", "TPC"));
0096 if (!parTpcNode)
0097 {
0098 parTpcNode = new PHCompositeNode("TPC");
0099 parNode->addNode(parTpcNode);
0100 }
0101
0102 tpcParamsContainer = new PHParametersContainer("TPC");
0103 tpcParamsContainer->CreateAndFillFrom(tpcPdbParams, "TPC");
0104 parTpcNode->addNode(
0105 new PHDataNode<PHParametersContainer>(
0106 tpcParamsContainer, "G4GEO_TPC"));
0107 }
0108
0109 const PHParameters* tpcParams = tpcParamsContainer->GetParameters(0);
0110 if (!tpcParams)
0111 {
0112 std::cout << PHWHERE << " Missing TPC geometry parameters" << std::endl;
0113 return Fun4AllReturnCodes::ABORTRUN;
0114 }
0115
0116 const double neonFraction = tpcParams->get_double_param("Ne_frac");
0117 const double argonFraction = tpcParams->get_double_param("Ar_frac");
0118 const double cf4Fraction = tpcParams->get_double_param("CF4_frac");
0119 const double nitrogenFraction = tpcParams->get_double_param("N2_frac");
0120 const double isobutaneFraction = tpcParams->get_double_param("isobutane_frac");
0121
0122 m_tpcMipDedx =
0123 1e-6 * (neonFraction * neonMipDedx +
0124 argonFraction * argonMipDedx +
0125 cf4Fraction * cf4MipDedx +
0126 nitrogenFraction * nitrogenMipDedx +
0127 isobutaneFraction * isobutaneMipDedx);
0128
0129 if (Verbosity() > 0)
0130 {
0131 std::cout << Name()
0132 << " TPC gas fractions (Ne/Ar/CF4/N2/isobutane): "
0133 << neonFraction << "/" << argonFraction << "/"
0134 << cf4Fraction << "/" << nitrogenFraction << "/"
0135 << isobutaneFraction
0136 << ", MIP dE/dx: " << m_tpcMipDedx << " GeV/cm"
0137 << std::endl;
0138 }
0139
0140 return Fun4AllReturnCodes::EVENT_OK;
0141 }
0142
0143 void PHG4GeantinoIonization::set_mvtx_mip_dedx(const double value)
0144 {
0145 mvtxMipDedx = value;
0146 }
0147
0148 void PHG4GeantinoIonization::set_intt_mip_dedx(const double value)
0149 {
0150 inttMipDedx = value;
0151 }
0152
0153 double PHG4GeantinoIonization::mip_dedx(const DetectorId detector) const
0154 {
0155 switch (detector)
0156 {
0157 case DetectorId::mvtx:
0158 return mvtxMipDedx;
0159 case DetectorId::intt:
0160 return inttMipDedx;
0161 case DetectorId::tpc:
0162 return m_tpcMipDedx;
0163 case DetectorId::tpot:
0164 return tpotMipDedx;
0165 }
0166
0167 return 0;
0168 }
0169
0170 int PHG4GeantinoIonization::process_event(PHCompositeNode* topNode)
0171 {
0172 const auto* truthInfo = findNode::getClass<PHG4TruthInfoContainer>(topNode, "G4TruthInfo");
0173 if (!truthInfo)
0174 {
0175 std::cout << PHWHERE << " Missing G4TruthInfo" << std::endl;
0176 return Fun4AllReturnCodes::ABORTEVENT;
0177 }
0178
0179 for (const auto& config : m_detectorConfigs)
0180 {
0181 if (!config.enabled)
0182 {
0183 continue;
0184 }
0185
0186 auto* hits = findNode::getClass<PHG4HitContainer>(topNode, config.hitNodeName);
0187 if (!hits)
0188 {
0189 if (Verbosity() > 1)
0190 {
0191 std::cout << PHWHERE << " Missing optional node "
0192 << config.hitNodeName << std::endl;
0193 }
0194 continue;
0195 }
0196
0197 process_detector(hits, truthInfo, config);
0198 }
0199
0200 return Fun4AllReturnCodes::EVENT_OK;
0201 }
0202
0203 void PHG4GeantinoIonization::process_detector(
0204 PHG4HitContainer* hits,
0205 const PHG4TruthInfoContainer* truthInfo,
0206 const DetectorConfig& config) const
0207 {
0208 std::size_t inspected = 0;
0209 std::size_t modified = 0;
0210 std::size_t missingParticle = 0;
0211 std::size_t invalidPath = 0;
0212
0213 const auto hitRange = hits->getHits();
0214 for (auto hitIter = hitRange.first; hitIter != hitRange.second; ++hitIter)
0215 {
0216 auto* hit = hitIter->second;
0217 if (!hit)
0218 {
0219 continue;
0220 }
0221
0222 ++inspected;
0223
0224 const auto* particle = truthInfo->GetParticle(hit->get_trkid());
0225 if (!particle)
0226 {
0227 ++missingParticle;
0228 continue;
0229 }
0230
0231 if (particle->get_name() != m_particleName)
0232 {
0233 continue;
0234 }
0235
0236
0237
0238
0239 const double edep = hit->get_edep();
0240 const double eion = hit->get_eion();
0241 if (std::isfinite(edep) && edep >= 0 &&
0242 std::isfinite(eion) && eion >= 0)
0243 {
0244 continue;
0245 }
0246
0247 const double dx = hit->get_x(1) - hit->get_x(0);
0248 const double dy = hit->get_y(1) - hit->get_y(0);
0249 const double dz = hit->get_z(1) - hit->get_z(0);
0250 const double pathLength = std::sqrt(dx * dx + dy * dy + dz * dz);
0251
0252 const double mipDedx = mip_dedx(config.detector);
0253 if (!std::isfinite(pathLength) || pathLength <= 0 ||
0254 !std::isfinite(mipDedx) || mipDedx <= 0)
0255 {
0256 ++invalidPath;
0257 continue;
0258 }
0259
0260 const double syntheticEnergyDeposit = mipDedx * pathLength;
0261 const double syntheticIonization = syntheticEnergyDeposit;
0262 hit->set_edep(syntheticEnergyDeposit);
0263 hit->set_eion(syntheticIonization);
0264 ++modified;
0265
0266 if (Verbosity() > 2)
0267 {
0268 std::cout << Name() << " " << config.name
0269 << " hit " << hitIter->first
0270 << " track " << hit->get_trkid()
0271 << " path length " << pathLength << " cm"
0272 << " synthetic edep " << syntheticEnergyDeposit << " GeV"
0273 << ", eion " << syntheticIonization << " GeV"
0274 << std::endl;
0275 }
0276 }
0277
0278 if (Verbosity() > 0)
0279 {
0280 std::cout << Name() << " " << config.name
0281 << ": inspected " << inspected
0282 << ", modified " << modified
0283 << ", missing particle " << missingParticle
0284 << ", invalid path " << invalidPath
0285 << std::endl;
0286 }
0287 }