Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-06 08:19:13

0001 /*!
0002  * \file TrackEvaluation.cc
0003  * \author Hugo Pereira Da Costa <hugo.pereira-da-costa@cea.fr>
0004  */
0005 
0006 #include "TrackEvaluation.h"
0007 
0008 #include <g4detectors/PHG4CylinderGeomContainer.h>
0009 #include <g4detectors/PHG4TpcCylinderGeom.h>
0010 #include <g4detectors/PHG4TpcCylinderGeomContainer.h>
0011 
0012 #include <g4main/PHG4Hit.h>
0013 #include <g4main/PHG4HitContainer.h>
0014 #include <g4main/PHG4Hitv1.h>
0015 #include <g4main/PHG4Particle.h>
0016 #include <g4main/PHG4TruthInfoContainer.h>
0017 #include <g4main/PHG4VtxPoint.h>
0018 
0019 #include <micromegas/CylinderGeomMicromegas.h>
0020 #include <micromegas/MicromegasDefs.h>
0021 
0022 #include <trackbase/ActsGeometry.h>
0023 #include <trackbase/ClusterErrorPara.h>
0024 #include <trackbase/InttDefs.h>
0025 #include <trackbase/MvtxDefs.h>
0026 #include <trackbase/TpcDefs.h>
0027 #include <trackbase/TrkrCluster.h>
0028 #include <trackbase/TrkrClusterContainer.h>
0029 #include <trackbase/TrkrClusterHitAssoc.h>
0030 #include <trackbase/TrkrDefs.h>
0031 #include <trackbase/TrkrHit.h>
0032 #include <trackbase/TrkrHitSet.h>
0033 #include <trackbase/TrkrHitSetContainer.h>
0034 #include <trackbase/TrkrHitTruthAssoc.h>
0035 #include <trackbase_historic/SvtxTrack.h>
0036 #include <trackbase_historic/SvtxTrackMap.h>
0037 
0038 #include <fun4all/Fun4AllReturnCodes.h>
0039 
0040 #include <phool/PHCompositeNode.h>
0041 #include <phool/PHNodeIterator.h>
0042 #include <phool/getClass.h>
0043 
0044 #include <TVector3.h>
0045 
0046 #include <algorithm>
0047 #include <bitset>
0048 #include <cassert>
0049 #include <iostream>
0050 #include <numeric>
0051 
0052 //_____________________________________________________________________
0053 namespace
0054 {
0055 
0056   //! range adaptor to be able to use range-based for loop
0057   template <class T>
0058   class range_adaptor
0059   {
0060    public:
0061     explicit range_adaptor(const T& range)
0062       : m_range(range)
0063     {
0064     }
0065     inline const typename T::first_type& begin() { return m_range.first; }
0066     inline const typename T::second_type& end() { return m_range.second; }
0067 
0068    private:
0069     T m_range;
0070   };
0071 
0072   //! square
0073   template <class T>
0074   inline constexpr T square(const T& x)
0075   {
0076     return x * x;
0077   }
0078 
0079   //! radius
0080   template <class T>
0081   inline constexpr T get_r(T x, T y)
0082   {
0083     return std::sqrt(square(x) + square(y));
0084   }
0085 
0086   //! pt
0087   template <class T>
0088   T get_pt(T px, T py)
0089   {
0090     return std::sqrt(square(px) + square(py));
0091   }
0092 
0093   //! p
0094   template <class T>
0095   T get_p(T px, T py, T pz)
0096   {
0097     return std::sqrt(square(px) + square(py) + square(pz));
0098   }
0099 
0100   //! eta
0101   template <class T>
0102   T get_eta(T p, T pz)
0103   {
0104     return std::log((p + pz) / (p - pz)) / 2;
0105   }
0106 
0107   //! needed for weighted linear interpolation
0108   struct interpolation_data_t
0109   {
0110     using list = std::vector<interpolation_data_t>;
0111     double x() const { return position.x(); }
0112     double y() const { return position.y(); }
0113     double z() const { return position.z(); }
0114 
0115     double px() const { return momentum.x(); }
0116     double py() const { return momentum.y(); }
0117     double pz() const { return momentum.z(); }
0118 
0119     TVector3 position;
0120     TVector3 momentum;
0121     double weight = 1;
0122   };
0123 
0124   //! calculate the average of member function called on all members in collection
0125   template <double (interpolation_data_t::*accessor)() const>
0126   double average(const interpolation_data_t::list& hits)
0127   {
0128     // calculate all terms needed for the interpolation
0129     // need to use double everywhere here due to numerical divergences
0130     double sw = 0;
0131     double swx = 0;
0132 
0133     bool valid(false);
0134     for (const auto& hit : hits)
0135     {
0136       const double x = (hit.*accessor)();
0137       if (std::isnan(x))
0138       {
0139         continue;
0140       }
0141 
0142       const double w = hit.weight;
0143       if (w <= 0)
0144       {
0145         continue;
0146       }
0147 
0148       valid = true;
0149       sw += w;
0150       swx += w * x;
0151     }
0152 
0153     if (!valid)
0154     {
0155       return NAN;
0156     }
0157     return swx / sw;
0158   }
0159 
0160   //! get cluster keys from a given track
0161   std::vector<TrkrDefs::cluskey> get_cluster_keys(SvtxTrack* track)
0162   {
0163     std::vector<TrkrDefs::cluskey> out;
0164     for (const auto& seed : {track->get_silicon_seed(), track->get_tpc_seed()})
0165     {
0166       if (seed)
0167       {
0168         std::copy(seed->begin_cluster_keys(), seed->end_cluster_keys(), std::back_inserter(out));
0169       }
0170     }
0171 
0172     return out;
0173   }
0174 
0175   //! true if a track is a primary
0176   inline int is_primary(PHG4Particle* particle)
0177   {
0178     return particle->get_parent_id() == 0;
0179   }
0180 
0181   //! get mask from track clusters
0182   int64_t get_mask(SvtxTrack* track)
0183   {
0184     const auto cluster_keys = get_cluster_keys(track);
0185     return std::accumulate(cluster_keys.begin(), cluster_keys.end(), int64_t(0),
0186                            [](int64_t value, const TrkrDefs::cluskey& key)
0187                            {
0188                              return TrkrDefs::getLayer(key) < 64 ? value | (1LL << TrkrDefs::getLayer(key)) : value;
0189                            });
0190   }
0191 
0192   //! return number of clusters of a given type
0193   template <int type>
0194   int get_clusters(SvtxTrack* track)
0195   {
0196     const auto cluster_keys = get_cluster_keys(track);
0197     return std::count_if(cluster_keys.begin(), cluster_keys.end(),
0198                          [](const TrkrDefs::cluskey& key)
0199                          { return TrkrDefs::getTrkrId(key) == type; });
0200   }
0201 
0202   //! create track struct from struct from svx track
0203   TrackEvaluationContainerv1::TrackStruct create_track(SvtxTrack* track)
0204   {
0205     TrackEvaluationContainerv1::TrackStruct trackStruct;
0206 
0207     trackStruct.charge = track->get_charge();
0208     trackStruct.nclusters = track->size_cluster_keys();
0209     trackStruct.mask = get_mask(track);
0210     trackStruct.nclusters_mvtx = get_clusters<TrkrDefs::mvtxId>(track);
0211     trackStruct.nclusters_intt = get_clusters<TrkrDefs::inttId>(track);
0212     trackStruct.nclusters_tpc = get_clusters<TrkrDefs::tpcId>(track);
0213     trackStruct.nclusters_micromegas = get_clusters<TrkrDefs::micromegasId>(track);
0214 
0215     trackStruct.chisquare = track->get_chisq();
0216     trackStruct.ndf = track->get_ndf();
0217 
0218     trackStruct.x = track->get_x();
0219     trackStruct.y = track->get_y();
0220     trackStruct.z = track->get_z();
0221     trackStruct.r = get_r(trackStruct.x, trackStruct.y);
0222     trackStruct.phi = std::atan2(trackStruct.y, trackStruct.x);
0223 
0224     trackStruct.px = track->get_px();
0225     trackStruct.py = track->get_py();
0226     trackStruct.pz = track->get_pz();
0227     trackStruct.pt = get_pt(trackStruct.px, trackStruct.py);
0228     trackStruct.p = get_p(trackStruct.px, trackStruct.py, trackStruct.pz);
0229     trackStruct.eta = get_eta(trackStruct.p, trackStruct.pz);
0230 
0231     return trackStruct;
0232   }
0233 
0234   //! number of hits associated to cluster
0235   void add_cluster_size(TrackEvaluationContainerv1::ClusterStruct& cluster, TrkrCluster* trk_clus)
0236   {
0237     cluster.size = trk_clus->getSize();
0238     cluster.phi_size = trk_clus->getPhiSize();
0239     cluster.z_size = trk_clus->getZSize();
0240     cluster.ovlp = trk_clus->getOverlap();
0241     cluster.edge = trk_clus->getEdge();
0242     cluster.adc = trk_clus->getAdc();
0243     cluster.max_adc = trk_clus->getMaxAdc();
0244   }
0245 
0246   //! hit energy for a given cluster
0247   void add_cluster_energy(TrackEvaluationContainerv1::ClusterStruct& cluster, TrkrDefs::cluskey clus_key,
0248                           TrkrClusterHitAssoc* cluster_hit_map,
0249                           TrkrHitSetContainer* hitsetcontainer)
0250   {
0251     // check container
0252     if (!(cluster_hit_map && hitsetcontainer))
0253     {
0254       return;
0255     }
0256 
0257     // for now this is only filled for micromegas
0258     const auto detId = TrkrDefs::getTrkrId(clus_key);
0259     if (detId != TrkrDefs::micromegasId)
0260     {
0261       return;
0262     }
0263 
0264     const auto hitset_key = TrkrDefs::getHitSetKeyFromClusKey(clus_key);
0265     const auto hitset = hitsetcontainer->findHitSet(hitset_key);
0266     if (!hitset)
0267     {
0268       return;
0269     }
0270 
0271     const auto range = cluster_hit_map->getHits(clus_key);
0272     cluster.energy_max = 0;
0273     cluster.energy_sum = 0;
0274 
0275     for (const auto& pair : range_adaptor(range))
0276     {
0277       const auto hit = hitset->getHit(pair.second);
0278       if (hit)
0279       {
0280         const auto energy = hit->getEnergy();
0281         cluster.energy_sum += energy;
0282         if (energy > cluster.energy_max)
0283         {
0284           cluster.energy_max = energy;
0285         }
0286       }
0287     }
0288   }
0289 
0290   // ad}d truth information
0291   void add_truth_information(TrackEvaluationContainerv1::TrackStruct& track, PHG4Particle* particle, PHG4TruthInfoContainer* truthinfo)
0292   {
0293     if (particle)
0294     {
0295       PHG4VtxPoint* vtx = truthinfo->GetVtx(particle->get_vtx_id());
0296       track.is_primary = is_primary(particle);
0297       track.pid = particle->get_pid();
0298       track.gtrackID = particle->get_track_id();
0299       track.truth_t = vtx->get_t();
0300       track.truth_px = particle->get_px();
0301       track.truth_py = particle->get_py();
0302       track.truth_pz = particle->get_pz();
0303       track.truth_pt = get_pt(track.truth_px, track.truth_py);
0304       track.truth_p = get_p(track.truth_px, track.truth_py, track.truth_pz);
0305       track.truth_eta = get_eta(track.truth_p, track.truth_pz);
0306     }
0307   }
0308 
0309   // calculate intersection between line and circle
0310   double line_circle_intersection(const TVector3& p0, const TVector3& p1, double radius)
0311   {
0312     const double A = square(p1.x() - p0.x()) + square(p1.y() - p0.y());
0313     const double B = 2 * p0.x() * (p1.x() - p0.x()) + 2 * p0.y() * (p1.y() - p0.y());
0314     const double C = square(p0.x()) + square(p0.y()) - square(radius);
0315     const double delta = square(B) - 4 * A * C;
0316     if (delta < 0)
0317     {
0318       return -1;
0319     }
0320 
0321     // check first intersection
0322     const double tup = (-B + std::sqrt(delta)) / (2 * A);
0323     if (tup >= 0 && tup < 1)
0324     {
0325       return tup;
0326     }
0327 
0328     // check second intersection
0329     const double tdn = (-B - sqrt(delta)) / (2 * A);
0330     if (tdn >= 0 && tdn < 1)
0331     {
0332       return tdn;
0333     }
0334 
0335     // no valid extrapolation
0336     return -1;
0337   }
0338 
0339   // print to stream
0340   [[maybe_unused]] inline std::ostream& operator<<(std::ostream& out, const TrackEvaluationContainerv1::ClusterStruct& cluster)
0341   {
0342     out << "ClusterStruct" << std::endl;
0343     out << "  cluster: (" << cluster.x << "," << cluster.y << "," << cluster.z << ")" << std::endl;
0344     out << "  track:   (" << cluster.trk_x << "," << cluster.trk_y << "," << cluster.trk_z << ")" << std::endl;
0345     out << "  truth:   (" << cluster.truth_x << "," << cluster.truth_y << "," << cluster.truth_z << ")" << std::endl;
0346     return out;
0347   }
0348 
0349   [[maybe_unused]] inline std::ostream& operator<<(std::ostream& out, const TVector3& position)
0350   {
0351     out << "(" << position.x() << ", " << position.y() << ", " << position.z() << ")";
0352     return out;
0353   }
0354 
0355 }  // namespace
0356 
0357 //_____________________________________________________________________
0358 TrackEvaluation::TrackEvaluation(const std::string& name)
0359   : SubsysReco(name)
0360 {
0361 }
0362 
0363 //_____________________________________________________________________
0364 int TrackEvaluation::Init(PHCompositeNode* topNode)
0365 {
0366   // find DST node
0367   PHNodeIterator iter(topNode);
0368   auto dstNode = dynamic_cast<PHCompositeNode*>(iter.findFirst("PHCompositeNode", "DST"));
0369   if (!dstNode)
0370   {
0371     std::cout << "TrackEvaluation::Init - DST Node missing" << std::endl;
0372     return Fun4AllReturnCodes::ABORTEVENT;
0373   }
0374 
0375   // get EVAL node
0376   iter = PHNodeIterator(dstNode);
0377   auto evalNode = dynamic_cast<PHCompositeNode*>(iter.findFirst("PHCompositeNode", "EVAL"));
0378   if (!evalNode)
0379   {
0380     // create
0381     std::cout << "TrackEvaluation::Init - EVAL node missing - creating" << std::endl;
0382     evalNode = new PHCompositeNode("EVAL");
0383     dstNode->addNode(evalNode);
0384   }
0385 
0386   auto newNode = new PHIODataNode<PHObject>(new TrackEvaluationContainerv1, "TrackEvaluationContainer", "PHObject");
0387   evalNode->addNode(newNode);
0388 
0389   return Fun4AllReturnCodes::EVENT_OK;
0390 }
0391 
0392 //_____________________________________________________________________
0393 int TrackEvaluation::InitRun(PHCompositeNode* /*unused*/)
0394 {
0395   return Fun4AllReturnCodes::EVENT_OK;
0396 }
0397 
0398 //_____________________________________________________________________
0399 int TrackEvaluation::process_event(PHCompositeNode* topNode)
0400 {
0401   // load nodes
0402   auto res = load_nodes(topNode);
0403   if (res != Fun4AllReturnCodes::EVENT_OK)
0404   {
0405     return res;
0406   }
0407 
0408   // cleanup output container
0409   std::cout << "start..." << std::endl;
0410   if (m_container)
0411   {
0412     m_container->Reset();
0413   }
0414   std::cout << "event..." << std::endl;
0415   if (m_flags & EvalEvent)
0416   {
0417     evaluate_event();
0418   }
0419   std::cout << "clusters..." << std::endl;
0420   if (m_flags & EvalClusters)
0421   {
0422     evaluate_clusters();
0423   }
0424   std::cout << "tracks..." << std::endl;
0425   if (m_flags & EvalTracks)
0426   {
0427     evaluate_tracks();
0428   }
0429   std::cout << "end..." << std::endl;
0430   // clear maps
0431   m_g4hit_map.clear();
0432   return Fun4AllReturnCodes::EVENT_OK;
0433 }
0434 
0435 //_____________________________________________________________________
0436 int TrackEvaluation::End(PHCompositeNode* /*unused*/)
0437 {
0438   return Fun4AllReturnCodes::EVENT_OK;
0439 }
0440 
0441 //_____________________________________________________________________
0442 int TrackEvaluation::load_nodes(PHCompositeNode* topNode)
0443 {
0444   // acts geometry
0445   m_tGeometry = findNode::getClass<ActsGeometry>(topNode, "ActsGeometry");
0446   assert(m_tGeometry);
0447 
0448   // get necessary nodes
0449   m_track_map = findNode::getClass<SvtxTrackMap>(topNode, "SvtxTrackMap");
0450 
0451   // cluster map
0452   m_cluster_map = findNode::getClass<TrkrClusterContainer>(topNode, "CORRECTED_TRKR_CLUSTER");
0453   if (!m_cluster_map)
0454   {
0455     m_cluster_map = findNode::getClass<TrkrClusterContainer>(topNode, "TRKR_CLUSTER");
0456   }
0457 
0458   // cluster hit association map
0459   m_cluster_hit_map = findNode::getClass<TrkrClusterHitAssoc>(topNode, "TRKR_CLUSTERHITASSOC");
0460 
0461   // cluster hit association map
0462   m_hit_truth_map = findNode::getClass<TrkrHitTruthAssoc>(topNode, "TRKR_HITTRUTHASSOC");
0463 
0464   // local container
0465   m_container = findNode::getClass<TrackEvaluationContainerv1>(topNode, "TrackEvaluationContainer");
0466 
0467   // hitset container
0468   m_hitsetcontainer = findNode::getClass<TrkrHitSetContainer>(topNode, "TRKR_HITSET");
0469 
0470   // g4hits
0471   m_g4hits_tpc = findNode::getClass<PHG4HitContainer>(topNode, "G4HIT_TPC");
0472   m_g4hits_intt = findNode::getClass<PHG4HitContainer>(topNode, "G4HIT_INTT");
0473   m_g4hits_mvtx = findNode::getClass<PHG4HitContainer>(topNode, "G4HIT_MVTX");
0474   m_g4hits_micromegas = findNode::getClass<PHG4HitContainer>(topNode, "G4HIT_MICROMEGAS");
0475 
0476   // g4 truth info
0477   m_g4truthinfo = findNode::getClass<PHG4TruthInfoContainer>(topNode, "G4TruthInfo");
0478 
0479   // tpc geometry
0480   m_tpc_geom_container = findNode::getClass<PHG4TpcCylinderGeomContainer>(topNode, "CYLINDERCELLGEOM_SVTX");
0481   assert(m_tpc_geom_container);
0482 
0483   // micromegas geometry
0484   m_micromegas_geom_container = findNode::getClass<PHG4CylinderGeomContainer>(topNode, "CYLINDERGEOM_MICROMEGAS_FULL");
0485 
0486   return Fun4AllReturnCodes::EVENT_OK;
0487 }
0488 
0489 //_____________________________________________________________________
0490 void TrackEvaluation::evaluate_event()
0491 {
0492   if (!m_container)
0493   {
0494     return;
0495   }
0496 
0497   // create event struct
0498   TrackEvaluationContainerv1::EventStruct event;
0499   if (m_cluster_map)
0500   {
0501     for (const auto& hitsetkey : m_cluster_map->getHitSetKeys())
0502     {
0503       const auto trkrId = TrkrDefs::getTrkrId(hitsetkey);
0504       const auto layer = TrkrDefs::getLayer(hitsetkey);
0505       assert(layer < TrackEvaluationContainerv1::EventStruct::max_layer);
0506 
0507       // fill cluster related information
0508       const auto clusters = m_cluster_map->getClusters(hitsetkey);
0509       const int nclusters = std::distance(clusters.first, clusters.second);
0510 
0511       switch (trkrId)
0512       {
0513       case TrkrDefs::mvtxId:
0514         event.nclusters_mvtx += nclusters;
0515         break;
0516       case TrkrDefs::inttId:
0517         event.nclusters_intt += nclusters;
0518         break;
0519       case TrkrDefs::tpcId:
0520         event.nclusters_tpc += nclusters;
0521         break;
0522       case TrkrDefs::micromegasId:
0523         event.nclusters_micromegas += nclusters;
0524         break;
0525       }
0526 
0527       event.nclusters[layer] += nclusters;
0528     }
0529   }
0530 
0531   // store
0532   m_container->addEvent(event);
0533 }
0534 
0535 //_____________________________________________________________________
0536 void TrackEvaluation::evaluate_clusters()
0537 {
0538   if (!(m_cluster_map && m_hitsetcontainer && m_container))
0539   {
0540     return;
0541   }
0542 
0543   // clear array
0544   m_container->clearClusters();
0545   SvtxTrack* track = nullptr;
0546   // first loop over hitsets
0547   for (const auto& hitsetkey : m_cluster_map->getHitSetKeys())
0548   {
0549     for (const auto& [key, cluster] : range_adaptor(m_cluster_map->getClusters(hitsetkey)))
0550     {
0551       // create cluster structure
0552       auto cluster_struct = create_cluster(key, cluster, track);
0553       add_cluster_size(cluster_struct, cluster);
0554       add_cluster_energy(cluster_struct, key, m_cluster_hit_map, m_hitsetcontainer);
0555 
0556       // truth information
0557       const auto g4hits = find_g4hits(key);
0558       const bool is_micromegas(TrkrDefs::getTrkrId(key) == TrkrDefs::micromegasId);
0559       if (is_micromegas)
0560       {
0561         const int tileid = MicromegasDefs::getTileId(key);
0562         add_truth_information_micromegas(cluster_struct, tileid, g4hits);
0563       }
0564       else
0565       {
0566         add_truth_information(cluster_struct, g4hits);
0567       }
0568 
0569       // add in array
0570       m_container->addCluster(cluster_struct);
0571     }
0572   }
0573 }
0574 
0575 //_____________________________________________________________________
0576 void TrackEvaluation::evaluate_tracks()
0577 {
0578   if (!(m_track_map && m_cluster_map && m_container))
0579   {
0580     return;
0581   }
0582 
0583   // clear array
0584   m_container->clearTracks();
0585   for (const auto& [track_id, track] : *m_track_map)
0586   {
0587     auto track_struct = create_track(track);
0588 
0589     // truth information
0590     const auto [id, contributors] = get_max_contributor(track);
0591     track_struct.contributors = contributors;
0592 
0593     // get particle
0594     auto particle = m_g4truthinfo->GetParticle(id);
0595     track_struct.embed = get_embed(particle);
0596     ::add_truth_information(track_struct, particle, m_g4truthinfo);
0597 
0598     // running iterator over track states, used to match a given cluster to a track state
0599     auto state_iter = track->begin_states();
0600     // loop over clusters
0601     for (const auto& cluster_key : get_cluster_keys(track))
0602     {
0603       auto cluster = m_cluster_map->findCluster(cluster_key);
0604       if (!cluster)
0605       {
0606         std::cout << "TrackEvaluation::evaluate_tracks - unable to find cluster for key " << cluster_key << std::endl;
0607         continue;
0608       }
0609       // create new cluster struct
0610       auto cluster_struct = create_cluster(cluster_key, cluster, track);
0611       add_cluster_size(cluster_struct, cluster);
0612       add_cluster_energy(cluster_struct, cluster_key, m_cluster_hit_map, m_hitsetcontainer);
0613       // truth information
0614       const auto g4hits = find_g4hits(cluster_key);
0615       const bool is_micromegas(TrkrDefs::getTrkrId(cluster_key) == TrkrDefs::micromegasId);
0616       if (is_micromegas)
0617       {
0618         const int tileid = MicromegasDefs::getTileId(cluster_key);
0619         add_truth_information_micromegas(cluster_struct, tileid, g4hits);
0620       }
0621       else
0622       {
0623         add_truth_information(cluster_struct, g4hits);
0624       }
0625 
0626       // find track state that is the closest to cluster
0627       /* this assumes that both clusters and states are sorted along r */
0628       const auto radius(cluster_struct.r);
0629       float dr_min = -1;
0630       for (auto iter = state_iter; iter != track->end_states(); ++iter)
0631       {
0632         const auto dr = std::abs(radius - get_r(iter->second->get_x(), iter->second->get_y()));
0633         if (dr_min < 0 || dr < dr_min)
0634         {
0635           state_iter = iter;
0636           dr_min = dr;
0637         }
0638         else
0639         {
0640           break;
0641         }
0642       }
0643 
0644       // store track state in cluster struct
0645       if (is_micromegas)
0646       {
0647         const int tileid = MicromegasDefs::getTileId(cluster_key);
0648         add_trk_information_micromegas(cluster_struct, tileid, state_iter->second);
0649       }
0650       else
0651       {
0652         add_trk_information(cluster_struct, state_iter->second);
0653       }
0654 
0655       // add to track
0656       track_struct.clusters.push_back(cluster_struct);
0657     }
0658     m_container->addTrack(track_struct);
0659   }
0660 }
0661 
0662 //_____________________________________________________________________
0663 TrackEvaluation::G4HitSet TrackEvaluation::find_g4hits(TrkrDefs::cluskey cluster_key) const
0664 {
0665   // check maps
0666   if (!(m_cluster_hit_map && m_hit_truth_map))
0667   {
0668     return G4HitSet();
0669   }
0670 
0671   // check if in map
0672   auto map_iter = m_g4hit_map.lower_bound(cluster_key);
0673   if (map_iter != m_g4hit_map.end() && cluster_key == map_iter->first)
0674   {
0675     return map_iter->second;
0676   }
0677 
0678   // find hitset associated to cluster
0679   G4HitSet out;
0680   const auto hitset_key = TrkrDefs::getHitSetKeyFromClusKey(cluster_key);
0681   for (const auto& [first, hit_key] : range_adaptor(m_cluster_hit_map->getHits(cluster_key)))
0682   {
0683     // store hits to g4hit associations
0684     TrkrHitTruthAssoc::MMap g4hit_map;
0685     m_hit_truth_map->getG4Hits(hitset_key, hit_key, g4hit_map);
0686 
0687     // find corresponding g4 hist
0688     for (auto& truth_iter : g4hit_map)
0689     {
0690       const auto g4hit_key = truth_iter.second.second;
0691       PHG4Hit* g4hit = nullptr;
0692 
0693       switch (TrkrDefs::getTrkrId(hitset_key))
0694       {
0695       case TrkrDefs::mvtxId:
0696         if (m_g4hits_mvtx)
0697         {
0698           g4hit = m_g4hits_mvtx->findHit(g4hit_key);
0699         }
0700         break;
0701 
0702       case TrkrDefs::inttId:
0703         if (m_g4hits_intt)
0704         {
0705           g4hit = m_g4hits_intt->findHit(g4hit_key);
0706         }
0707         break;
0708 
0709       case TrkrDefs::tpcId:
0710         if (m_g4hits_tpc)
0711         {
0712           g4hit = m_g4hits_tpc->findHit(g4hit_key);
0713         }
0714         break;
0715 
0716       case TrkrDefs::micromegasId:
0717         if (m_g4hits_micromegas)
0718         {
0719           g4hit = m_g4hits_micromegas->findHit(g4hit_key);
0720         }
0721         break;
0722 
0723       default:
0724         break;
0725       }
0726 
0727       if (g4hit)
0728       {
0729         out.insert(g4hit);
0730       }
0731       else
0732       {
0733         std::cout << "TrackEvaluation::find_g4hits - g4hit not found " << g4hit_key << std::endl;
0734       }
0735     }
0736   }
0737 
0738   // insert in map and return
0739   return m_g4hit_map.insert(map_iter, std::make_pair(cluster_key, std::move(out)))->second;
0740 }
0741 
0742 //_____________________________________________________________________
0743 std::pair<int, int> TrackEvaluation::get_max_contributor(SvtxTrack* track) const
0744 {
0745   if (!(m_track_map && m_cluster_map && m_g4truthinfo))
0746   {
0747     return {0, 0};
0748   }
0749 
0750   // maps MC track id and number of matching g4hits
0751   using IdMap = std::map<int, int>;
0752   IdMap contributor_map;
0753 
0754   // loop over clusters
0755   for (const auto& cluster_key : get_cluster_keys(track))
0756   {
0757     for (const auto& hit : find_g4hits(cluster_key))
0758     {
0759       const int trkid = hit->get_trkid();
0760       auto iter = contributor_map.lower_bound(trkid);
0761       if (iter == contributor_map.end() || iter->first != trkid)
0762       {
0763         contributor_map.insert(iter, std::make_pair(trkid, 1));
0764       }
0765       else
0766       {
0767         ++iter->second;
0768       }
0769     }
0770   }
0771 
0772   if (contributor_map.empty())
0773   {
0774     return {0, 0};
0775   }
0776   else
0777   {
0778     return *std::max_element(
0779         contributor_map.cbegin(), contributor_map.cend(),
0780         [](const IdMap::value_type& first, const IdMap::value_type& second)
0781         { return first.second < second.second; });
0782   }
0783 }
0784 
0785 //_____________________________________________________________________
0786 int TrackEvaluation::get_embed(PHG4Particle* particle) const
0787 {
0788   return (m_g4truthinfo && particle) ? m_g4truthinfo->isEmbeded(particle->get_primary_id()) : 0;
0789 }
0790 
0791 //_____________________________________________________________________
0792 TrackEvaluationContainerv1::ClusterStruct TrackEvaluation::create_cluster(TrkrDefs::cluskey key, TrkrCluster* cluster, SvtxTrack* track) const
0793 {
0794   TrackSeed* si_seed = nullptr;
0795   TrackSeed* tpc_seed = nullptr;
0796   if (track != nullptr)
0797   {
0798     si_seed = track->get_silicon_seed();
0799     tpc_seed = track->get_tpc_seed();
0800   }
0801   // get global coordinates
0802   Acts::Vector3 global;
0803   global = m_tGeometry->getGlobalPosition(key, cluster);
0804   TrackEvaluationContainerv1::ClusterStruct cluster_struct;
0805   cluster_struct.layer = TrkrDefs::getLayer(key);
0806   cluster_struct.x = global.x();
0807   cluster_struct.y = global.y();
0808   cluster_struct.z = global.z();
0809   cluster_struct.r = get_r(cluster_struct.x, cluster_struct.y);
0810   cluster_struct.phi = std::atan2(cluster_struct.y, cluster_struct.x);
0811   cluster_struct.phi_error = 0.0;
0812   cluster_struct.z_error = 0.0;
0813   cluster_struct.trk_alpha = 0.0;
0814   cluster_struct.trk_beta = 0.0;
0815 
0816   ClusterErrorPara ClusErrPara;
0817 
0818   if (track != nullptr)
0819   {
0820     float r = cluster_struct.r;
0821 
0822     if (cluster_struct.layer >= 7)
0823     {
0824       auto para_errors_mm = ClusErrPara.get_clusterv5_modified_error(cluster, r, key);
0825 
0826       cluster_struct.phi_error = cluster->getRPhiError() / cluster_struct.r;
0827       cluster_struct.z_error = cluster->getZError();
0828       cluster_struct.para_phi_error = sqrt(para_errors_mm.first) / cluster_struct.r;
0829       cluster_struct.para_z_error = sqrt(para_errors_mm.second);
0830       //    float R = TMath::Abs(1.0/tpc_seed->get_qOverR());
0831       cluster_struct.trk_radius = 1.0 / tpc_seed->get_qOverR();
0832       cluster_struct.trk_alpha = (r * r) / (2 * r * TMath::Abs(1.0 / tpc_seed->get_qOverR()));
0833       cluster_struct.trk_beta = atan(tpc_seed->get_slope());
0834     }
0835     else
0836     {
0837       auto para_errors_mvtx = ClusErrPara.get_cluster_error(cluster, r, key, si_seed->get_qOverR(), si_seed->get_slope());
0838       cluster_struct.phi_error = sqrt(para_errors_mvtx.first) / cluster_struct.r;
0839       cluster_struct.z_error = sqrt(para_errors_mvtx.second);
0840       //    float R = TMath::Abs(1.0/si_seed->get_qOverR());
0841       cluster_struct.trk_radius = 1.0 / tpc_seed->get_qOverR();
0842       cluster_struct.trk_alpha = (r * r) / (2 * r * TMath::Abs(1.0 / tpc_seed->get_qOverR()));
0843       cluster_struct.trk_beta = atan(si_seed->get_slope());
0844     }
0845   }
0846 
0847   return cluster_struct;
0848 }
0849 
0850 //_____________________________________________________________________
0851 void TrackEvaluation::add_trk_information(TrackEvaluationContainerv1::ClusterStruct& cluster, SvtxTrackState* state) const
0852 {
0853   // need to extrapolate to the right r
0854   const auto trk_r = get_r(state->get_x(), state->get_y());
0855   const auto dr = cluster.r - trk_r;
0856   const auto trk_drdt = (state->get_x() * state->get_px() + state->get_y() * state->get_py()) / trk_r;
0857   const auto trk_dxdr = state->get_px() / trk_drdt;
0858   const auto trk_dydr = state->get_py() / trk_drdt;
0859   const auto trk_dzdr = state->get_pz() / trk_drdt;
0860 
0861   // store state position
0862   cluster.trk_x = state->get_x() + dr * trk_dxdr;
0863   cluster.trk_y = state->get_y() + dr * trk_dydr;
0864   cluster.trk_z = state->get_z() + dr * trk_dzdr;
0865   cluster.trk_r = get_r(cluster.trk_x, cluster.trk_y);
0866   cluster.trk_phi = std::atan2(cluster.trk_y, cluster.trk_x);
0867 
0868   /* store local momentum information */
0869   cluster.trk_px = state->get_px();
0870   cluster.trk_py = state->get_py();
0871   cluster.trk_pz = state->get_pz();
0872 
0873   /*
0874   store state angles in (r,phi) and (r,z) plans
0875   they are needed to study space charge distortions
0876   */
0877   const auto cosphi(std::cos(cluster.trk_phi));
0878   const auto sinphi(std::sin(cluster.trk_phi));
0879   const auto trk_pphi = -state->get_px() * sinphi + state->get_py() * cosphi;
0880   const auto trk_pr = state->get_px() * cosphi + state->get_py() * sinphi;
0881   const auto trk_pz = state->get_pz();
0882   cluster.trk_alpha = std::atan2(trk_pphi, trk_pr);
0883   cluster.trk_beta = std::atan2(trk_pz, trk_pr);
0884   cluster.trk_phi_error = state->get_phi_error();
0885   cluster.trk_z_error = state->get_z_error();
0886 }
0887 
0888 //_____________________________________________________________________
0889 void TrackEvaluation::add_trk_information_micromegas(TrackEvaluationContainerv1::ClusterStruct& cluster, int tileid, SvtxTrackState* state) const
0890 {
0891   // get geometry cylinder from layer
0892   const auto layer = cluster.layer;
0893   const auto layergeom = dynamic_cast<CylinderGeomMicromegas*>(m_micromegas_geom_container->GetLayerGeom(layer));
0894   assert(layergeom);
0895 
0896   // convert cluster position to local tile coordinates
0897   const TVector3 cluster_world(cluster.x, cluster.y, cluster.z);
0898   const auto cluster_local = layergeom->get_local_from_world_coords(tileid, m_tGeometry, cluster_world);
0899 
0900   // convert track position to local tile coordinates
0901   TVector3 track_world(state->get_x(), state->get_y(), state->get_z());
0902   TVector3 track_local = layergeom->get_local_from_world_coords(tileid, m_tGeometry, track_world);
0903 
0904   // convert direction to local tile coordinates
0905   const TVector3 direction_world(state->get_px(), state->get_py(), state->get_pz());
0906   const TVector3 direction_local = layergeom->get_local_from_world_vect(tileid, m_tGeometry, direction_world);
0907 
0908   // extrapolate to same local z (should be zero) as cluster
0909   const auto delta_z = cluster_local.z() - track_local.z();
0910   track_local += TVector3(
0911       delta_z * direction_local.x() / direction_local.z(),
0912       delta_z * direction_local.y() / direction_local.z(),
0913       delta_z);
0914 
0915   // convert back to global coordinates
0916   track_world = layergeom->get_world_from_local_coords(tileid, m_tGeometry, track_local);
0917 
0918   // store state position
0919   cluster.trk_x = track_world.x();
0920   cluster.trk_y = track_world.y();
0921   cluster.trk_z = track_world.z();
0922   cluster.trk_r = get_r(cluster.trk_x, cluster.trk_y);
0923   cluster.trk_phi = std::atan2(cluster.trk_y, cluster.trk_x);
0924 
0925   /* store local momentum information */
0926   cluster.trk_px = state->get_px();
0927   cluster.trk_py = state->get_py();
0928   cluster.trk_pz = state->get_pz();
0929 
0930   /*
0931   store state angles in (r,phi) and (r,z) plans
0932   they are needed to study space charge distortions
0933   */
0934   const auto cosphi(std::cos(cluster.trk_phi));
0935   const auto sinphi(std::sin(cluster.trk_phi));
0936   const auto trk_pphi = -state->get_px() * sinphi + state->get_py() * cosphi;
0937   const auto trk_pr = state->get_px() * cosphi + state->get_py() * sinphi;
0938   const auto trk_pz = state->get_pz();
0939   cluster.trk_alpha = std::atan2(trk_pphi, trk_pr);
0940   cluster.trk_beta = std::atan2(trk_pz, trk_pr);
0941   cluster.trk_phi_error = state->get_phi_error();
0942   cluster.trk_z_error = state->get_z_error();
0943 }
0944 
0945 //_____________________________________________________________________
0946 void TrackEvaluation::add_truth_information(TrackEvaluationContainerv1::ClusterStruct& cluster, const std::set<PHG4Hit*>& g4hits) const
0947 {
0948   // store number of contributing g4hits
0949   cluster.truth_size = g4hits.size();
0950 
0951   // get layer, tpc flag and corresponding layer geometry
0952   const auto layer = cluster.layer;
0953   const bool is_tpc(layer >= 7 && layer < 55);
0954   const PHG4TpcCylinderGeom* layergeom = is_tpc ? m_tpc_geom_container->GetLayerCellGeom(layer) : nullptr;
0955   const auto rin = layergeom ? layergeom->get_radius() - layergeom->get_thickness() / 2 : 0;
0956   const auto rout = layergeom ? layergeom->get_radius() + layergeom->get_thickness() / 2 : 0;
0957 
0958   // convert hits to list of interpolation_data_t
0959   interpolation_data_t::list hits;
0960   for (const auto& g4hit : g4hits)
0961   {
0962     interpolation_data_t::list tmp_hits;
0963     const auto weight = g4hit->get_edep();
0964     for (int i = 0; i < 2; ++i)
0965     {
0966       const TVector3 g4hit_world(g4hit->get_x(i), g4hit->get_y(i), g4hit->get_z(i));
0967       const TVector3 momentum_world(g4hit->get_px(i), g4hit->get_py(i), g4hit->get_pz(i));
0968       tmp_hits.push_back({.position = g4hit_world, .momentum = momentum_world, .weight = weight});
0969     }
0970 
0971     if (is_tpc)
0972     {
0973       // add layer boundary checks
0974       // ensure first hit has lowest r
0975       auto r0 = get_r(tmp_hits[0].x(), tmp_hits[0].y());
0976       auto r1 = get_r(tmp_hits[1].x(), tmp_hits[1].y());
0977       if (r0 > r1)
0978       {
0979         std::swap(tmp_hits[0], tmp_hits[1]);
0980         std::swap(r0, r1);
0981       }
0982 
0983       // do nothing if out of bound
0984       if (r1 <= rin || r0 >= rout)
0985       {
0986         continue;
0987       }
0988 
0989       // keep track of original deltar
0990       const auto dr_old = r1 - r0;
0991 
0992       // clamp r0 to rin
0993       if (r0 < rin)
0994       {
0995         const auto t = line_circle_intersection(tmp_hits[0].position, tmp_hits[1].position, rin);
0996         if (t < 0)
0997         {
0998           continue;
0999         }
1000 
1001         tmp_hits[0].position = tmp_hits[0].position * (1. - t) + tmp_hits[1].position * t;
1002         tmp_hits[0].momentum = tmp_hits[0].momentum * (1. - t) + tmp_hits[1].momentum * t;
1003         r0 = rin;
1004       }
1005 
1006       if (r1 > rout)
1007       {
1008         const auto t = line_circle_intersection(tmp_hits[0].position, tmp_hits[1].position, rout);
1009         if (t < 0)
1010         {
1011           continue;
1012         }
1013 
1014         tmp_hits[1].position = tmp_hits[0].position * (1. - t) + tmp_hits[1].position * t;
1015         tmp_hits[1].momentum = tmp_hits[0].momentum * (1. - t) + tmp_hits[1].momentum * t;
1016         r1 = rout;
1017       }
1018 
1019       // update weights, only if clamping occured
1020       const auto dr_new = r1 - r0;
1021       tmp_hits[0].weight *= dr_new / dr_old;
1022       tmp_hits[1].weight *= dr_new / dr_old;
1023     }
1024 
1025     // store in global list
1026     hits.push_back(std::move(tmp_hits[0]));
1027     hits.push_back(std::move(tmp_hits[1]));
1028   }
1029 
1030   // add truth position
1031   cluster.truth_x = average<&interpolation_data_t::x>(hits);
1032   cluster.truth_y = average<&interpolation_data_t::y>(hits);
1033   cluster.truth_z = average<&interpolation_data_t::z>(hits);
1034 
1035   // add truth momentum information
1036   cluster.truth_px = average<&interpolation_data_t::px>(hits);
1037   cluster.truth_py = average<&interpolation_data_t::py>(hits);
1038   cluster.truth_pz = average<&interpolation_data_t::pz>(hits);
1039 
1040   cluster.truth_r = get_r(cluster.truth_x, cluster.truth_y);
1041   cluster.truth_phi = std::atan2(cluster.truth_y, cluster.truth_x);
1042 
1043   /*
1044   store state angles in (r,phi) and (r,z) plans
1045   they are needed to study space charge distortions
1046   */
1047   const auto cosphi(std::cos(cluster.truth_phi));
1048   const auto sinphi(std::sin(cluster.truth_phi));
1049   const auto truth_pphi = -cluster.truth_px * sinphi + cluster.truth_py * cosphi;
1050   const auto truth_pr = cluster.truth_px * cosphi + cluster.truth_py * sinphi;
1051 
1052   cluster.truth_alpha = std::atan2(truth_pphi, truth_pr);
1053   cluster.truth_beta = std::atan2(cluster.truth_pz, truth_pr);
1054 }
1055 
1056 //_____________________________________________________________________
1057 void TrackEvaluation::add_truth_information_micromegas(TrackEvaluationContainerv1::ClusterStruct& cluster, int tileid, const std::set<PHG4Hit*>& g4hits) const
1058 {
1059   // store number of contributing g4hits
1060   cluster.truth_size = g4hits.size();
1061 
1062   const auto layer = cluster.layer;
1063   const auto layergeom = dynamic_cast<CylinderGeomMicromegas*>(m_micromegas_geom_container->GetLayerGeom(layer));
1064   assert(layergeom);
1065 
1066   // convert cluster position to local tile coordinates
1067   const TVector3 cluster_world(cluster.x, cluster.y, cluster.z);
1068   const auto cluster_local = layergeom->get_local_from_world_coords(tileid, m_tGeometry, cluster_world);
1069 
1070   // convert hits to list of interpolation_data_t
1071   interpolation_data_t::list hits;
1072   for (const auto& g4hit : g4hits)
1073   {
1074     const auto weight = g4hit->get_edep();
1075     for (int i = 0; i < 2; ++i)
1076     {
1077       // convert position to local
1078       TVector3 g4hit_world(g4hit->get_x(i), g4hit->get_y(i), g4hit->get_z(i));
1079       auto g4hit_local = layergeom->get_local_from_world_coords(tileid, m_tGeometry, g4hit_world);
1080 
1081       // convert momentum to local
1082       TVector3 momentum_world(g4hit->get_px(i), g4hit->get_py(i), g4hit->get_pz(i));
1083       TVector3 momentum_local = layergeom->get_local_from_world_vect(tileid, m_tGeometry, momentum_world);
1084 
1085       hits.push_back({.position = g4hit_local, .momentum = momentum_local, .weight = weight});
1086     }
1087   }
1088 
1089   // do position interpolation
1090   const TVector3 interpolation_local(
1091       average<&interpolation_data_t::x>(hits),
1092       average<&interpolation_data_t::y>(hits),
1093       average<&interpolation_data_t::z>(hits));
1094 
1095   // convert back to global
1096   const TVector3 interpolation_world = layergeom->get_world_from_local_coords(tileid, m_tGeometry, interpolation_local);
1097 
1098   // do momentum interpolation
1099   const TVector3 momentum_local(
1100       average<&interpolation_data_t::px>(hits),
1101       average<&interpolation_data_t::py>(hits),
1102       average<&interpolation_data_t::pz>(hits));
1103 
1104   // convert back to global
1105   const TVector3 momentum_world = layergeom->get_world_from_local_vect(tileid, m_tGeometry, momentum_local);
1106 
1107   cluster.truth_x = interpolation_world.x();
1108   cluster.truth_y = interpolation_world.y();
1109   cluster.truth_z = interpolation_world.z();
1110   cluster.truth_r = get_r(cluster.truth_x, cluster.truth_y);
1111   cluster.truth_phi = std::atan2(cluster.truth_y, cluster.truth_x);
1112 
1113   /* add truth momentum information */
1114   cluster.truth_px = momentum_world.x();
1115   cluster.truth_py = momentum_world.y();
1116   cluster.truth_pz = momentum_world.z();
1117 
1118   /*
1119   store state angles in (r,phi) and (r,z) plans
1120   they are needed to study space charge distortions
1121   */
1122   const auto cosphi(std::cos(cluster.truth_phi));
1123   const auto sinphi(std::sin(cluster.truth_phi));
1124   const auto truth_pphi = -cluster.truth_px * sinphi + cluster.truth_py * cosphi;
1125   const auto truth_pr = cluster.truth_px * cosphi + cluster.truth_py * sinphi;
1126 
1127   cluster.truth_alpha = std::atan2(truth_pphi, truth_pr);
1128   cluster.truth_beta = std::atan2(cluster.truth_pz, truth_pr);
1129 }