Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-12-17 09:21:54

0001 #include "TruthRecoTrackMatching.h"
0002 #include "ClusKeyIter.h"
0003 #include "TrkrClusterIsMatcher.h"
0004 
0005 #include <g4detectors/PHG4TpcGeomContainer.h>
0006 
0007 #include <g4main/PHG4TruthInfoContainer.h>
0008 
0009 #include <g4tracking/EmbRecoMatch.h>
0010 #include <g4tracking/EmbRecoMatchContainer.h>
0011 #include <g4tracking/EmbRecoMatchContainerv1.h>
0012 #include <g4tracking/EmbRecoMatchv1.h>
0013 #include <g4tracking/TrkrTruthTrack.h>
0014 #include <g4tracking/TrkrTruthTrackContainer.h>
0015 
0016 #include <trackbase/ActsGeometry.h>
0017 #include <trackbase/TrkrClusterContainer.h>
0018 #include <trackbase/TrkrDefs.h>
0019 
0020 #include <trackbase_historic/SvtxTrack.h>
0021 #include <trackbase_historic/SvtxTrackMap.h>
0022 
0023 #include <fun4all/Fun4AllReturnCodes.h>
0024 
0025 #include <phool/PHCompositeNode.h>
0026 #include <phool/PHIODataNode.h>
0027 #include <phool/PHNode.h>  // for PHNode
0028 #include <phool/PHNodeIterator.h>
0029 #include <phool/PHObject.h>  // for PHObject
0030 #include <phool/getClass.h>
0031 #include <phool/phool.h>  // for PHWHERE
0032 
0033 #include <TFile.h>
0034 #include <TTree.h>
0035 
0036 #include <algorithm>
0037 #include <format>
0038 
0039 // To change:
0040 // Make the definition of matching clusters to be if the truth cluster center is within 1/2 of width of the reco track center
0041 
0042 TruthRecoTrackMatching::TruthRecoTrackMatching(
0043     TrkrClusterIsMatcher* _ismatcher, const unsigned short _nmincluster_match, const float _nmincluster_ratio, const float _cutoff_dphi, const float _same_dphi, const float _cutoff_deta, const float _same_deta, const unsigned short _max_nreco_per_truth, const unsigned short _max_ntruth_per_reco)
0044   : m_ismatcher{_ismatcher}
0045   , m_nmincluster_match{_nmincluster_match}  // minimum number of clusters to match, default=4
0046   , m_nmincluster_ratio{_nmincluster_ratio}  // minimum ratio to match a track, default=0.
0047                                              // -- Track Kinematic Cuts to match --
0048   , m_cutoff_dphi{_cutoff_dphi}              // maximum |dphi|=|phi_reco-phi_truth| to search for match
0049   , m_same_dphi{_same_dphi}                  // all tracks in this |dphi| must be tested for matches
0050   , m_cutoff_deta{_cutoff_deta}              // same as m_cutoff_dphi for deta
0051   , m_same_deta{_same_deta}                  // same as m_same_dphi for deta
0052                                              // cluster matching widths ()
0053                                              // number of truth tracks allowed matched per reco track, and v. versa
0054   , m_max_nreco_per_truth{_max_nreco_per_truth}
0055   , m_max_ntruth_per_reco{_max_ntruth_per_reco}
0056 {
0057   m_TCEval.ismatcher = m_ismatcher;
0058   if (Verbosity() > 50)
0059   {
0060     std::cout << " Starting TruthRecoTrackMatching.cc " << std::endl;
0061   }
0062 }
0063 
0064 int TruthRecoTrackMatching::InitRun(PHCompositeNode* topNode)  //`
0065 {
0066   if (Verbosity() > 10)
0067   {
0068     topNode->print();
0069   }
0070   auto init_status = m_ismatcher->init(topNode);
0071   if (init_status == Fun4AllReturnCodes::ABORTRUN)
0072   {
0073     return init_status;
0074   }
0075 
0076   if (createNodes(topNode) != Fun4AllReturnCodes::EVENT_OK)
0077   {
0078     return Fun4AllReturnCodes::ABORTRUN;
0079   }
0080   m_nmatched_id_reco = &(m_EmbRecoMatchContainer->map_nTruthPerReco());
0081   m_nmatched_id_true = &(m_EmbRecoMatchContainer->map_nRecoPerTruth());
0082   return Fun4AllReturnCodes::EVENT_OK;
0083 }
0084 
0085 int TruthRecoTrackMatching::process_event(PHCompositeNode* topnode)
0086 {
0087   if (topnode == nullptr)
0088   {
0089     return Fun4AllReturnCodes::ABORTRUN;
0090   }
0091   if (Verbosity() > 1000)
0092   {
0093     topnode->print();  // perhaps not needed
0094   }
0095 
0096   m_nmatched_index_true.clear();
0097 
0098   // -------------------------------------------------------------------------------
0099   // Build recoData
0100   // ------------------------------------------------------------------------------
0101 
0102   // recoData is a vector of tuples that acts like a table with four columns,
0103   // with one entry each of n tracks:
0104   //    (0)::float  (1)::float  (2)::float  (3)::short
0105   //     phi-0        eta-0       pT-0       index-0
0106   //     phi-1        eta-1       pT-1       index-1
0107   //     phi-2        eta-2       pT-2       index-2
0108   //     ...          ...         ...        ...
0109   //     phi-n-2      eta-n-2     pT-n-2     index-n-2
0110   //     phi-n-1      eta-n-1     pT-n-1     index-n-1
0111   //     phi-n        eta-n       pT-n       index-n
0112   if (Verbosity() > 60)
0113   {
0114     std::cout << "reco tracks size: " << m_SvtxTrackMap->size() << std::endl;
0115   }
0116 
0117   recoData.clear();
0118 
0119   for (auto& reco : *m_SvtxTrackMap)
0120   {
0121     auto index_reco = reco.first;
0122     auto* track = reco.second;
0123     recoData.emplace_back(track->get_phi(), track->get_eta(), track->get_pt(), index_reco);
0124   }
0125   // sort the recoData table by phi
0126   std::sort(recoData.begin(), recoData.end(), CompRECOtoPhi());
0127 
0128   // phi will be sorted by proximity in the table, so re-add the first entries to the
0129   // end of the table so that they can be compared against the phi across the
0130   // circular boundary condition. This makes the table potentially look like:
0131   //    (0)::float  (1)::float  (2)::float  (3)::short
0132   //     phi-0        eta-0       pT-0       index-0
0133   //     phi-1        eta-1       pT-1       index-1
0134   //     phi-2        eta-2       pT-2       index-2
0135   //     ...          ...         ...        ...
0136   //     phi-n-2      eta-n-2     pT-n-2     index-n-2
0137   //     phi-n-1      eta-n-1     pT-n-1     index-n-1
0138   //     phi-n        eta-n       pT-n       index-n
0139   //     phi-0        eta-0       pT-0       index-0 // <- values wrapped from the lowest phi values
0140   //     phi-1        eta-1       pT-1       index-1 // <-
0141   RECOvec wraps{};
0142   auto wrap_from_start = std::upper_bound(recoData.begin(),
0143                                           recoData.end(), (-M_PI + m_cutoff_dphi), CompRECOtoPhi());
0144   for (auto iter = recoData.begin(); iter != wrap_from_start; ++iter)
0145   {
0146     auto entry = *iter;                                              // make a new copy to wrap to the other side of recoData
0147     std::get<RECOphi>(entry) = std::get<RECOphi>(entry) + 2 * M_PI;  // put the new copy on the other end
0148     wraps.push_back(entry);
0149   }
0150   for (auto E : wraps)
0151   {
0152     recoData.push_back(E);
0153   }
0154 
0155   if (Verbosity() > 70)
0156   {
0157     std::cout << " ****************************************** " << std::endl;
0158     std::cout << " This is the RECO map of tracks to match to " << std::endl;
0159     std::cout << " ****************************************** " << std::endl;
0160     for (auto& E : recoData)
0161     {
0162       std::cout << std::format(" id:{:2}  (phi:eta:pt) ({:5.2}:{:5.2}:{:5.2})", std::get<RECOid>(E),
0163                                std::get<RECOphi>(E), std::get<RECOeta>(E), std::get<RECOpt>(E))
0164                 << std::endl;
0165     }
0166     std::cout << " ****end*listing*********************** " << std::endl;
0167   }
0168 
0169   /******************************************************************************
0170    * Loop through the truth tracks one at a time. Based on track phi, eta, and pT
0171    * build two indices of truth-to-reco pairs:
0172    *    innerbox_pairs: pairs of truth-to-reco tracks within same_d{phi,eta}
0173    *      i.e. |phi_true-phi_reco|<same_dphi && |eta_true-eta_reco|<same_deta).
0174    *      These are the "innerboxes" (retangles in phi and eta space).
0175    *      All of these pairs will have to be checked for matching tracks, and the
0176    *      best fits will be removed first.
0177    *    outerbox_pairs: these are wider boxes (sized by cutoff_d{phi,eta})
0178    *      These are possible matches that are only checked for tracks remaining
0179    *      after the innerbox_pairs are checked and matches made.
0180    ******************************************************************************/
0181 
0182   std::vector<std::pair<unsigned short, unsigned short>> outerbox_pairs{};
0183   std::vector<std::pair<unsigned short, unsigned short>> innerbox_pairs{};
0184 
0185   if (topnode == nullptr)
0186   {
0187     return Fun4AllReturnCodes::ABORTRUN;
0188   }
0189   if (Verbosity() > 70)
0190   {
0191     std::cout << "Number of truth tracks: " << m_TrkrTruthTrackContainer->getMap().size() << std::endl;
0192     for (auto& _pair : m_TrkrTruthTrackContainer->getMap())
0193     {
0194       auto& track = _pair.second;
0195       std::cout << std::format(" id:{:2}  (phi:eta:pt) ({:5.2}:{:5.2}:{:5.2} nclus: {})",
0196                                track->getTrackid(), track->getPhi(), track->getPseudoRapidity(),
0197                                track->getPt(), track->getClusters().size())
0198                 << std::endl;
0199     }
0200     std::cout << " ----end-listing-truth-tracks---------- " << std::endl;
0201   }
0202 
0203   for (auto _pair : m_TrkrTruthTrackContainer->getMap())
0204   {
0205     auto id_true = _pair.first;
0206     auto* track = _pair.second;
0207 
0208     auto match_indices = find_box_matches(track->getPhi(),
0209                                           track->getPseudoRapidity(), track->getPt());
0210 
0211     // keep track of all truth tracks (by track-id) which has been matched
0212     for (auto& id_reco : match_indices.first)
0213     {
0214       innerbox_pairs.emplace_back(id_true, id_reco);
0215     }
0216     for (auto& id_reco : match_indices.second)
0217     {
0218       outerbox_pairs.emplace_back(id_true, id_reco);
0219     }
0220 
0221     if (Verbosity() > 80)
0222     {
0223       std::cout << std::format(" T({})  find box({:5.2}:{:5.2}:{:5.2})",
0224                                track->getTrackid(), track->getPhi(),
0225                                track->getPseudoRapidity(), track->getPt());
0226       for (auto& id_reco : match_indices.first)
0227       {
0228         std::cout << "->IB(" << id_reco << ")";
0229       }
0230       for (auto& id_reco : match_indices.second)
0231       {
0232         std::cout << "->OB(" << id_reco << ")";
0233       }
0234       std::cout << std::endl;
0235     }
0236   }
0237 
0238   if (Verbosity() > 100)
0239   {
0240     std::cout << "Innerbox pairs" << std::endl;
0241   }
0242   match_tracks_in_box(innerbox_pairs);
0243   if (Verbosity() > 100)
0244   {
0245     std::cout << "Outerbox pairs" << std::endl;
0246   }
0247   match_tracks_in_box(outerbox_pairs);
0248 
0249   // fill the list of all truth track ids that are not matched
0250   for (auto _pair : m_TrkrTruthTrackContainer->getMap())
0251   {
0252     auto id_true = _pair.first;
0253     m_EmbRecoMatchContainer->checkfill_idsTruthUnmatched(id_true);
0254   }
0255 
0256   if (Verbosity() > 50)
0257   {
0258     std::cout << " --START--print-all-stored-matches--" << std::endl;
0259     std::cout << " --0-- Printing all matches stored (start)" << std::endl;
0260     // re-print all the tracks with the matches with the fit values
0261     for (auto* match : m_EmbRecoMatchContainer->getMatches())
0262     {
0263       std::cout << std::format(" Match id({:2}->{:2}) nClusMatch-nClusTrue-nClusReco ({:2}:{:2}:{:2})",
0264                                match->idTruthTrack(), match->idRecoTrack(), match->nClustersMatched(),
0265                                match->nClustersTruth(), match->nClustersReco())
0266                 << std::endl;
0267     }
0268     std::cout << " --END--print-all-stored-matches----" << std::endl;
0269   }
0270 
0271   if (m_write_diag)
0272   {
0273     fill_tree();
0274   }
0275 
0276   return Fun4AllReturnCodes::EVENT_OK;
0277 }
0278 
0279 int TruthRecoTrackMatching::End(PHCompositeNode* /*topNode*/)
0280 {
0281   TFile* s_current = gDirectory->GetFile();
0282   if (m_write_diag)
0283   {
0284     m_diag_file->cd();
0285     /* G4Eval::write_StringToTFile( */
0286     /*     "trk_match_sel", */
0287     /*     Form("  Matching criteria:\n" */
0288     /*          "  min. clusters to match:  %i\n" */
0289     /*          "  min. clust. match ratio: %4.2f" */
0290     /*          "  dphi small window:       %4.2f" */
0291     /*          "  dphi large windows:      %4.2f" */
0292     /*          "  deta small window:       %4.2f" */
0293     /*          "  deta large window:       %4.2f" */
0294     /*          "  nmax phg4 matches per svtx:  %i" */
0295     /*          "  nmax svtx matches per phg4:  %i", */
0296     /*          m_nmincluster_match, m_nmincluster_ratio, m_same_dphi, m_cutoff_dphi, m_same_deta, m_cutoff_deta, m_max_ntruth_per_reco, m_max_nreco_per_truth)); */
0297     m_diag_tree->Write();
0298     m_diag_file->Save();
0299     m_diag_file->Close();
0300     if (s_current != nullptr)
0301     {
0302       s_current->cd();
0303     }
0304   }
0305 
0306   return Fun4AllReturnCodes::EVENT_OK;
0307 }
0308 
0309 //--------------------------------------------------
0310 // Internal functions
0311 //--------------------------------------------------
0312 
0313 int TruthRecoTrackMatching::createNodes(PHCompositeNode* topNode)
0314 {
0315   PHNodeIterator iter(topNode);
0316   PHCompositeNode* dstNode = dynamic_cast<PHCompositeNode*>(iter.findFirst("PHCompositeNode", "DST"));
0317   if (!dstNode)
0318   {
0319     std::cout << PHWHERE << "DST Node missing, doing nothing." << std::endl;
0320     exit(1);
0321   }
0322 
0323   // Initiailize the modules
0324   m_PHG4TruthInfoContainer = findNode::getClass<PHG4TruthInfoContainer>(topNode, "G4TruthInfo");
0325   if (!m_PHG4TruthInfoContainer)
0326   {
0327     std::cout << "Could not locate G4TruthInfo node when running "
0328               << "\"TruthRecoTrackMatching\" module." << std::endl;
0329     return Fun4AllReturnCodes::ABORTEVENT;
0330   }
0331 
0332   m_SvtxTrackMap = findNode::getClass<SvtxTrackMap>(topNode, "SvtxTrackMap");
0333   if (!m_SvtxTrackMap)
0334   {
0335     std::cout << "Could not locate SvtxTrackMap node when running "
0336               << "\"TruthRecoTrackMatching\" module." << std::endl;
0337     return Fun4AllReturnCodes::ABORTEVENT;
0338   }
0339 
0340   m_TruthClusterContainer = findNode::getClass<TrkrClusterContainer>(topNode,
0341                                                                      "TRKR_TRUTHCLUSTERCONTAINER");
0342   if (!m_TruthClusterContainer)
0343   {
0344     std::cout << "Could not locate TRKR_TRUTHCLUSTERCONTAINER node when "
0345               << "running \"TruthRecoTrackMatching\" module." << std::endl;
0346     return Fun4AllReturnCodes::ABORTEVENT;
0347   }
0348 
0349   m_RecoClusterContainer = findNode::getClass<TrkrClusterContainer>(topNode, "TRKR_CLUSTER");
0350   if (!m_RecoClusterContainer)
0351   {
0352     std::cout << "Could not locate TRKR_CLUSTER node when running "
0353               << "\"TruthRecoTrackMatching\" module." << std::endl;
0354     return Fun4AllReturnCodes::ABORTEVENT;
0355   }
0356 
0357   m_TrkrTruthTrackContainer = findNode::getClass<TrkrTruthTrackContainer>(topNode,
0358                                                                           "TRKR_TRUTHTRACKCONTAINER");
0359   if (!m_TrkrTruthTrackContainer)
0360   {
0361     std::cout << "Could not locate TRKR_TRUTHTRACKCONTAINER node when "
0362               << "running \"TruthRecoTrackMatching\" module." << std::endl;
0363     return Fun4AllReturnCodes::ABORTEVENT;
0364   }
0365 
0366   m_PHG4TpcGeomContainer =
0367       findNode::getClass<PHG4TpcGeomContainer>(topNode, "TPCGEOMCONTAINER");
0368   if (!m_PHG4TpcGeomContainer)
0369   {
0370     std::cout << "Could not locate TPCGEOMCONTAINER node when "
0371               << "running \"TruthRecoTrackMatching\" module." << std::endl;
0372     return Fun4AllReturnCodes::ABORTEVENT;
0373   }
0374 
0375   // note that layers 0-6, and > 55, don't work
0376   /* for (int layer=7; layer<55; ++layer) { */
0377   /*   PHG4TpcGeom *layergeom = m_PHG4TpcGeomContainer->GetLayerCellGeom(layer); */
0378   /*   if (layer==7) m_zstep = layergeom->get_zstep(); */
0379   /*   m_phistep[layer] = layergeom->get_phistep(); */
0380   /* } */
0381   m_EmbRecoMatchContainer = findNode::getClass<EmbRecoMatchContainer>(topNode,
0382                                                                       "TRKR_EMBRECOMATCHCONTAINER");
0383   if (!m_EmbRecoMatchContainer)
0384   {
0385     PHNodeIterator dstiter(dstNode);
0386 
0387     auto* DetNode = dynamic_cast<PHCompositeNode*>(dstiter.findFirst("PHCompositeNode", "TRKR"));
0388     if (!DetNode)
0389     {
0390       DetNode = new PHCompositeNode("TRKR");
0391       dstNode->addNode(DetNode);
0392     }
0393 
0394     m_EmbRecoMatchContainer = new EmbRecoMatchContainerv1;
0395     auto* newNode = new PHIODataNode<PHObject>(m_EmbRecoMatchContainer,
0396                                                "TRKR_EMBRECOMATCHCONTAINER", "PHObject");
0397     DetNode->addNode(newNode);
0398   }
0399 
0400   m_ActsGeometry = findNode::getClass<ActsGeometry>(topNode, "ActsGeometry");
0401 
0402   return Fun4AllReturnCodes::EVENT_OK;
0403 }
0404 
0405 std::pair<std::vector<unsigned short>, std::vector<unsigned short>>
0406 TruthRecoTrackMatching::find_box_matches(float truth_phi, float truth_eta, float truth_pt)
0407 {
0408   // sort through the recoData to find:
0409   //     inner_box : possible matches withing m_same_d{phi,eta,pt}
0410   //     outer_box : (assumed to be strictly larger than, and therefore contain, inner_box, in m_cutoff_d{phi, eta,pt}
0411   // recoData is already sorted by eta, but as the boxes "zoom in" on acceptable matches, it gets
0412   // sorted by eta and (if applicable) pT as well. `mix_pair` keeps track of the range of recoData that has
0413   // been sorted so that it can be resorted to phi order.
0414   RECO_pair_iter mix_pair{recoData.begin(), recoData.end()};
0415 
0416   mix_pair.first = std::lower_bound(mix_pair.first, mix_pair.second,
0417                                     truth_phi - m_cutoff_dphi, CompRECOtoPhi());
0418 
0419   mix_pair.second = std::upper_bound(mix_pair.first, mix_pair.second,
0420                                      truth_phi + m_cutoff_dphi, CompRECOtoPhi());
0421 
0422   if (mix_pair.first == mix_pair.second)
0423   {
0424     // there are no possible phi_matches; return nothing
0425     return {{}, {}};
0426   }
0427 
0428   // There are tracks within the outer_box phi range;
0429   // now re-shuffle the phi outerbox range for eta and see if there
0430   // are tracks in outer_box-eta range
0431   std::sort(mix_pair.first, mix_pair.second, CompRECOtoEta());
0432 
0433   RECO_pair_iter outer_box = mix_pair;
0434   outer_box.first = std::lower_bound(mix_pair.first, mix_pair.second,
0435                                      truth_eta - m_cutoff_deta, CompRECOtoEta());
0436 
0437   outer_box.second = std::lower_bound(outer_box.first, outer_box.second,
0438                                       truth_eta + m_cutoff_deta, CompRECOtoEta());
0439 
0440   if (outer_box.first == outer_box.second)
0441   {
0442     // there are no eta_matches in outerbox; resort mix_pair and return nothing
0443     std::sort(mix_pair.first, mix_pair.second, CompRECOtoPhi());
0444     return {{}, {}};
0445   }
0446 
0447   // if pt is specified, restrict pt_box to the given range
0448   RECO_pair_iter inner_box = outer_box;
0449   const float _delta_outer_pt = delta_outer_pt(truth_pt);
0450   const float _delta_inner_pt = delta_inner_pt(truth_pt);
0451   if (_delta_outer_pt > 0)
0452   {
0453     std::sort(outer_box.first, outer_box.second, CompRECOtoPt());
0454     outer_box.first = std::lower_bound(outer_box.first, outer_box.second, truth_pt - _delta_outer_pt, CompRECOtoPt());
0455     outer_box.second = std::upper_bound(outer_box.first, outer_box.second, truth_pt + _delta_outer_pt, CompRECOtoPt());
0456 
0457     // if not outer_box, resort and return nothing
0458     if (outer_box.first == outer_box.second)
0459     {
0460       // there are no eta_matches in outerbox; resort mix_pair and return nothing
0461       std::sort(mix_pair.first, mix_pair.second, CompRECOtoPhi());
0462       return {{}, {}};
0463     }
0464 
0465     // now calculate the inner box -- this time sorting in reverse -- pT, then eta, then phi
0466     inner_box = outer_box;
0467     if (_delta_inner_pt > 0)
0468     {
0469       // start the inner pair -- the outerbox is already sorted by pT
0470       inner_box.first = std::lower_bound(inner_box.first,
0471                                          inner_box.second, truth_pt - _delta_inner_pt, CompRECOtoPt());
0472       inner_box.second = std::upper_bound(inner_box.first,
0473                                           inner_box.second, truth_pt + _delta_inner_pt, CompRECOtoPt());
0474     }
0475 
0476     // go back to sorted by eta
0477     std::sort(inner_box.first, inner_box.second, CompRECOtoEta());
0478   }
0479 
0480   // At this point we know that we have outer_box matches
0481   // Finish checking if there are any possible inner_box matches
0482 
0483   // check for inner_box eta matches
0484   if (inner_box.first != inner_box.second)
0485   {
0486     inner_box.first = std::lower_bound(inner_box.first, inner_box.second,
0487                                        truth_eta - m_same_deta, CompRECOtoEta());
0488 
0489     inner_box.second = std::lower_bound(inner_box.first, inner_box.second,
0490                                         truth_eta + m_same_deta, CompRECOtoEta());
0491   }
0492 
0493   // check for inner_box phi matches
0494   if (inner_box.first != inner_box.second)
0495   {
0496     std::sort(inner_box.first, inner_box.second, CompRECOtoPhi());
0497 
0498     inner_box.first = std::lower_bound(inner_box.first, inner_box.second,
0499                                        truth_phi - m_same_dphi, CompRECOtoPhi());
0500 
0501     inner_box.second = std::lower_bound(inner_box.first, inner_box.second,
0502                                         truth_phi + m_same_dphi, CompRECOtoPhi());
0503   }
0504 
0505   // At this point there are definitely outer_box matches, and
0506   // possible inner_box matches.
0507   // Return all these possible matches, evaluating all inner_box_matches
0508   std::vector<unsigned short> inner_box_matches;
0509   std::vector<unsigned short> outer_box_matches;
0510 
0511   // fill inner_box_matches
0512   for (auto iter = inner_box.first; iter != inner_box.second; ++iter)
0513   {
0514     inner_box_matches.push_back(std::get<RECOid>(*iter));
0515   }
0516   // fill inner_box_matches
0517   for (auto iter = outer_box.first; iter != inner_box.first; ++iter)
0518   {
0519     outer_box_matches.push_back(std::get<RECOid>(*iter));
0520   }
0521   for (auto iter = inner_box.second; iter != outer_box.second; ++iter)
0522   {
0523     outer_box_matches.push_back(std::get<RECOid>(*iter));
0524   }
0525 
0526   // resort recoData back to phi order
0527   std::sort(mix_pair.first, mix_pair.second, CompRECOtoPhi());
0528 
0529   // return the box matches
0530   return std::make_pair(inner_box_matches, outer_box_matches);
0531 }
0532 
0533 void TruthRecoTrackMatching::match_tracks_in_box(
0534     std::vector<std::pair<unsigned short, unsigned short>>& box_pairs  // possible matches by proximity in eta, phi, pT
0535 )
0536 {
0537   if (box_pairs.empty())
0538   {
0539     return;
0540   }
0541 
0542   std::sort(box_pairs.begin(), box_pairs.end());  // sorted by first index_true, then id_reco
0543   std::vector<PossibleMatch> poss_matches;
0544 
0545   auto ipair = box_pairs.begin();  // save current examined pair for sorting/unsorting purposes
0546   while (ipair != box_pairs.end())
0547   {
0548     auto id_true = ipair->first;
0549 
0550     // See if this track already has the maximum number of matches. If it does, then skip all pairs using this truth-track-index;
0551     if (at_nmax_index_true(id_true))
0552     {
0553       while (ipair != box_pairs.end())
0554       {
0555         ++ipair;
0556         if (ipair->first != id_true)
0557         {
0558           break;
0559         }
0560       }
0561       continue;
0562     }
0563 
0564     // make all possible reco matches matched for this track
0565     /* std::map<TrkrDefs::hitsetkey,TrkrDefs::cluskey> truth_keys; */
0566     m_TCEval.addClusKeys(m_TrkrTruthTrackContainer->getTruthTrack(id_true));
0567     // add the truth keys into the track counter
0568     /* auto truth_track = m_TrkrTruthTrackContainer->getTruthTrack(id_true); */
0569     /* for (auto& key : truth_track->getClusters()) { */
0570     /*   truth_keys[TrkrDefs::getHitSetKeyFromClusKey(key)] = key; */
0571     /* } */
0572     /* unsigned short nclus_true = truth_keys.size(); */
0573 
0574     while (ipair != box_pairs.end())
0575     {  // Loop over all possible matches only for this index_true
0576       //(subsequent true tracks will be caught on following loops)
0577       if (ipair->first != id_true)
0578       {
0579         break;  // never true on first iteration
0580       }
0581       if (at_nmax_id_reco(ipair->second))
0582       {
0583         ++ipair;
0584         continue;
0585       }  // make sure the reco-track isn't alread matched
0586       // ok, make a possible match: compare the clusters in the truth track and the reco track
0587       SvtxTrack* reco_track = m_SvtxTrackMap->get(ipair->second);
0588       m_TCEval.addClusKeys(reco_track);
0589       m_TCEval.find_matches();
0590 
0591       /* unsigned short nclus_match   = 0; // fill in the comparison loop */
0592       /* unsigned short nclus_nomatch = 0; // number of reco and truth cluster
0593        *             // that share a hitsetkey, but still fair matching criteria */
0594       /* unsigned short nclus_reco    = 0; // count in the comparison loop */
0595       // do the comparison of the tracks
0596       /* for (auto reco_ckey : G4Eval::ClusKeyIter(reco_track)) { */
0597       /*   ++nclus_reco; */
0598       /*   auto hitsetkey = TrkrDefs::getHitSetKeyFromClusKey(reco_ckey); */
0599       /*   if (truth_keys.count(hitsetkey) != 0) { */
0600       /*     // reco and truth cluster are in same hitsetkey-indexed subsurface. */
0601       /*     // See if they match (++nclus_match) or not (++nclus_nomatch) */
0602       /*     if (m_cluster_comp(truth_keys[hitsetkey], reco_ckey).first) { */
0603       /*       ++nclus_match; */
0604       /*     } else { */
0605       /*       ++nclus_nomatch; */
0606       /*     } */
0607       /*   } */
0608       /* } */
0609       unsigned short nclus_match = m_TCEval.phg4_n_matched();
0610       unsigned short nclus_true = m_TCEval.phg4_nclus();
0611       unsigned short nclus_reco = m_TCEval.svtx_nclus();
0612       unsigned short nclus_nomatch = (int) (m_TCEval.svtx_keys.size() - nclus_match);
0613 
0614       if (Verbosity() > 100)
0615       {
0616         auto* truth_track = m_TrkrTruthTrackContainer->getTruthTrack(id_true);
0617         std::cout << std::format(
0618                          "possmatch:(phi,eta,pT:id) true({:5.2},{:5.2},{:4.2}:{:2}) reco({:5.2},{:5.2},{:4.2}:{:2}) nCl(match:true:reco:nomatch)({:2}-{:2}-{:2}-{:2})",
0619                          truth_track->getPhi(), truth_track->getPseudoRapidity(), truth_track->getPt(),
0620                          truth_track->getTrackid(), reco_track->get_phi(), reco_track->get_eta(), reco_track->get_pt(),
0621                          ipair->second, nclus_match, nclus_true, nclus_reco, nclus_nomatch)
0622                   << std::endl;
0623       }
0624       if (nclus_match >= m_nmincluster_match && (static_cast<float>(nclus_match) / nclus_true >= m_nmincluster_ratio))
0625       {
0626         poss_matches.push_back(
0627             {nclus_match, nclus_true, nclus_reco,
0628              ipair->first, ipair->second});
0629       }
0630       ++ipair;
0631     }
0632   }
0633 
0634   // add all possible matches started for the largest PM_nmatch (the top)
0635   //  for groups of ties of PM_nmatch, sort by PM_ntrue (from smallest)
0636   //    for groups of ties (PM_nmatch, PM_ntrue), go by smallest PM_nreco
0637   //       for groups of ties (PM_nmatch, PM_ntrue, PM_nreco), do a detailed sum diff in the deltas
0638   std::sort(poss_matches.begin(), poss_matches.end(), SortPossibleMatch());
0639 
0640   if (Verbosity() > 200)
0641   {
0642     std::cout << " All chosen possible matches (" << poss_matches.size() << ") track pairs  (nClMatched-nClTrue-nClReco : idTrue-idReco)" << std::endl;
0643     int i{0};
0644     for (auto match : poss_matches)
0645     {
0646       /* auto truth_track = m_TrkrTruthTrackContainer->getTruthTracks()[std::get<PM_idtrue>(match)]; */
0647       /* auto index_trut = truth_track->getTrackid(); */
0648       std::cout << std::format(" pair({:2}):  {:2}-{:2}-{:2}-<{:2}>-{:2} ", (i++), std::get<PM_nmatch>(match),
0649                                std::get<PM_ntrue>(match), std::get<PM_nreco>(match), std::get<PM_idtrue>(match),
0650                                std::get<PM_idreco>(match))
0651                 << std::endl;
0652     }
0653   }
0654 
0655   /* std::set<int> matched_idreco, matched_idtrue; */
0656   auto iter = poss_matches.begin();
0657   while (iter != poss_matches.end())
0658   {
0659     if (skip_match(*iter))
0660     {
0661       ++iter;
0662       continue;
0663     }
0664     std::vector<std::pair<float, int>> sigma_metric = {{0., 0}};
0665     int n_sigma = 0;
0666     auto iter_same = iter + 1;  // iterator to look forward from first point and get detailed comparisons for all possibly matched tracks
0667     while (
0668         iter_same != poss_matches.end() && (*iter_same)[PM_nmatch] == (*iter)[PM_nmatch] && (*iter_same)[PM_ntrue] == (*iter)[PM_ntrue] && (*iter_same)[PM_nreco] == (*iter)[PM_nreco])
0669     {
0670       ++n_sigma;
0671       if (n_sigma == 1)
0672       {
0673         sigma_metric[0].first = sigma_CompMatchClusters(*iter);
0674       }
0675       sigma_metric.emplace_back(sigma_CompMatchClusters(*iter_same), n_sigma);
0676       ++iter_same;
0677     }
0678     std::sort(sigma_metric.begin(), sigma_metric.end());
0679 
0680     bool first = true;
0681     for (auto& sigma : sigma_metric)
0682     {
0683       if (first)
0684       {
0685         first = false;
0686       }
0687       else if (skip_match(*(iter + sigma.second)))
0688       {
0689         continue;
0690       }
0691       auto match = *(iter + sigma.second);
0692       auto id_true = match[PM_idtrue];
0693       auto id_reco = match[PM_idreco];
0694       auto* save_match = new EmbRecoMatchv1(id_true, id_reco,
0695                                             match[PM_ntrue], match[PM_nreco], match[PM_nmatch]);
0696       m_EmbRecoMatchContainer->addMatch(save_match);
0697 
0698       if (!m_nmatched_index_true.contains(id_true))
0699       {
0700         m_nmatched_index_true[id_true] = 1;
0701       }
0702       else
0703       {
0704         m_nmatched_index_true[id_true] += 1;
0705       }
0706     }
0707     iter += sigma_metric.size();
0708   }
0709 }
0710 
0711 // ----------------------------------------
0712 // convenience function
0713 // ----------------------------------------
0714 inline float TruthRecoTrackMatching::abs_dphi(float phi0, float phi1)
0715 {
0716   float dphi = std::abs(phi0 - phi1);
0717   while (dphi > M_PI)
0718   {
0719     dphi = std::abs(dphi - 2 * M_PI);
0720   }
0721   return dphi;
0722 }
0723 
0724 float TruthRecoTrackMatching::sigma_CompMatchClusters(PossibleMatch& match)
0725 {
0726   auto id_true = match[PM_idtrue];
0727   auto id_reco = match[PM_idreco];
0728 
0729   m_TCEval.addClusKeys(m_TrkrTruthTrackContainer->getTruthTrack(id_true));
0730   m_TCEval.addClusKeys(m_SvtxTrackMap->get(id_reco));
0731 
0732   m_TCEval.find_matches();
0733 
0734   int n_matched = m_TCEval.phg4_n_matched();
0735 
0736   if (n_matched)
0737   {
0738     return std::numeric_limits<float>::max();
0739   }
0740 
0741   return m_TCEval.match_stat / n_matched;
0742 }
0743 /* auto truth_track = m_TrkrTruthTrackContainer->getTruthTrack(id_true); */
0744 /* if (!truth_track) return std::numeric_limits<float>::max(); */
0745 /* std::map<TrkrDefs::hitsetkey,TrkrDefs::cluskey> truth_keys; // truth cluster keys */
0746 /* for (auto& key : truth_track->getClusters()) */
0747 /*   truth_keys[TrkrDefs::getHitSetKeyFromClusKey(key)] = key; */
0748 
0749 /* SvtxTrack* reco_track = m_SvtxTrackMap->get(id_reco); */
0750 /* if (!reco_track) return std::numeric_limits<float>::max(); */
0751 
0752 /* auto tpcseed = reco_track->get_tpc_seed(); */
0753 /* if (!tpcseed)    return std::numeric_limits<float>::max(); */
0754 
0755 /* double n_matches = 0.; // get the mean match values */
0756 /* double sum_diff  = 0.; */
0757 
0758 /* for (auto reco_ckey : G4Eval::ClusKeyIter(reco_track)) { */
0759 /* auto hitsetkey = TrkrDefs::getHitSetKeyFromClusKey(reco_ckey); */
0760 /* if (truth_keys.count(hitsetkey) == 0) continue; */
0761 /* auto comp_val = m_cluster_comp(truth_keys[hitsetkey], reco_ckey); */
0762 
0763 /* if (comp_val.first) { */
0764 /* n_matches += 1.; */
0765 /* sum_diff += comp_val.second; */
0766 /* } */
0767 /* } */
0768 /* return sum_diff/n_matches; */
0769 /* } */
0770 
0771 inline bool TruthRecoTrackMatching::skip_match(PossibleMatch& match)
0772 {
0773   return at_nmax_id_reco(match[PM_idreco]) || at_nmax_index_true(match[PM_idreco]);
0774 }
0775 
0776 // ----------------------------------------------
0777 // functions for permissible matching pt
0778 // Currently not used, therefore tracks of any pT
0779 // will be compared against each other.
0780 // If wanted, will have to be updated with
0781 // same values in the future.
0782 // ----------------------------------------------
0783 float TruthRecoTrackMatching::delta_outer_pt(float pt) const
0784 {
0785   return -1. + pt * 0.;  // 10. + 0.01*pt;
0786 }
0787 float TruthRecoTrackMatching::delta_inner_pt(float pt) const
0788 {
0789   return -1. + pt * 0.;  // 10. + 0.01*pt;
0790 }
0791 
0792 bool TruthRecoTrackMatching::at_nmax_index_true(unsigned short index_true)
0793 {
0794   if (!m_nmatched_index_true.contains(index_true))
0795   {
0796     return false;
0797   }
0798   return m_nmatched_index_true[index_true] >= m_max_nreco_per_truth;
0799 }
0800 
0801 bool TruthRecoTrackMatching::at_nmax_id_reco(unsigned short id_reco)
0802 {
0803   if (!m_nmatched_id_reco->contains(id_reco))
0804   {
0805     return false;
0806   }
0807   return (*m_nmatched_id_reco)[id_reco] >= m_max_ntruth_per_reco;
0808 }
0809 
0810 void TruthRecoTrackMatching::set_diagnostic_file(const std::string& file_name)
0811 {
0812   m_write_diag = true;
0813   TFile* s_current = gDirectory->GetFile();
0814   m_diag_file = new TFile(file_name.c_str(), "recreate");
0815   // write out the cuts on this set of data
0816   m_diag_file->cd();
0817   m_diag_tree = new TTree("T", "Tree of Reco and True Clusters");
0818 
0819   m_diag_tree->Branch("event", &m_event);
0820 
0821   m_diag_tree->Branch("trkid_reco_matched", &m_trkid_reco_matched);
0822   m_diag_tree->Branch("itrk_reco_matched", &m_cnt_reco_matched);
0823   m_diag_tree->Branch("i0_reco_matched", &m_i0_reco_matched);
0824   m_diag_tree->Branch("i1_reco_matched", &m_i1_reco_matched);
0825   m_diag_tree->Branch("layer_reco_matched", &m_layer_reco_matched);
0826   m_diag_tree->Branch("x_reco_matched", &m_x_reco_matched);
0827   m_diag_tree->Branch("y_reco_matched", &m_y_reco_matched);
0828   m_diag_tree->Branch("z_reco_matched", &m_z_reco_matched);
0829 
0830   m_diag_tree->Branch("trkid_reco_notmatched", &m_trkid_reco_notmatched);
0831   m_diag_tree->Branch("itrk_reco_notmatched", &m_cnt_reco_notmatched);
0832   m_diag_tree->Branch("i0_reco_notmatched", &m_i0_reco_notmatched);
0833   m_diag_tree->Branch("i1_reco_notmatched", &m_i1_reco_notmatched);
0834   m_diag_tree->Branch("layer_reco_notmatched", &m_layer_reco_notmatched);
0835   m_diag_tree->Branch("x_reco_notmatched", &m_x_reco_notmatched);
0836   m_diag_tree->Branch("y_reco_notmatched", &m_y_reco_notmatched);
0837   m_diag_tree->Branch("z_reco_notmatched", &m_z_reco_notmatched);
0838 
0839   m_diag_tree->Branch("trkid_true_matched", &m_trkid_true_matched);
0840   m_diag_tree->Branch("itrk_true_matched", &m_cnt_true_matched);
0841   m_diag_tree->Branch("i0_true_matched", &m_i0_true_matched);
0842   m_diag_tree->Branch("i1_true_matched", &m_i1_true_matched);
0843   m_diag_tree->Branch("layer_true_matched", &m_layer_true_matched);
0844   m_diag_tree->Branch("x_true_matched", &m_x_true_matched);
0845   m_diag_tree->Branch("y_true_matched", &m_y_true_matched);
0846   m_diag_tree->Branch("z_true_matched", &m_z_true_matched);
0847 
0848   m_diag_tree->Branch("trkid_true_notmatched", &m_trkid_true_notmatched);
0849   m_diag_tree->Branch("itrk_true_notmatched", &m_cnt_true_notmatched);
0850   m_diag_tree->Branch("i0_true_notmatched", &m_i0_true_notmatched);
0851   m_diag_tree->Branch("i1_true_notmatched", &m_i1_true_notmatched);
0852   m_diag_tree->Branch("layer_true_notmatched", &m_layer_true_notmatched);
0853   m_diag_tree->Branch("x_true_notmatched", &m_x_true_notmatched);
0854   m_diag_tree->Branch("y_true_notmatched", &m_y_true_notmatched);
0855   m_diag_tree->Branch("z_true_notmatched", &m_z_true_notmatched);
0856 
0857   if (s_current != nullptr)
0858   {
0859     s_current->cd();
0860   }
0861 }
0862 
0863 void TruthRecoTrackMatching::clear_branch_vectors()
0864 {
0865   m_trkid_reco_matched.clear();
0866   m_i0_reco_matched.clear();
0867   m_i1_reco_matched.clear();
0868   m_layer_reco_matched.clear();
0869   m_x_reco_matched.clear();
0870   m_y_reco_matched.clear();
0871   m_z_reco_matched.clear();
0872 
0873   m_trkid_reco_notmatched.clear();
0874   m_i0_reco_notmatched.clear();
0875   m_i1_reco_notmatched.clear();
0876   m_layer_reco_notmatched.clear();
0877   m_x_reco_notmatched.clear();
0878   m_y_reco_notmatched.clear();
0879   m_z_reco_notmatched.clear();
0880 
0881   m_trkid_true_matched.clear();
0882   m_i0_true_matched.clear();
0883   m_i1_true_matched.clear();
0884   m_layer_true_matched.clear();
0885   m_x_true_matched.clear();
0886   m_y_true_matched.clear();
0887   m_z_true_matched.clear();
0888 
0889   m_trkid_true_notmatched.clear();
0890   m_i0_true_notmatched.clear();
0891   m_i1_true_notmatched.clear();
0892   m_layer_true_notmatched.clear();
0893   m_x_true_notmatched.clear();
0894   m_y_true_notmatched.clear();
0895   m_z_true_notmatched.clear();
0896 }
0897 
0898 void TruthRecoTrackMatching::fill_tree()
0899 {
0900   // fill clusters or un-matched truth tracks
0901   int cnt = 0;
0902   int itrk = 0;
0903   for (auto& trkid : m_EmbRecoMatchContainer->ids_TruthUnmatched())
0904   {
0905     m_trkid_true_notmatched.push_back(trkid);
0906     m_i0_true_notmatched.push_back(cnt);
0907     auto* track = m_TrkrTruthTrackContainer->getTruthTrack(trkid);
0908     for (auto& ckey : track->getClusters())
0909     {
0910       auto* cluster = m_TruthClusterContainer->findCluster(ckey);
0911       m_cnt_true_notmatched.push_back(itrk);
0912       Eigen::Vector3d gloc = m_ActsGeometry->getGlobalPosition(ckey, cluster);
0913       m_layer_true_notmatched.push_back(TrkrDefs::getLayer(ckey));
0914       m_x_true_notmatched.push_back(gloc[0]);
0915       m_y_true_notmatched.push_back(gloc[1]);
0916       m_z_true_notmatched.push_back(gloc[2]);
0917       ++cnt;
0918     }
0919     m_i1_true_notmatched.push_back(cnt);
0920     ++itrk;
0921   }
0922 
0923   // fill clusters of matched truth tracks
0924   cnt = 0;
0925   itrk = 0;
0926   for (auto& trkid : m_EmbRecoMatchContainer->ids_TruthMatched())
0927   {
0928     m_trkid_true_matched.push_back(trkid);
0929     m_i0_true_matched.push_back(cnt);
0930     auto* track = m_TrkrTruthTrackContainer->getTruthTrack(trkid);
0931     for (auto& ckey : track->getClusters())
0932     {
0933       auto* cluster = m_TruthClusterContainer->findCluster(ckey);
0934       m_cnt_true_matched.push_back(itrk);
0935       Eigen::Vector3d gloc = m_ActsGeometry->getGlobalPosition(ckey, cluster);
0936       m_layer_true_matched.push_back(TrkrDefs::getLayer(ckey));
0937       m_x_true_matched.push_back(gloc[0]);
0938       m_y_true_matched.push_back(gloc[1]);
0939       m_z_true_matched.push_back(gloc[2]);
0940       ++cnt;
0941     }
0942     m_i1_true_matched.push_back(cnt);
0943     ++itrk;
0944   }
0945 
0946   // fill clusters of matched reco tracks
0947   std::set<unsigned int> set_reco_matched;
0948   cnt = 0;
0949   itrk = 0;
0950   for (auto& trkid : m_EmbRecoMatchContainer->ids_RecoMatched())
0951   {
0952     set_reco_matched.insert(trkid);
0953     m_trkid_reco_matched.push_back(trkid);
0954     SvtxTrack* reco_track = m_SvtxTrackMap->get(trkid);
0955     m_i0_reco_matched.push_back(cnt);
0956 
0957     for (auto reco_ckey : ClusKeyIter(reco_track))
0958     {
0959       auto* cluster = m_RecoClusterContainer->findCluster(reco_ckey);
0960       Eigen::Vector3d gloc = m_ActsGeometry->getGlobalPosition(reco_ckey, cluster);
0961       m_layer_reco_matched.push_back(TrkrDefs::getLayer(reco_ckey));
0962       m_cnt_reco_matched.push_back(itrk);
0963       m_x_reco_matched.push_back(gloc[0]);
0964       m_y_reco_matched.push_back(gloc[1]);
0965       m_z_reco_matched.push_back(gloc[2]);
0966       ++cnt;
0967     }
0968     m_i1_reco_matched.push_back(cnt);
0969     ++itrk;
0970   }
0971 
0972   // fill clusters of not matched reco tracks
0973   cnt = 0;
0974   itrk = 0;
0975   for (auto& reco : *m_SvtxTrackMap)
0976   {
0977     auto trkid = reco.first;
0978     if (set_reco_matched.contains(trkid))
0979     {
0980       continue;
0981     }
0982     m_trkid_reco_notmatched.push_back(trkid);
0983     SvtxTrack* reco_track = m_SvtxTrackMap->get(trkid);
0984     m_i0_reco_notmatched.push_back(cnt);
0985     for (auto reco_ckey : ClusKeyIter(reco_track))
0986     {
0987       auto* cluster = m_RecoClusterContainer->findCluster(reco_ckey);
0988       Eigen::Vector3d gloc = m_ActsGeometry->getGlobalPosition(reco_ckey, cluster);
0989       m_layer_reco_notmatched.push_back(TrkrDefs::getLayer(reco_ckey));
0990       m_cnt_reco_notmatched.push_back(itrk);
0991       m_x_reco_notmatched.push_back(gloc[0]);
0992       m_y_reco_notmatched.push_back(gloc[1]);
0993       m_z_reco_notmatched.push_back(gloc[2]);
0994       ++cnt;
0995     }
0996     m_i1_reco_notmatched.push_back(cnt);
0997     ++itrk;
0998   }
0999   ++m_event;
1000   m_diag_tree->Fill();
1001   clear_branch_vectors();
1002   return;
1003 }