Back to home page

sPhenix code displayed by LXR

 
 

    


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

0001 #include "PHSimpleVertexFinder.h"
0002 
0003 /// Tracking includes
0004 
0005 #include <trackbase/TrackVertexCrossingAssoc_v1.h>
0006 #include <trackbase/TrkrCluster.h>  // for TrkrCluster
0007 #include <trackbase/TrkrClusterContainer.h>
0008 #include <trackbase/TrkrDefs.h>            // for cluskey, getLayer, TrkrId
0009 #include <trackbase/TrackFitUtils.h>
0010 #include <trackbase_historic/SvtxTrack.h>  // for SvtxTrack, SvtxTrack::C...
0011 #include <trackbase_historic/SvtxTrackMap_v2.h>
0012 
0013 #include <globalvertex/SvtxVertexMap_v1.h>
0014 #include <globalvertex/SvtxVertex_v2.h>
0015 
0016 #include <fun4all/Fun4AllReturnCodes.h>
0017 #include <phool/PHCompositeNode.h>
0018 #include <phool/PHNode.h>
0019 #include <phool/PHNodeIterator.h>
0020 
0021 #include <phool/getClass.h>
0022 #include <phool/phool.h>
0023 
0024 #include <Acts/Surfaces/PerigeeSurface.hpp>
0025 
0026 #include <cmath>  // for sqrt, fabs, atan2, cos
0027 #include <iomanip>
0028 #include <iostream>  // for operator<<, basic_ostream
0029 #include <map>       // for map
0030 #include <set>       // for _Rb_tree_const_iterator
0031 #include <utility>   // for pair, make_pair
0032 
0033 #include <algorithm>
0034 #include <cassert>
0035 #include <functional>
0036 #include <numeric>
0037 #include <vector>
0038 
0039 #include <Eigen/Dense>
0040 
0041 //____________________________________________________________________________..
0042 PHSimpleVertexFinder::PHSimpleVertexFinder(const std::string &name)
0043   : SubsysReco(name)
0044 {
0045 }
0046 
0047 //____________________________________________________________________________..
0048 int PHSimpleVertexFinder::InitRun(PHCompositeNode *topNode)
0049 {
0050   if (Verbosity() > 0)
0051   {
0052     std::cout << __PRETTY_FUNCTION__ << " is pp mode? " << _pp_mode << std::endl;
0053   }
0054 
0055   int ret = GetNodes(topNode);
0056   if (ret != Fun4AllReturnCodes::EVENT_OK)
0057   {
0058     return ret;
0059   }
0060   ret = CreateNodes(topNode);
0061   if (ret != Fun4AllReturnCodes::EVENT_OK)
0062   {
0063     return ret;
0064   }
0065   return ret;
0066 }
0067 
0068 //____________________________________________________________________________..
0069 int PHSimpleVertexFinder::process_event(PHCompositeNode * /*topNode*/)
0070 {
0071   if (Verbosity() > 0)
0072   {
0073     std::cout << PHWHERE << " track map size " << _track_map->size() << std::endl;
0074   }
0075 
0076   _active_dcacut = _base_dcacut;
0077 
0078   // in case these objects are in the input file, we clear the nodes and replace them
0079     _svtx_vertex_map->Reset();
0080     _track_vertex_crossing_map->Reset();
0081 
0082   // Write to a new map on the node tree that contains (crossing, trackid) pairs for all tracks
0083   // Later, will add to it a map  containing (crossing, vertexid)
0084 
0085   std::set<short int> crossings;
0086   for (const auto &[trackkey, track] : *_track_map)
0087   {
0088     auto crossing = track->get_crossing();
0089 
0090     if (Verbosity() > 0)
0091       {
0092     std::cout << "track id " << trackkey << " crossing " << crossing 
0093           << " x y z " << track->get_x() << "  " << track->get_y() << "  " << track->get_z() << std::endl;
0094       }
0095 
0096     // crossing zero contains unmatched TPC tracks
0097     // Here we skip those crossing = zero tracks that do not have silicon seeds
0098     if (_pp_mode)
0099     {
0100       auto *siseed = track->get_silicon_seed();
0101       if (crossing == 0 && !siseed)
0102       {
0103       continue;
0104       }
0105     }
0106     
0107     crossings.insert(crossing);
0108     _track_vertex_crossing_map->addTrackAssoc(crossing, trackkey);    
0109   }
0110   
0111   unsigned int vertex_id = 0;
0112 
0113   for (auto cross : crossings)
0114   {
0115     // reset maps for each crossing
0116     _vertex_track_map.clear();
0117     _track_pair_map.clear();
0118     _track_pair_pca_map.clear();
0119     _vertex_position_map.clear();
0120     _vertex_covariance_map.clear();
0121     _vertex_set.clear();
0122 
0123     if (Verbosity() > 0)
0124     {
0125       std::cout << "process tracks for beam crossing " << cross << std::endl;
0126     }
0127 
0128     // get the subset of tracks for this crossing
0129     auto crossing_track_index = _track_vertex_crossing_map->getTracks(cross);
0130     SvtxTrackMap *crossing_tracks = new SvtxTrackMap_v2;
0131     for (auto iter = crossing_track_index.first; iter != crossing_track_index.second; ++iter)
0132     {
0133       unsigned int trackkey = (*iter).second;
0134       SvtxTrack *track = _track_map->get(trackkey);
0135       if(!track)
0136       {
0137         continue;
0138       }
0139       crossing_tracks->insertWithKey(track, trackkey);
0140     }
0141 
0142     // Find all instances where two tracks have a dca of < _dcacut,  and capture the pair details
0143     // Fills _track_pair_map and _track_pair_pca_map
0144     if(_zero_field)
0145       {
0146     checkDCAsZF(crossing_tracks);
0147       }
0148     else
0149       {
0150     checkDCAs(crossing_tracks);
0151       }
0152 
0153     /// If we didn't find any matches, try again with a slightly larger DCA cut
0154     if (_track_pair_map.empty())
0155     {
0156       _active_dcacut = 3.0 * _base_dcacut;
0157       if(_zero_field)
0158     {
0159       checkDCAsZF(crossing_tracks);
0160     }
0161       else
0162     {
0163       checkDCAs(crossing_tracks);
0164     }
0165     }
0166     
0167     if (Verbosity() > 0)
0168     {
0169       std::cout << "crossing " << cross << " track pair map size " << _track_pair_map.size() << std::endl;
0170     }
0171 
0172     // get all connected pairs of tracks by looping over the track_pair map
0173     std::vector<std::set<unsigned int>> connected_tracks = findConnectedTracks();
0174 
0175     // we want the biggest vertex first, sort the vector of connected track sets by size
0176     for (unsigned int ivtx = 0; ivtx < connected_tracks.size(); ++ivtx)
0177       {
0178     bool isdone = true; 
0179     for (unsigned int j = 0; j < connected_tracks.size() - ivtx - 1; j++) 
0180       { 
0181         if (connected_tracks[j].size() < connected_tracks[j + 1].size())
0182           {   
0183         swap(connected_tracks[j], connected_tracks[j + 1]);
0184         isdone = false;
0185           }
0186 
0187         if(isdone)
0188         {
0189           break;
0190         }
0191       }
0192       }
0193     
0194     // make vertices - each set of connected tracks is a vertex
0195     for (unsigned int ivtx = 0; ivtx < connected_tracks.size(); ++ivtx)
0196     {
0197       if (Verbosity() > 0)
0198       {
0199         std::cout << "process vertex " << ivtx + vertex_id << std::endl;
0200       }
0201 
0202       for (auto it : connected_tracks[ivtx])
0203       {
0204         unsigned int id = it;
0205         _vertex_track_map.insert(std::make_pair(ivtx, id));
0206         if (Verbosity() > 0)
0207         {
0208           std::cout << "  adding track " << id << " to vertex " << ivtx + vertex_id << std::endl;
0209         }
0210       }
0211     }
0212 
0213     // make a list of vertices
0214     for (auto it : _vertex_track_map)
0215     {
0216       if (Verbosity() > 1)
0217       {
0218         std::cout << " vertex " << it.first + vertex_id << " track " << it.second << std::endl;
0219       }
0220       _vertex_set.insert(it.first);
0221     }
0222 
0223     // this finds average vertex positions after removal of outlying track pairs
0224     removeOutlierTrackPairs();
0225 
0226     // average covariance for accepted tracks
0227     for (auto it : _vertex_set)
0228     {
0229       matrix_t avgCov = matrix_t::Zero();
0230       double cov_wt = 0.0;
0231 
0232       auto ret = _vertex_track_map.equal_range(it);
0233       for (auto cit = ret.first; cit != ret.second; ++cit)
0234       {
0235         unsigned int trid = cit->second;
0236         matrix_t cov;
0237         auto *track = _track_map->get(trid);
0238         for (int i = 0; i < 3; ++i)
0239         {
0240           for (int j = 0; j < 3; ++j)
0241           {
0242             cov(i, j) = track->get_error(i, j);
0243           }
0244         }
0245 
0246         avgCov += cov;
0247         cov_wt++;
0248       }
0249 
0250       avgCov /= sqrt(cov_wt);
0251       if (Verbosity() > 2)
0252       {
0253         std::cout << "Average covariance for vertex " << it << " is:" << std::endl;
0254         std::cout << std::setprecision(8) << avgCov << std::endl;
0255       }
0256       _vertex_covariance_map.insert(std::make_pair(it, avgCov));
0257     }
0258 
0259     // Write the vertices to the vertex map on the node tree
0260     //==============================================
0261 
0262     for (auto it : _vertex_set)
0263     {
0264       unsigned int thisid = it + vertex_id;  // the address of the vertex in the event
0265 
0266       auto svtxVertex = std::make_unique<SvtxVertex_v2>();
0267 
0268       svtxVertex->set_chisq(0.0);
0269       svtxVertex->set_ndof(0);
0270       svtxVertex->set_t0(0);
0271       svtxVertex->set_id(thisid);
0272       svtxVertex->set_beam_crossing(cross);
0273 
0274       auto ret = _vertex_track_map.equal_range(it);
0275       for (auto cit = ret.first; cit != ret.second; ++cit)
0276       {
0277         unsigned int trid = cit->second;
0278 
0279     if (Verbosity() > 1)
0280         {
0281           std::cout << "   vertex " << thisid << " insert track " << trid << std::endl;
0282       SvtxTrack *track = _track_map->get(trid);
0283       if (track)
0284         {
0285           auto *siseed = track->get_silicon_seed();
0286           short int intt_crossing = siseed->get_crossing();
0287           std::cout << " vtxid " << thisid << " vertex crossing " << cross
0288             << " track crossing " << cross
0289             << " intt crossing " << intt_crossing
0290             << " trackID " << trid
0291             << " track Z " << track->get_z()
0292             << " X " << track->get_x()
0293             << " Y " << track->get_y()
0294             << " quality " << track->get_quality()
0295             << " pt " << track->get_pt()
0296             << std::endl;
0297         }
0298         }
0299         svtxVertex->insert_track(trid);
0300         _track_map->get(trid)->set_vertex_id(thisid);
0301       }
0302 
0303       Eigen::Vector3d pos = _vertex_position_map.find(it)->second;
0304       svtxVertex->set_x(pos.x());
0305       svtxVertex->set_y(pos.y());
0306       svtxVertex->set_z(pos.z());
0307       if (Verbosity() > 1)
0308       {
0309         std::cout << "   vertex " << thisid << " insert pos.x " << pos.x() << " pos.y " << pos.y() << " pos.z " << pos.z() << std::endl;
0310       }
0311 
0312       auto vtxCov = _vertex_covariance_map.find(it)->second;
0313       for (int i = 0; i < 3; ++i)
0314       {
0315         for (int j = 0; j < 3; ++j)
0316         {
0317           svtxVertex->set_error(i, j, vtxCov(i, j));
0318         }
0319       }
0320 
0321       _svtx_vertex_map->insert(svtxVertex.release());
0322     }
0323 
0324     vertex_id += _vertex_set.size();
0325 
0326     /// Iterate through the tracks and assign the closest vtx id to
0327     /// the track position for propagating back to the vtx. Catches any
0328     /// tracks that were missed or were not  compatible with any of the
0329     /// identified vertices
0330     //=================================================
0331     for (const auto &[trackkey, track] : *crossing_tracks)
0332     {
0333       auto *thistrack = _track_map->get(trackkey);  // get the original, not the copy
0334       auto vtxid = thistrack->get_vertex_id();
0335       if (Verbosity() > 1)
0336       {
0337         std::cout << "        track " << trackkey << " track vtxid " << vtxid << std::endl;
0338       }
0339 
0340       /// If there is a vertex already assigned, keep going
0341       if (vtxid != std::numeric_limits<unsigned int>::max())
0342       {
0343         continue;
0344       }
0345 
0346       float maxdz = std::numeric_limits<float>::max();
0347       unsigned int newvtxid = std::numeric_limits<unsigned int>::max();
0348 
0349       for (auto it : _vertex_set)
0350       {
0351         unsigned int thisid = it + vertex_id - _vertex_set.size();
0352 
0353         if (Verbosity() > 1)
0354         {
0355           std::cout << "                test vertex " << thisid << std::endl;
0356         }
0357 
0358         auto *thisvertex = _svtx_vertex_map->get(thisid);
0359         float dz = thistrack->get_z() - thisvertex->get_z();
0360         if (std::abs(dz) < maxdz)
0361         {
0362           maxdz = dz;
0363           newvtxid = thisid;
0364         }
0365       }
0366 
0367       // this updates the track, but does not add it to the vertex primary track list
0368       if (newvtxid != std::numeric_limits<unsigned int>::max())
0369       {
0370         thistrack->set_vertex_id(newvtxid);
0371         if (Verbosity() > 1)
0372         {
0373           std::cout << "                assign vertex " << newvtxid << " to additional track " << trackkey << std::endl;
0374         }
0375       }
0376     }
0377 
0378     delete crossing_tracks;
0379 
0380   }  // end loop over crossings
0381 
0382   // update the crossing vertex map with the results
0383   for (const auto &[vtxkey, vertex] : *_svtx_vertex_map)
0384   {
0385     short int crossing = vertex->get_beam_crossing();
0386     _track_vertex_crossing_map->addVertexAssoc(crossing, vtxkey);
0387 
0388     if (Verbosity() > 1)
0389     {
0390       std::cout << "Vertex ID: " << vtxkey << " vertex crossing " << crossing << " list of tracks: " << std::endl;
0391       for (auto trackiter = vertex->begin_tracks(); trackiter != vertex->end_tracks(); ++trackiter)
0392       {
0393         SvtxTrack *track = _track_map->get(*trackiter);
0394         if (!track)
0395         {
0396           continue;
0397         }
0398 
0399         auto *siseed = track->get_silicon_seed();
0400         short int intt_crossing = siseed->get_crossing();
0401 
0402         // the track crossing may be from the INTT clusters or from geometric matching if there are no INTT clusters
0403         short int track_crossing = track->get_crossing();
0404         std::cout << " vtxid " << vtxkey << " vertex crossing " << crossing
0405                   << " track crossing " << track_crossing
0406                   << " intt crossing " << intt_crossing
0407                   << " trackID " << *trackiter
0408                   << " track Z " << track->get_z()
0409                   << " X " << track->get_x()
0410                   << " Y " << track->get_y()
0411                   << " quality " << track->get_quality()
0412                   << " pt " << track->get_pt()
0413                   << std::endl;
0414         if (Verbosity() > 3)
0415         {
0416           siseed->identify();
0417         }
0418       }
0419     }
0420   }
0421 
0422   if (Verbosity() > 2)
0423   {
0424     _track_vertex_crossing_map->identify();
0425   }
0426 
0427   return Fun4AllReturnCodes::EVENT_OK;
0428 }
0429 
0430 int PHSimpleVertexFinder::End(PHCompositeNode * /*topNode*/)
0431 {
0432   return Fun4AllReturnCodes::EVENT_OK;
0433 }
0434 
0435 int PHSimpleVertexFinder::CreateNodes(PHCompositeNode *topNode)
0436 {
0437   PHNodeIterator iter(topNode);
0438 
0439   PHCompositeNode *dstNode = dynamic_cast<PHCompositeNode *>(iter.findFirst("PHCompositeNode", "DST"));
0440 
0441   /// Check that it is there
0442   if (!dstNode)
0443   {
0444     std::cerr << "DST Node missing, quitting" << std::endl;
0445     throw std::runtime_error("failed to find DST node in PHActsInitialVertexFinder::createNodes");
0446   }
0447 
0448   /// Get the tracking subnode
0449   PHCompositeNode *svtxNode = dynamic_cast<PHCompositeNode *>(iter.findFirst("PHCompositeNode", "SVTX"));
0450 
0451   if (!svtxNode)
0452   {
0453     svtxNode = new PHCompositeNode("SVTX");
0454     dstNode->addNode(svtxNode);
0455   }
0456 
0457   _svtx_vertex_map = findNode::getClass<SvtxVertexMap>(topNode, _vertex_map_name);
0458 
0459   if (!_svtx_vertex_map)
0460   {
0461     _svtx_vertex_map = new SvtxVertexMap_v1;
0462     PHIODataNode<PHObject> *vertexNode = new PHIODataNode<PHObject>(
0463         _svtx_vertex_map, _vertex_map_name, "PHObject");
0464 
0465     svtxNode->addNode(vertexNode);
0466   }
0467 
0468   _track_vertex_crossing_map = findNode::getClass<TrackVertexCrossingAssoc>(topNode, "TrackVertexCrossingAssocMap");
0469   if (!_track_vertex_crossing_map)
0470   {
0471     _track_vertex_crossing_map = new TrackVertexCrossingAssoc_v1;
0472     PHIODataNode<PHObject> *trackvertexNode = new PHIODataNode<PHObject>(
0473         _track_vertex_crossing_map, "TrackVertexCrossingAssocMap", "PHObject");
0474 
0475     svtxNode->addNode(trackvertexNode);
0476   }
0477 
0478   return Fun4AllReturnCodes::EVENT_OK;
0479 }
0480 int PHSimpleVertexFinder::GetNodes(PHCompositeNode *topNode)
0481 {
0482   _track_map = findNode::getClass<SvtxTrackMap>(topNode, _track_map_name);
0483   if (!_track_map)
0484   {
0485     std::cout << PHWHERE << " ERROR: Can't find SvtxTrackMap: " << _track_map_name << std::endl;
0486     return Fun4AllReturnCodes::ABORTEVENT;
0487   }
0488 
0489 
0490   _cluster_map = findNode::getClass<TrkrClusterContainer>(topNode, m_clusterContainerName);
0491   if (!_cluster_map)
0492   {
0493     std::cout << PHWHERE << " ERROR: Can't find node TRKR_CLUSTER container" << std::endl;
0494     return Fun4AllReturnCodes::ABORTEVENT;
0495   }
0496 
0497   _tGeometry = findNode::getClass<ActsGeometry>(topNode, "ActsGeometry");
0498   if (!_tGeometry)
0499   {
0500     std::cout << PHWHERE << "Error, can't find acts tracking geometry" << std::endl;
0501     return Fun4AllReturnCodes::ABORTEVENT;
0502   }
0503 
0504   return Fun4AllReturnCodes::EVENT_OK;
0505 }
0506 
0507 void PHSimpleVertexFinder::checkDCAs(SvtxTrackMap *track_map)
0508 {
0509   // Loop over tracks and check for close DCA match with all other tracks
0510   for (auto tr1_it = track_map->begin(); tr1_it != track_map->end(); ++tr1_it)
0511   {
0512     auto id1 = tr1_it->first;
0513     auto *tr1 = tr1_it->second;
0514     if (tr1->get_quality() > _qual_cut)
0515     {
0516       continue;
0517     }
0518     if (_require_mvtx)
0519     {
0520       unsigned int nmvtx = 0;
0521       TrackSeed *siliconseed = tr1->get_silicon_seed();
0522       if (!siliconseed)
0523       {
0524         continue;
0525       }
0526 
0527       for (auto clusit = siliconseed->begin_cluster_keys(); clusit != siliconseed->end_cluster_keys(); ++clusit)
0528       {
0529         if (TrkrDefs::getTrkrId(*clusit) == TrkrDefs::mvtxId)
0530         {
0531           nmvtx++;
0532         }
0533         if (nmvtx >= _nmvtx_required)
0534         {
0535           break;
0536         }
0537       }
0538       if (nmvtx < _nmvtx_required)
0539       {
0540         continue;
0541       }
0542       if (Verbosity() > 3)
0543       {
0544         std::cout << " tr1 id " << id1 << " has nmvtx at least " << nmvtx << std::endl;
0545       }
0546     }
0547 
0548     // look for close DCA matches with all other such tracks
0549     for (auto tr2_it = std::next(tr1_it); tr2_it != track_map->end(); ++tr2_it)
0550     {
0551       auto id2 = tr2_it->first;
0552       auto *tr2 = tr2_it->second;
0553       if (tr2->get_quality() > _qual_cut)
0554       {
0555         continue;
0556       }
0557       if (_require_mvtx)
0558       {
0559         unsigned int nmvtx = 0;
0560         TrackSeed *siliconseed = tr2->get_silicon_seed();
0561         if (!siliconseed)
0562         {
0563           continue;
0564         }
0565 
0566         for (auto clusit = siliconseed->begin_cluster_keys(); clusit != siliconseed->end_cluster_keys(); ++clusit)
0567         {
0568           if (TrkrDefs::getTrkrId(*clusit) == TrkrDefs::mvtxId)
0569           {
0570             nmvtx++;
0571           }
0572           if (nmvtx >= _nmvtx_required)
0573           {
0574             break;
0575           }
0576         }
0577         if (nmvtx < _nmvtx_required)
0578         {
0579           continue;
0580         }
0581         if (Verbosity() > 3)
0582         {
0583           std::cout << " tr2 id " << id2 << " has nmvtx at least " << nmvtx << std::endl;
0584         }
0585       }
0586 
0587       // find DCA of these two tracks
0588       if (Verbosity() > 3)
0589       {
0590         std::cout << "Check DCA for tracks " << id1 << " and  " << id2 << std::endl;
0591       }
0592 
0593       findDcaTwoTracks(tr1, tr2);
0594     }
0595   }
0596 }
0597 
0598 void PHSimpleVertexFinder::checkDCAsZF(SvtxTrackMap *track_map)
0599 {
0600   // ZF tracks do not have an Acts fit, and the seeding does not give
0601   // reliable track parameters - refit clusters with straight lines
0602   // No distortion corrections applied in TPC at present
0603 
0604   std::vector<unsigned int> cumulative_trackid_vec;
0605   std::vector<unsigned int> cumulative_nmvtx_vec;
0606   std::vector<unsigned int> cumulative_nintt_vec;
0607   std::vector<std::vector<Acts::Vector3>> cumulative_global_vec;
0608   std::vector<std::vector<TrkrDefs::cluskey>> cumulative_cluskey_vec;
0609   std::vector<std::vector<float>> cumulative_fitpars_vec;
0610 
0611   for (auto & tr1_it : *track_map)
0612   {
0613     auto id1 = tr1_it.first;
0614     auto *tr1 = tr1_it.second;
0615 
0616     //    tr1->identify();
0617  
0618     TrackSeed *siliconseed = tr1->get_silicon_seed();
0619     if (_require_mvtx)
0620       {
0621     if (!siliconseed)
0622       {
0623         continue;
0624       }
0625       }
0626     TrackSeed *tpcseed = tr1->get_tpc_seed();
0627 
0628     std::vector<Acts::Vector3> global_vec;
0629     std::vector<TrkrDefs::cluskey> cluskey_vec;
0630     
0631     // Get a vector of cluster keys from the silicon seed and TPC seed
0632     if(siliconseed)
0633       {
0634     getTrackletClusterList(siliconseed, cluskey_vec);
0635     if(Verbosity() > 0) 
0636       { 
0637         std::cout << "  after silicon: silicon cluskey_vec size " << cluskey_vec.size() << std::endl; 
0638         for(unsigned long i : cluskey_vec)
0639           {  
0640         std::cout << i << std::endl;
0641           }
0642       }
0643       }
0644     if(tpcseed)
0645       {
0646 
0647     getTrackletClusterList(tpcseed, cluskey_vec);
0648     if(Verbosity() > 0) 
0649       { 
0650         std::cout << "  after tpc: cluskey_vec size " << cluskey_vec.size() << std::endl; 
0651         for(unsigned long i : cluskey_vec)
0652           {  
0653         std::cout << i << std::endl;
0654           }
0655       }
0656       }
0657 
0658     unsigned int nmvtx = 0;
0659     unsigned int nintt = 0;
0660     for (auto& key : cluskey_vec)
0661       {
0662         if (TrkrDefs::getTrkrId(key) == TrkrDefs::mvtxId)
0663       {
0664         nmvtx++;
0665       }
0666     if(TrkrDefs::getTrkrId(key) == TrkrDefs::inttId)
0667       {
0668         nintt++;
0669       }
0670       }
0671     
0672     // store cluster global positions in a vector
0673     TrackFitUtils::getTrackletClusters(_tGeometry, _cluster_map, global_vec, cluskey_vec);
0674     
0675     std::vector<float> fitpars = TrackFitUtils::fitClustersZeroField(global_vec, cluskey_vec, true);
0676     
0677     if (Verbosity() > 1)
0678       {
0679     if(fitpars.size() == 4)
0680       {
0681         std::cout << " Track " << id1 << " dy/dx " << fitpars[0] << " y intercept " << fitpars[1] 
0682               << " dx/dz " << fitpars[2] << " Z0 " << fitpars[3] << std::endl;
0683       }
0684     else
0685       {
0686         std::cout << " Track " << id1 << " ZF line fits failed, fitpars is empty" << std::endl;
0687       }
0688       }
0689 
0690     cumulative_trackid_vec.push_back(id1);   
0691     cumulative_nmvtx_vec.push_back(nmvtx);   
0692     cumulative_nintt_vec.push_back(nintt);   
0693     cumulative_global_vec.push_back(global_vec);
0694     cumulative_cluskey_vec.push_back(cluskey_vec);
0695     cumulative_fitpars_vec.push_back(fitpars);
0696   }
0697 
0698   for(unsigned int i1 = 0; i1 < cumulative_trackid_vec.size(); ++i1)
0699     {
0700       if(cumulative_fitpars_vec[i1].empty()) { continue; }
0701 
0702       for(unsigned int i2 = i1; i2 < cumulative_trackid_vec.size(); ++i2)
0703     {
0704       if(cumulative_fitpars_vec[i2].empty()) { continue; }
0705 
0706       //  For straight line: fitpars[4] = { xyslope, y0, xzslope, z0 }
0707       Eigen::Vector3d a1(0.0, cumulative_fitpars_vec[i1][1],cumulative_fitpars_vec[i1][3]);      // point on track 1 at x = 0
0708       Eigen::Vector3d a2(0.0, cumulative_fitpars_vec[i2][1],cumulative_fitpars_vec[i2][3]);      // point on track 2 at x = 0
0709       // direction vectors made from dy/dx = xyslope and dz/dx = xzslope
0710       Eigen::Vector3d b1(1.0, cumulative_fitpars_vec[i1][0],cumulative_fitpars_vec[i1][2]);      // direction vector of track 1
0711       Eigen::Vector3d b2(1.0, cumulative_fitpars_vec[i2][0],cumulative_fitpars_vec[i2][2]);      // direction vector of track 2
0712           
0713       Eigen::Vector3d PCA1(0, 0, 0);
0714       Eigen::Vector3d PCA2(0, 0, 0);
0715       double dca = dcaTwoLines(a1, b1, a2, b2, PCA1, PCA2);
0716 
0717 
0718       // check dca cut is satisfied, and that PCA is close to beam line
0719       if (fabs(dca) < _active_dcacut
0720           && (PCA1.x() > _beamline_x_cut_lo && PCA1.x() < _beamline_x_cut_hi)
0721           && (PCA1.y() > _beamline_y_cut_lo && PCA1.y() < _beamline_y_cut_hi)
0722           && (PCA2.x() > _beamline_x_cut_lo && PCA2.x() < _beamline_x_cut_hi)
0723           && (PCA2.y() > _beamline_y_cut_lo && PCA2.y() < _beamline_y_cut_hi)
0724           )
0725         {
0726           int id1 = cumulative_trackid_vec[i1];
0727           int id2 = cumulative_trackid_vec[i2];
0728 
0729           if (Verbosity() > 3)
0730         {
0731           std::cout << " good match for tracks " << id1 << " and " << id2 << std::endl;
0732           std::cout << "    a1.x " << a1.x() << " a1.y " << a1.y() << " a1.z " << a1.z() << std::endl;
0733           std::cout << "    a2.x  " << a2.x() << " a2.y " << a2.y() << " a2.z " << a2.z() << std::endl;
0734           std::cout << "    PCA1.x() " << PCA1.x() << " PCA1.y " << PCA1.y() << " PCA1.z " << PCA1.z() << std::endl;
0735           std::cout << "    PCA2.x() " << PCA2.x() << " PCA2.y " << PCA2.y() << " PCA2.z " << PCA2.z() << std::endl;
0736           std::cout << "    dca " << dca << std::endl;
0737         }
0738           
0739           // capture the results for successful matches
0740           _track_pair_map.insert(std::make_pair(id1, std::make_pair(id2, dca)));
0741           _track_pair_pca_map.insert(std::make_pair(id1, std::make_pair(id2, std::make_pair(PCA1, PCA2))));
0742         }         
0743     }
0744     }
0745 
0746   return; 
0747 }
0748 
0749 void PHSimpleVertexFinder::getTrackletClusterList(TrackSeed* tracklet, std::vector<TrkrDefs::cluskey>& cluskey_vec)
0750 {
0751   for (auto clusIter = tracklet->begin_cluster_keys();
0752        clusIter != tracklet->end_cluster_keys();
0753        ++clusIter)
0754   {
0755     auto key = *clusIter;
0756     auto *cluster = _cluster_map->findCluster(key);
0757     if (!cluster)
0758     {
0759       std::cout << PHWHERE << "Failed to get cluster with key " << key << std::endl;
0760       continue;
0761     }
0762 
0763     /// Make a safety check for clusters that couldn't be attached to a surface
0764     auto surf = _tGeometry->maps().getSurface(key, cluster);
0765     if (!surf)
0766     {
0767       continue;
0768     }
0769 
0770     // drop some bad layers in the TPC completely
0771     unsigned int layer = TrkrDefs::getLayer(key);
0772     if (layer == 7 || layer == 22 || layer == 23 || layer == 38 || layer == 39)
0773     {
0774       continue;
0775     }
0776 
0777     // drop INTT clusters for now  -- TEMPORARY!
0778     if (layer > 2 && layer < 7)
0779     {
0780       continue;
0781     }
0782 
0783 
0784     cluskey_vec.push_back(key);
0785 
0786   }  // end loop over clusters for this track
0787 }
0788 
0789 void PHSimpleVertexFinder::checkDCAs()
0790 {
0791   // Loop over tracks and check for close DCA match with all other tracks
0792   for (auto tr1_it = _track_map->begin(); tr1_it != _track_map->end(); ++tr1_it)
0793   {
0794     auto id1 = tr1_it->first;
0795     auto *tr1 = tr1_it->second;
0796     if (tr1->get_quality() > _qual_cut)
0797     {
0798       continue;
0799     }
0800     if (_require_mvtx)
0801     {
0802       unsigned int nmvtx = 0;
0803       TrackSeed *siliconseed = tr1->get_silicon_seed();
0804       if (!siliconseed)
0805       {
0806         continue;
0807       }
0808 
0809       for (auto clusit = siliconseed->begin_cluster_keys(); clusit != siliconseed->end_cluster_keys(); ++clusit)
0810       {
0811         if (TrkrDefs::getTrkrId(*clusit) == TrkrDefs::mvtxId)
0812         {
0813           nmvtx++;
0814         }
0815         if (nmvtx >= _nmvtx_required)
0816         {
0817           break;
0818         }
0819       }
0820       if (nmvtx < _nmvtx_required)
0821       {
0822         continue;
0823       }
0824       if (Verbosity() > 3)
0825       {
0826         std::cout << " tr1 id " << id1 << " has nmvtx at least " << nmvtx << std::endl;
0827       }
0828     }
0829 
0830     // look for close DCA matches with all other such tracks
0831     for (auto tr2_it = std::next(tr1_it); tr2_it != _track_map->end(); ++tr2_it)
0832     {
0833       auto id2 = tr2_it->first;
0834       auto *tr2 = tr2_it->second;
0835       if (tr2->get_quality() > _qual_cut)
0836       {
0837         continue;
0838       }
0839       if (_require_mvtx)
0840       {
0841         unsigned int nmvtx = 0;
0842         TrackSeed *siliconseed = tr2->get_silicon_seed();
0843         if (!siliconseed)
0844         {
0845           continue;
0846         }
0847 
0848         for (auto clusit = siliconseed->begin_cluster_keys(); clusit != siliconseed->end_cluster_keys(); ++clusit)
0849         {
0850           if (TrkrDefs::getTrkrId(*clusit) == TrkrDefs::mvtxId)
0851           {
0852             nmvtx++;
0853           }
0854           if (nmvtx >= _nmvtx_required)
0855           {
0856             break;
0857           }
0858         }
0859         if (nmvtx < _nmvtx_required)
0860         {
0861           continue;
0862         }
0863         if (Verbosity() > 3)
0864         {
0865           std::cout << " tr2 id " << id2 << " has nmvtx at least " << nmvtx << std::endl;
0866         }
0867       }
0868 
0869       // find DCA of these two tracks
0870       if (Verbosity() > 3)
0871       {
0872         std::cout << "Check DCA for tracks " << id1 << " and  " << id2 << std::endl;
0873       }
0874 
0875       findDcaTwoTracks(tr1, tr2);
0876     }
0877   }
0878 }
0879 
0880 void PHSimpleVertexFinder::findDcaTwoTracks(SvtxTrack *tr1, SvtxTrack *tr2)
0881 {
0882   if (tr1->get_pt() < _track_pt_cut)
0883   {
0884     return;
0885   }
0886   if (tr2->get_pt() < _track_pt_cut)
0887   {
0888     return;
0889   }
0890 
0891   unsigned int id1 = tr1->get_id();
0892   unsigned int id2 = tr2->get_id();
0893 
0894   // get the line equations for the tracks
0895 
0896   Eigen::Vector3d a1(tr1->get_x(), tr1->get_y(), tr1->get_z());
0897   Eigen::Vector3d b1(tr1->get_px() / tr1->get_p(), tr1->get_py() / tr1->get_p(), tr1->get_pz() / tr1->get_p());
0898   Eigen::Vector3d a2(tr2->get_x(), tr2->get_y(), tr2->get_z());
0899   Eigen::Vector3d b2(tr2->get_px() / tr2->get_p(), tr2->get_py() / tr2->get_p(), tr2->get_pz() / tr2->get_p());
0900 
0901   Eigen::Vector3d PCA1(0, 0, 0);
0902   Eigen::Vector3d PCA2(0, 0, 0);
0903   double dca = dcaTwoLines(a1, b1, a2, b2, PCA1, PCA2);
0904 
0905   if (Verbosity() > 3)
0906     {
0907       std::cout << " pair dca is " << dca << " _active_dcacut is " << _active_dcacut
0908         << " PCA1.x " << PCA1.x() << " PCA1.y " << PCA1.y()
0909         << " PCA2.x " << PCA2.x() << " PCA2.y " << PCA2.y() << std::endl;
0910     }
0911   
0912   // check dca cut is satisfied, and that PCA is close to beam line
0913   if (fabs(dca) < _active_dcacut
0914       && (PCA1.x() > _beamline_x_cut_lo && PCA1.x() < _beamline_x_cut_hi)
0915       && (PCA1.y() > _beamline_y_cut_lo && PCA1.y() < _beamline_y_cut_hi)
0916       && (PCA2.x() > _beamline_x_cut_lo && PCA2.x() < _beamline_x_cut_hi)
0917       && (PCA2.y() > _beamline_y_cut_lo && PCA2.y() < _beamline_y_cut_hi)   )
0918     {
0919       if (Verbosity() > 3)
0920     {
0921       std::cout << " good match for tracks " << tr1->get_id() << " and " << tr2->get_id() << " with pT " << tr1->get_pt() << " and " << tr2->get_pt() << std::endl;
0922       std::cout << "    a1.x " << a1.x() << " a1.y " << a1.y() << " a1.z " << a1.z() << std::endl;
0923       std::cout << "    a2.x  " << a2.x() << " a2.y " << a2.y() << " a2.z " << a2.z() << std::endl;
0924       std::cout << "    PCA1.x() " << PCA1.x() << " PCA1.y " << PCA1.y() << " PCA1.z " << PCA1.z() << std::endl;
0925       std::cout << "    PCA2.x() " << PCA2.x() << " PCA2.y " << PCA2.y() << " PCA2.z " << PCA2.z() << std::endl;
0926       std::cout << "    dca " << dca << std::endl;
0927     }
0928       
0929       // capture the results for successful matches
0930       _track_pair_map.insert(std::make_pair(id1, std::make_pair(id2, dca)));
0931       _track_pair_pca_map.insert(std::make_pair(id1, std::make_pair(id2, std::make_pair(PCA1, PCA2))));
0932     }
0933   
0934   return;
0935 }
0936 
0937 double PHSimpleVertexFinder::dcaTwoLines(const Eigen::Vector3d &a1, const Eigen::Vector3d &b1,
0938                                          const Eigen::Vector3d &a2, const Eigen::Vector3d &b2,
0939                                          Eigen::Vector3d &PCA1, Eigen::Vector3d &PCA2)
0940 {
0941   // The shortest distance between two skew lines described by
0942   //  a1 + c * b1
0943   //  a2 + d * b2
0944   // where a1, a2, are vectors representing points on the lines, b1, b2 are direction vectors, and c and d are scalars
0945   // is:
0946   // dca = (b1 x b2) .(a2-a1) / |b1 x b2|
0947 
0948   // bcrossb/mag_bcrossb is a unit vector perpendicular to both direction vectors b1 and b2
0949   auto bcrossb = b1.cross(b2);
0950   auto mag_bcrossb = bcrossb.norm();
0951   // a2-a1 is the vector joining any arbitrary points on the two lines
0952   auto aminusa = a2 - a1;
0953 
0954   // The DCA of these two lines is the projection of a2-a1 along the direction of the perpendicular to both
0955   // remember that a2-a1 is longer than (or equal to) the dca by definition
0956   double dca = 999;
0957   if (mag_bcrossb != 0)
0958   {
0959     dca = bcrossb.dot(aminusa) / mag_bcrossb;
0960   }
0961   else
0962   {
0963     return dca;  // same track, skip combination
0964   }
0965 
0966   // get the points at which the normal to the lines intersect the lines, where the lines are perpendicular
0967   // Assume the shortest distance occurs at points A on line 1, and B on line 2, call the line AB
0968   //    AB = a2+d*b2 - (a1+c*b1)
0969   // we need to find c and d where AB is perpendicular to both lines. so AB.b1 = 0 and AB.b2 = 0
0970   //    ( (a2 -a1) + d*b2 -c*b1 ).b1 = 0
0971   //    ( (a2 -a1) + d*b2 -c*b1 ).b2 = 0
0972   // so we have two simultaneous equations in 2 unknowns
0973   //    (a2-a1).b1 + d*b2.b1 -c*b1.b1 = 0 => d*b2.b1 = c*b1.b1 - (a2-a1).b1 => d = (1/b2.b1) * (c*b1.b1 - (a2-a1).b1)
0974   //    (a2-a1).b2 + d*b2.b2 - c*b1.b2 = 0 => c*b1.b2 =  (a2-a1).b2 + [(1/b2.b1) * (c*b1*b1 -(a2-a1).b1)}*b2.b2
0975   //    c*b1.b2 - (1/b2.b1) * c*b1.b1*b2.b2 = (a2-a1).b2 - (1/b2.b1)*(a2-a1).b1*b2.b2
0976   //    c *(b1.b2 - (1/b2.b1)*b1.b1*b2.b2)  = (a2-a1).b2 - (1/b2.b1)*(a2-a1).b1*b2.b2
0977   // call this: c*X = Y
0978   // plug back into the d equation
0979   //     d = c*b1.b1 / b2.b1 - (a2-a1).b1 / b2.b1
0980   // and call the d equation: d = c * F - G
0981 
0982   double X = b1.dot(b2) - b1.dot(b1) * b2.dot(b2) / b2.dot(b1);
0983   double Y = (a2.dot(b2) - a1.dot(b2)) - (a2.dot(b1) - a1.dot(b1)) * b2.dot(b2) / b2.dot(b1);
0984   double c = Y / X;
0985 
0986   double F = b1.dot(b1) / b2.dot(b1);
0987   double G = -(a2.dot(b1) - a1.dot(b1)) / b2.dot(b1);
0988   double d = c * F + G;
0989 
0990   // then the points of closest approach are:
0991   PCA1 = a1 + c * b1;
0992   PCA2 = a2 + d * b2;
0993 
0994   return dca;
0995 }
0996 
0997 std::vector<std::set<unsigned int>> PHSimpleVertexFinder::findConnectedTracks()
0998 {
0999   std::vector<std::set<unsigned int>> connected_tracks;
1000   std::set<unsigned int> connected;
1001   std::set<unsigned int> used;
1002   for (auto it : _track_pair_map)
1003   {
1004     unsigned int id1 = it.first;
1005     unsigned int id2 = it.second.first;
1006     double dca12 = it.second.second;
1007     
1008     if(Verbosity() > 2)
1009       {
1010     auto rt = _track_pair_pca_map.equal_range(id1);
1011     for (auto ct = rt.first; ct != rt.second; ++ct)
1012       {
1013         unsigned int idb = ct->second.first;
1014         if(idb==id2)
1015           {
1016         auto pca1=ct->second.second.first;
1017         auto pca2=ct->second.second.second;
1018         std::cout << "Begin search on id1 = " << id1 << " and id2 = " << id2 << " dca12 = " << dca12 << std::endl;
1019         std::cout << "       id1 " << id1 << " pca1 " << pca1.x() << "  " << pca1.y() << "  " << pca1.z() << std::endl;
1020         std::cout << "       id2 " << id2 << " pca1 " << pca2.x() << "  " << pca2.y() << "  " << pca2.z() << std::endl;
1021           }
1022       }
1023       }
1024     
1025     if ((used.contains(id1)) && (used.contains(id2)))
1026       {
1027     if (Verbosity() > 2)
1028       {
1029         std::cout << " tracks " << id1 << " and " << id2 << " are both in used , skip them" << std::endl;
1030       }
1031     continue;
1032       }
1033     if ((!used.contains(id1)) && (!used.contains(id2)))
1034       {
1035     if (Verbosity() > 2)
1036       {
1037         auto rt1 = _track_pair_pca_map.equal_range(id1);
1038         for (auto ct = rt1.first; ct != rt1.second; ++ct)
1039           {
1040         unsigned int ida = ct->first;
1041         unsigned int idb = ct->second.first;
1042         
1043         if(idb==id2)
1044           {
1045             auto pcaa=ct->second.second.first;
1046             auto pcab=ct->second.second.second;
1047             std::cout << " tracks " << id1 << " and " << id2 << " dca = " << dca12
1048                   << " are both not in used, start a new connected set" << std::endl;
1049             std::cout << "       ida " << ida << " pcaa " << pcaa.x() << "  " << pcaa.y() << "  " << pcaa.z() << std::endl;
1050             std::cout << "       idb " << idb << " pcab " << pcab.x() << "  " << pcab.y() << "  " << pcab.z() << std::endl;
1051           }
1052           }
1053       }
1054     // close out and start a new connected set
1055     if (!connected.empty())
1056       {
1057         if (Verbosity() > 2)
1058           {
1059         std::cout << "           closing out set with size " << connected.size() << std::endl;
1060           }
1061         connected_tracks.push_back(connected);
1062         connected.clear();
1063       }
1064       }
1065     
1066     // get everything connected to id1 and id2
1067     connected.insert(id1);
1068     used.insert(id1);
1069     connected.insert(id2);
1070     used.insert(id2);
1071     for (auto cit : _track_pair_map)
1072     {
1073       unsigned int id3 = cit.first;
1074       unsigned int id4 = cit.second.first;
1075       double dca34 = cit.second.second;
1076 
1077       if ((connected.contains(id3)) || (connected.contains(id4)))
1078       {
1079         if (Verbosity() > 2)
1080       {
1081 
1082         auto rt2 = _track_pair_pca_map.equal_range(id3);
1083         for (auto ct = rt2.first; ct != rt2.second; ++ct)
1084           {
1085         unsigned int ida = ct->first;
1086         unsigned int idb = ct->second.first;
1087         
1088         if(idb==id4)
1089           {
1090             auto pcaa=ct->second.second.first;
1091             auto pcab=ct->second.second.second;
1092             std::cout << "         found connection to " << id3 << " and " << id4 << " dca34 = " << dca34
1093                   << " pca dz = " << pcaa.z() - pcab.z() << std::endl;
1094             std::cout << "       id3 " << ida << " pca3 " << pcaa.x() << "  " << pcaa.y() << "  " << pcaa.z() << std::endl;
1095             std::cout << "       id4 " << idb << " pca4 " << pcab.x() << "  " << pcab.y() << "  " << pcab.z() << std::endl;
1096         }
1097           }
1098       }
1099         connected.insert(id3);
1100         used.insert(id3);
1101         connected.insert(id4);
1102         used.insert(id4);
1103       }
1104     }
1105   }
1106 
1107   // close out the last set
1108   if (!connected.empty())
1109     {
1110       if (Verbosity() > 2)
1111     {
1112       std::cout << "           closing out last connected set with size " << connected.size() << std::endl;
1113     }
1114       connected_tracks.push_back(connected);
1115       connected.clear();
1116     }
1117   
1118   if (Verbosity() > 2)
1119     {
1120       std::cout << "connected_tracks size " << connected_tracks.size() << std::endl;
1121     }
1122   
1123   return connected_tracks;
1124 }
1125 
1126 void PHSimpleVertexFinder::removeOutlierTrackPairs()
1127 {
1128   //  Note: std::multimap<unsigned int, std::pair<unsigned int, std::pair<Eigen::Vector3d,  Eigen::Vector3d>>>  _track_pair_pca_map
1129 
1130   for (auto it : _vertex_set)
1131   {
1132     unsigned int vtxid = it;
1133     if (Verbosity() > 1)
1134     {
1135       std::cout << "calculate average position for vertex " << vtxid << std::endl;
1136     }
1137 
1138     // we need the median values of the x and y positions
1139     std::vector<double> vx;
1140     std::vector<double> vy;
1141     std::vector<double> vz;
1142 
1143     double pca_median_x = 0.;
1144     double pca_median_y = 0.;
1145     double pca_median_z = 0.;
1146 
1147     Eigen::Vector3d new_pca_avge(0., 0., 0.);
1148     double new_wt = 0.0;
1149 
1150     auto ret = _vertex_track_map.equal_range(vtxid);
1151 
1152     // Start by getting the positions for this vertex into vectors for the median calculation
1153     for (auto cit = ret.first; cit != ret.second; ++cit)
1154     {
1155       unsigned int tr1id = cit->second;
1156       if (Verbosity() > 2)
1157       {
1158         std::cout << "   vectors: get entries for track " << tr1id << " for vertex " << vtxid << std::endl;
1159       }
1160 
1161       // find all pairs for this vertex with tr1id
1162       auto pca_range = _track_pair_pca_map.equal_range(tr1id);
1163       for (auto pit = pca_range.first; pit != pca_range.second; ++pit)
1164       {
1165         unsigned int tr2id = pit->second.first;
1166 
1167         Eigen::Vector3d PCA1 = pit->second.second.first;
1168         Eigen::Vector3d PCA2 = pit->second.second.second;
1169 
1170         if (Verbosity() > 2)
1171         {
1172           std::cout << " vectors: tr1id " << tr1id << " tr2id " << tr2id
1173                     << " PCA1 " << PCA1.x() << "  " << PCA1.y() << "  " << PCA1.z()
1174                     << " PCA2 " << PCA2.x() << "  " << PCA2.y() << "  " << PCA2.z()
1175                     << std::endl;
1176         }
1177 
1178         vx.push_back(PCA1.x());
1179         vx.push_back(PCA2.x());
1180         vy.push_back(PCA1.y());
1181         vy.push_back(PCA2.y());
1182         vz.push_back(PCA1.z());
1183         vz.push_back(PCA2.z());
1184       }
1185     }
1186 
1187     // Get the medians for this vertex
1188     // Using the median as a reference for rejecting outliers only makes sense for more than 2 tracks
1189     if (vx.size() < 3)
1190     {
1191       new_pca_avge.x() = getAverage(vx);
1192       new_pca_avge.y() = getAverage(vy);
1193       new_pca_avge.z() = getAverage(vz);
1194       _vertex_position_map.insert(std::make_pair(vtxid, new_pca_avge));
1195       if (Verbosity() > 1)
1196       {
1197         std::cout << " Vertex has only 2 tracks, use average for PCA: " << new_pca_avge.x() << "  " << new_pca_avge.y() << "  " << new_pca_avge.z() << std::endl;
1198       }
1199 
1200       // done with this vertex
1201       continue;
1202     }
1203 
1204     pca_median_x = getMedian(vx);
1205     pca_median_y = getMedian(vy);
1206     pca_median_z = getMedian(vz);
1207     if (Verbosity() > 1)
1208     {
1209       std::cout << "Median values: x " << pca_median_x << " y " << pca_median_y << " z : " << pca_median_z << std::endl;
1210     }
1211 
1212     // Make the average vertex position with outlier rejection wrt the median
1213     for (auto cit = ret.first; cit != ret.second; ++cit)
1214     {
1215       unsigned int tr1id = cit->second;
1216       if (Verbosity() > 2)
1217       {
1218         std::cout << "   average: get entries for track " << tr1id << " for vertex " << vtxid << std::endl;
1219       }
1220 
1221       // find all pairs for this vertex with tr1id
1222       auto pca_range = _track_pair_pca_map.equal_range(tr1id);
1223       for (auto pit = pca_range.first; pit != pca_range.second; ++pit)
1224       {
1225         unsigned int tr2id = pit->second.first;
1226 
1227         Eigen::Vector3d PCA1 = pit->second.second.first;
1228         Eigen::Vector3d PCA2 = pit->second.second.second;
1229 
1230         if (
1231             fabs(PCA1.x() - pca_median_x) < _outlier_cut &&
1232             fabs(PCA1.y() - pca_median_y) < _outlier_cut &&
1233             fabs(PCA2.x() - pca_median_x) < _outlier_cut &&
1234             fabs(PCA2.y() - pca_median_y) < _outlier_cut)
1235         {
1236           // good track pair, add to new average
1237 
1238           new_pca_avge += PCA1;
1239           new_wt++;
1240           new_pca_avge += PCA2;
1241           new_wt++;
1242         }
1243         else
1244         {
1245           if (Verbosity() > 1)
1246           {
1247             std::cout << "Reject pair with tr1id " << tr1id << " tr2id " << tr2id << std::endl;
1248           }
1249         }
1250       }
1251     }
1252     if (new_wt > 0.0)
1253     {
1254       new_pca_avge = new_pca_avge / new_wt;
1255     }
1256     else
1257     {
1258       // There were no pairs that survived the track cuts, use the median values
1259       new_pca_avge.x() = pca_median_x;
1260       new_pca_avge.y() = pca_median_y;
1261       new_pca_avge.z() = pca_median_z;
1262     }
1263 
1264     _vertex_position_map.insert(std::make_pair(vtxid, new_pca_avge));
1265   }
1266 
1267   return;
1268 }
1269 
1270 double PHSimpleVertexFinder::getMedian(std::vector<double> &v)
1271 {
1272   double median = 0.0;
1273 
1274   if ((v.size() % 2) == 0)
1275   {
1276     // even number of entries
1277     // we want the average of the middle two numbers, v.size()/2 and v.size()/2-1
1278     auto m1 = v.begin() + v.size() / 2;
1279     std::nth_element(v.begin(), m1, v.end());
1280     double median1 = v[v.size() / 2];
1281 
1282     auto m2 = v.begin() + v.size() / 2 - 1;
1283     std::nth_element(v.begin(), m2, v.end());
1284     double median2 = v[v.size() / 2 - 1];
1285 
1286     median = (median1 + median2) / 2.0;
1287     if (Verbosity() > 2)
1288     {
1289       std::cout << "The vector size is " << v.size()
1290                 << " element m is " << v.size() / 2 << " = " << v[v.size() / 2]
1291                 << " element m-1 is " << v.size() / 2 - 1 << " = " << v[v.size() / 2 - 1]
1292                 << std::endl;
1293     }
1294   }
1295   else
1296   {
1297     // odd number of entries
1298     auto m = v.begin() + v.size() / 2;
1299     std::nth_element(v.begin(), m, v.end());
1300     median = v[v.size() / 2];
1301     if (Verbosity() > 2)
1302     {
1303       std::cout << "The vector size is " << v.size() << " element m is " << v.size() / 2 << " = " << v[v.size() / 2] << std::endl;
1304     }
1305   }
1306 
1307   return median;
1308 }
1309 double PHSimpleVertexFinder::getAverage(std::vector<double> &v)
1310 {
1311   double avge = 0.0;
1312   double wt = 0.0;
1313   for (auto it : v)
1314   {
1315     avge += it;
1316     wt++;
1317   }
1318 
1319   avge /= wt;
1320   if (Verbosity() > 2)
1321   {
1322     std::cout << " average = " << avge << std::endl;
1323   }
1324 
1325   return avge;
1326 }