Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 09:22:01

0001 #include "PHG4TruthEventAction.h"
0002 
0003 #include "PHG4Hit.h"
0004 #include "PHG4HitContainer.h"
0005 #include "PHG4HitDefs.h"   // for keytype
0006 #include "PHG4Particle.h"  // for PHG4Particle
0007 #include "PHG4Shower.h"
0008 #include "PHG4TruthInfoContainer.h"
0009 #include "PHG4UserPrimaryParticleInformation.h"
0010 
0011 #include <phool/PHCompositeNode.h>
0012 #include <phool/PHIODataNode.h>  // for PHIODataNode
0013 #include <phool/PHNode.h>
0014 #include <phool/PHNodeIterator.h>  // for PHNodeIterator
0015 #include <phool/PHPointerListIterator.h>
0016 #include <phool/getClass.h>
0017 #include <phool/phool.h>  // for PHWHERE
0018 
0019 #include <Geant4/G4Event.hh>
0020 #include <Geant4/G4PrimaryParticle.hh>                  // for G4PrimaryPart...
0021 #include <Geant4/G4PrimaryVertex.hh>                    // for G4PrimaryVertex
0022 #include <Geant4/G4VUserPrimaryParticleInformation.hh>  // for G4VUserPrimar...
0023 
0024 #include <Eigen/Dense>
0025 
0026 #include <cmath>     // for isnan
0027 #include <cstdlib>   // for abs
0028 #include <iostream>  // for operator<<, endl
0029 #include <iterator>  // for reverse_iterator
0030 #include <map>
0031 #include <string>   // for operator==
0032 #include <utility>  // for swap, pair
0033 #include <vector>   // for vector, vecto...
0034 
0035 class PHG4VtxPoint;
0036 
0037 //___________________________________________________
0038 void PHG4TruthEventAction::BeginOfEventAction(const G4Event* /*evt*/)
0039 {
0040   // if we do not find the node we need to make it.
0041   if (!m_TruthInfoContainer)
0042   {
0043     std::cout << "PHG4TruthEventAction::EndOfEventAction - unable to find G4TruthInfo node" << std::endl;
0044     return;
0045   }
0046 
0047   const PHG4TruthInfoContainer::Map& map = m_TruthInfoContainer->GetMap();
0048   if (!map.empty())
0049   {
0050     m_LowerKeyPrevExist = map.begin()->first;
0051     m_UpperKeyPrevExist = map.rbegin()->first;
0052   }
0053 }
0054 
0055 //___________________________________________________
0056 void PHG4TruthEventAction::EndOfEventAction(const G4Event* evt)
0057 {
0058   // if we do not find the node we need to make it.
0059   if (!m_TruthInfoContainer)
0060   {
0061     std::cout << "PHG4TruthEventAction::EndOfEventAction - unable to find G4TruthInfo node" << std::endl;
0062     return;
0063   }
0064   // First deal with the showers - they do need the info which
0065   // is removed from the maps in the subsequent cleanup to reduce the
0066   // output file size
0067   PruneShowers();
0068   ProcessShowers();
0069   // construct a list of track ids to preserve in the the output that includes any
0070   // track designated in the m_WriteSet during processing or its ancestry chain
0071 
0072   std::set<int> savelist;
0073   std::set<int> savevtxlist;
0074 
0075   for (int mytrkid : m_WriteSet)
0076   {
0077     std::vector<int> wrttracks;
0078     std::vector<int> wrtvtx;
0079 
0080     // usertrackid
0081     PHG4Particle* particle = m_TruthInfoContainer->GetParticle(mytrkid);
0082 
0083     // if track is already in save list, nothing needs to be done
0084     if (savelist.contains(mytrkid))
0085     {
0086       continue;
0087     }
0088 
0089     wrttracks.push_back(mytrkid);
0090     wrtvtx.push_back(particle->get_vtx_id());
0091 
0092     // now crawl up the truth info and add parents until we hit
0093     // a track which is already being saved
0094     int parentid = particle->get_parent_id();
0095     while (!savelist.contains(parentid) && parentid != 0)
0096     {
0097       particle = m_TruthInfoContainer->GetParticle(parentid);
0098       wrttracks.push_back(parentid);
0099       wrtvtx.push_back(particle->get_vtx_id());
0100       parentid = particle->get_parent_id();
0101     }
0102 
0103     // now fill the new tracks into the save list
0104     // while emptying the write lists
0105     while (wrttracks.begin() != wrttracks.end())
0106     {
0107       savelist.insert(wrttracks.back());
0108       wrttracks.pop_back();
0109     }
0110 
0111     while (wrtvtx.begin() != wrtvtx.end())
0112     {
0113       savevtxlist.insert(wrtvtx.back());
0114       wrtvtx.pop_back();
0115     }
0116   }
0117 
0118   // the save lists are filled now, except primary track which never
0119   // made it into any active volume and their vertex
0120 
0121   // loop over particles in truth list and remove them if they are not
0122   // in the save list and are not primary particles (parent_id == 0)
0123 
0124   int removed[4] = {0};
0125   PHG4TruthInfoContainer::Range truth_range = m_TruthInfoContainer->GetParticleRange();
0126   PHG4TruthInfoContainer::Iterator truthiter = truth_range.first;
0127   while (truthiter != truth_range.second)
0128   {
0129     removed[0]++;
0130     int trackid = truthiter->first;
0131     if (!savelist.contains(trackid))
0132     {
0133       // track not in save list
0134 
0135       // check if particle below offset
0136       // primary particles had parentid = 0
0137       // for embedding: particles from initial sim do not have their keep flag set, we want to keep particles with trkid <= trackidoffset
0138       // tracks from a previous geant pass will not be recorded as leaving
0139       // hit in the sim, so we exclude this range from the removal
0140       // for regular sims, that range is zero to zero
0141       if (((trackid < m_LowerKeyPrevExist) || (trackid > m_UpperKeyPrevExist)) && ((truthiter->second)->get_parent_id() != 0))
0142       {
0143         m_TruthInfoContainer->delete_particle(truthiter++);
0144         removed[1]++;
0145       }
0146       else
0147       {
0148         // save vertex id for primary particle which leaves no hit
0149         // in active area
0150         savevtxlist.insert((truthiter->second)->get_vtx_id());
0151         ++truthiter;
0152       }
0153     }
0154     else
0155     {
0156       // track was in save list, move on
0157       ++truthiter;
0158     }
0159   }
0160 
0161   PHG4TruthInfoContainer::VtxRange vtxrange = m_TruthInfoContainer->GetVtxRange();
0162   PHG4TruthInfoContainer::VtxIterator vtxiter = vtxrange.first;
0163   while (vtxiter != vtxrange.second)
0164   {
0165     removed[2]++;
0166     if (!savevtxlist.contains(vtxiter->first))
0167     {
0168       m_TruthInfoContainer->delete_vtx(vtxiter++);
0169       removed[3]++;
0170     }
0171     else
0172     {
0173       ++vtxiter;
0174     }
0175   }
0176 
0177   // loop over all input particles and fish out the ones which have the embed flag set
0178   // and store their geant track ids in truthinfo container
0179   G4PrimaryVertex* pvtx = evt->GetPrimaryVertex();
0180   while (pvtx)
0181   {
0182     G4PrimaryParticle* part = pvtx->GetPrimary();
0183     while (part)
0184     {
0185       PHG4UserPrimaryParticleInformation* userdata = dynamic_cast<PHG4UserPrimaryParticleInformation*>(part->GetUserInformation());
0186       if (userdata)
0187       {
0188         if (userdata->get_embed())
0189         {
0190           m_TruthInfoContainer->AddEmbededTrkId(userdata->get_user_track_id(), userdata->get_embed());
0191           m_TruthInfoContainer->AddEmbededVtxId(userdata->get_user_vtx_id(), userdata->get_embed());
0192         }
0193       }
0194       part = part->GetNext();
0195     }
0196     pvtx = pvtx->GetNext();
0197   }
0198 
0199   return;
0200 }
0201 
0202 //___________________________________________________
0203 void PHG4TruthEventAction::AddTrackidToWritelist(const int trackid)
0204 {
0205   m_WriteSet.insert(trackid);
0206 }
0207 
0208 //___________________________________________________
0209 void PHG4TruthEventAction::SetInterfacePointers(PHCompositeNode* topNode)
0210 {
0211   // now look for the map and grab a pointer to it.
0212   m_TruthInfoContainer = findNode::getClass<PHG4TruthInfoContainer>(topNode, "G4TruthInfo");
0213 
0214   // if we do not find the node we need to make it.
0215   if (!m_TruthInfoContainer)
0216   {
0217     std::cout << "PHG4TruthEventAction::SetInterfacePointers - unable to find G4TruthInfo" << std::endl;
0218   }
0219 
0220   SearchNode(topNode);
0221 }
0222 
0223 // NOLINTNEXTLINE(misc-no-recursion)
0224 void PHG4TruthEventAction::SearchNode(PHCompositeNode* top)
0225 {
0226   // fill a lookup map between the g4hit container ids and the containers themselves
0227   // without knowing what the container names are in advance, only that they
0228   // begin G4HIT_*
0229 
0230   PHNodeIterator nodeiter(top);
0231   PHPointerListIterator<PHNode> iter(nodeiter.ls());
0232   PHNode* thisNode;
0233   while ((thisNode = iter()))
0234   {
0235     if (thisNode->getType() == "PHCompositeNode")
0236     {
0237       SearchNode(static_cast<PHCompositeNode*>(thisNode));
0238     }
0239     else if (thisNode->getType() == "PHIODataNode")
0240     {
0241       if (thisNode->getName().find("G4HIT_") == 0)
0242       {
0243         PHIODataNode<PHG4HitContainer>* DNode = static_cast<PHIODataNode<PHG4HitContainer>*>(thisNode);
0244         if (DNode)
0245         {
0246           PHG4HitContainer* object = dynamic_cast<PHG4HitContainer*>(DNode->getData());
0247           if (object)
0248           {
0249             m_HitContainerMap[object->GetID()] = object;
0250           }
0251         }
0252       }
0253     }
0254   }
0255 }
0256 
0257 int PHG4TruthEventAction::ResetEvent(PHCompositeNode* /*unused*/)
0258 {
0259   m_WriteSet.clear();
0260   return 0;
0261 }
0262 
0263 void PHG4TruthEventAction::PruneShowers()
0264 {
0265   PHG4TruthInfoContainer::ShowerRange range = m_TruthInfoContainer->GetShowerRange();
0266   for (PHG4TruthInfoContainer::ShowerIterator iter = range.first;
0267        iter != range.second;
0268        ++iter)
0269   {
0270     PHG4Shower* shower = iter->second;
0271 
0272     std::set<int> remove_ids;
0273     for (PHG4Shower::ParticleIdIter jter = shower->begin_g4particle_id();
0274          jter != shower->end_g4particle_id();
0275          ++jter)
0276     {
0277       int g4particle_id = *jter;
0278       PHG4Particle* particle = m_TruthInfoContainer->GetParticle(g4particle_id);
0279       if (!particle)
0280       {
0281         remove_ids.insert(g4particle_id);
0282         continue;
0283       }
0284     }
0285 
0286     for (int remove_id : remove_ids)
0287     {
0288       shower->remove_g4particle_id(remove_id);
0289     }
0290 
0291     std::set<int> remove_more_ids;
0292     for (std::map<int, std::set<PHG4HitDefs::keytype> >::iterator jter = shower->begin_g4hit_id();
0293          jter != shower->end_g4hit_id();
0294          ++jter)
0295     {
0296       int g4hitmap_id = jter->first;
0297       std::map<int, PHG4HitContainer*>::iterator mapiter = m_HitContainerMap.find(g4hitmap_id);
0298       if (mapiter == m_HitContainerMap.end())
0299       {
0300         continue;
0301       }
0302 
0303       // get the g4hits from this particle in this volume
0304       for (std::set<PHG4HitDefs::keytype>::iterator kter = jter->second.begin();
0305            kter != jter->second.end();)
0306       {
0307         PHG4HitDefs::keytype g4hit_id = *kter;
0308 
0309         PHG4Hit* g4hit = mapiter->second->findHit(g4hit_id);
0310         if (!g4hit)
0311         {
0312           // some zero edep g4hits have been removed already
0313           jter->second.erase(kter++);
0314           continue;
0315         }
0316 
0317         ++kter;
0318       }
0319 
0320       if (jter->second.empty())
0321       {
0322         remove_more_ids.insert(g4hitmap_id);
0323       }
0324     }
0325 
0326     for (int remove_more_id : remove_more_ids)
0327     {
0328       shower->remove_g4hit_volume(remove_more_id);
0329     }
0330   }
0331 
0332   range = m_TruthInfoContainer->GetShowerRange();
0333   for (PHG4TruthInfoContainer::ShowerIterator iter = range.first;
0334        iter != range.second;)
0335   {
0336     PHG4Shower* shower = iter->second;
0337 
0338     if (shower->empty_g4particle_id() && shower->empty_g4hit_id())
0339     {
0340       if (shower->get_edep() == 0)  // check whether this shower has already been processed in the previous simulation cycles
0341       {
0342         m_TruthInfoContainer->delete_shower(iter++);
0343         continue;
0344       }
0345     }
0346 
0347     ++iter;
0348   }
0349 }
0350 
0351 void PHG4TruthEventAction::ProcessShowers()
0352 {
0353   PHG4TruthInfoContainer::ShowerRange range = m_TruthInfoContainer->GetShowerRange();
0354   for (PHG4TruthInfoContainer::ShowerIterator shwiter = range.first;
0355        shwiter != range.second;
0356        ++shwiter)
0357   {
0358     PHG4Shower* shower = shwiter->second;
0359 
0360     // Data structures to hold weighted pca
0361     std::vector<std::vector<float> > points;
0362     std::vector<float> weights;
0363     float sumw = 0.0;
0364     float sumw2 = 0.0;
0365 
0366     for (std::map<int, std::set<PHG4HitDefs::keytype> >::iterator iter = shower->begin_g4hit_id();
0367          iter != shower->end_g4hit_id();
0368          ++iter)
0369     {
0370       int g4hitmap_id = iter->first;
0371       std::map<int, PHG4HitContainer*>::iterator mapiter = m_HitContainerMap.find(g4hitmap_id);
0372       if (mapiter == m_HitContainerMap.end())
0373       {
0374         continue;
0375       }
0376 
0377       PHG4HitContainer* hits = mapiter->second;
0378 
0379       unsigned int nhits = 0;
0380       float edep = 0.0;
0381       float eion = 0.0;
0382       float light_yield = 0.0;
0383       float edep_e = 0.0;
0384       float edep_h = 0.0;
0385 
0386       // get the g4hits from this particle in this volume
0387       for (unsigned long long g4hit_id : iter->second)
0388       {
0389         PHG4Hit* g4hit = hits->findHit(g4hit_id);
0390         if (!g4hit)
0391         {
0392           std::cout << PHWHERE << " missing g4hit" << std::endl;
0393           continue;
0394         }
0395 
0396         PHG4Particle* particle = m_TruthInfoContainer->GetParticle(g4hit->get_trkid());
0397         if (!particle)
0398         {
0399           std::cout << PHWHERE << " missing g4particle for track "
0400                     << g4hit->get_trkid() << std::endl;
0401           continue;
0402         }
0403 
0404         PHG4VtxPoint* vtx = m_TruthInfoContainer->GetVtx(particle->get_vtx_id());
0405         if (!vtx)
0406         {
0407           std::cout << PHWHERE << " missing g4vertex" << std::endl;
0408           continue;
0409         }
0410 
0411         // shower location and shape info
0412 
0413         if (!std::isnan(g4hit->get_x(0)) &&
0414             !std::isnan(g4hit->get_y(0)) &&
0415             !std::isnan(g4hit->get_z(0)))
0416         {
0417           std::vector<float> entry(3);
0418           entry[0] = g4hit->get_x(0);
0419           entry[1] = g4hit->get_y(0);
0420           entry[2] = g4hit->get_z(0);
0421 
0422           points.push_back(entry);
0423           float w = g4hit->get_edep();
0424           weights.push_back(w);
0425           sumw += w;
0426           sumw2 += w * w;
0427         }
0428 
0429         if (!std::isnan(g4hit->get_x(1)) &&
0430             !std::isnan(g4hit->get_y(1)) &&
0431             !std::isnan(g4hit->get_z(1)))
0432         {
0433           std::vector<float> entry(3);
0434           entry[0] = g4hit->get_x(1);
0435           entry[1] = g4hit->get_y(1);
0436           entry[2] = g4hit->get_z(1);
0437 
0438           points.push_back(entry);
0439           float w = g4hit->get_edep();
0440           weights.push_back(w);
0441           sumw += w;
0442           sumw2 += w * w;
0443         }
0444 
0445         // e/h ratio
0446 
0447         if (!std::isnan(g4hit->get_edep()))
0448         {
0449           if (abs(particle->get_pid()) == 11)
0450           {
0451             edep_e += g4hit->get_edep();
0452           }
0453           else
0454           {
0455             edep_h += g4hit->get_edep();
0456           }
0457         }
0458 
0459         // summary info
0460 
0461         ++nhits;
0462         if (!std::isnan(g4hit->get_edep()))
0463         {
0464           edep += g4hit->get_edep();
0465         }
0466         if (!std::isnan(g4hit->get_eion()))
0467         {
0468           eion += g4hit->get_eion();
0469         }
0470         if (!std::isnan(g4hit->get_light_yield()))
0471         {
0472           light_yield += g4hit->get_light_yield();
0473         }
0474       }  // g4hit loop
0475 
0476       // summary info
0477 
0478       if (nhits)
0479       {
0480         shower->set_nhits(g4hitmap_id, nhits);
0481       }
0482       if (edep != 0.0)
0483       {
0484         shower->set_edep(g4hitmap_id, edep);
0485       }
0486       if (eion != 0.0)
0487       {
0488         shower->set_eion(g4hitmap_id, eion);
0489       }
0490       if (light_yield != 0.0)
0491       {
0492         shower->set_light_yield(g4hitmap_id, light_yield);
0493       }
0494       if (edep_h != 0.0)
0495       {
0496         shower->set_eh_ratio(g4hitmap_id, edep_e / edep_h);
0497       }
0498     }  // volume loop
0499 
0500     // fill Eigen matrices to compute wPCA
0501     // resizing these non-destructively is expensive
0502     // so I fill vectors and then copy
0503     Eigen::Matrix<double, Eigen::Dynamic, 3> X(points.size(), 3);
0504     Eigen::Matrix<double, Eigen::Dynamic, 1> W(weights.size(), 1);
0505 
0506     for (unsigned int i = 0; i < points.size(); ++i)
0507     {
0508       for (unsigned int j = 0; j < 3; ++j)
0509       {
0510         X(i, j) = points[i][j];
0511       }
0512       W(i, 0) = weights[i];
0513     }
0514 
0515     // mean value of shower
0516     double prefactor = 1.0 / sumw;
0517     Eigen::Matrix<double, 1, 3> mean = prefactor * W.transpose() * X;
0518 
0519     // compute residual relative to the mean
0520     for (unsigned int i = 0; i < points.size(); ++i)
0521     {
0522       for (unsigned int j = 0; j < 3; ++j)
0523       {
0524         X(i, j) = points[i][j] - mean(0, j);
0525       }
0526     }
0527 
0528     // weighted covariance matrix
0529     prefactor = sumw / (sumw * sumw - sumw2);  // effectivelly 1/(N-1) when w_i = 1.0
0530     Eigen::Matrix<double, 3, 3> covar = prefactor * (X.transpose() * W.asDiagonal() * X);
0531 
0532     shower->set_x(mean(0, 0));
0533     shower->set_y(mean(0, 1));
0534     shower->set_z(mean(0, 2));
0535 
0536     for (unsigned int i = 0; i < 3; ++i)
0537     {
0538       for (unsigned int j = 0; j <= i; ++j)
0539       {
0540         shower->set_covar(i, j, covar(i, j));
0541       }
0542     }
0543 
0544     // shower->identify();
0545   }
0546 }