Back to home page

sPhenix code displayed by LXR

 
 

    


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

0001 /*!
0002  *  \file PHSimpleKFProp.cc
0003  *  \brief      kalman filter based propagator
0004  *  \author Michael Peters & Christof Roland
0005  */
0006 
0007 #include "PHSimpleKFProp.h"
0008 #include "ALICEKF.h"
0009 #include "GPUTPCTrackLinearisation.h"
0010 #include "GPUTPCTrackParam.h"
0011 #include "PHGhostRejection.h"
0012 #include "nanoflann.hpp"
0013 
0014 #include <ffamodules/CDBInterface.h>
0015 
0016 #include <g4detectors/PHG4TpcGeom.h>
0017 #include <g4detectors/PHG4TpcGeomContainer.h>
0018 
0019 #include <phfield/PHFieldUtility.h>
0020 #include <phfield/PHFieldConfig.h>
0021 
0022 // tpc distortion correction
0023 #include <tpc/TpcDistortionCorrectionContainer.h>
0024 
0025 #include <trackbase/ActsGeometry.h>
0026 #include <trackbase/TrackFitUtils.h>
0027 #include <trackbase/TrkrCluster.h>
0028 #include <trackbase/TrkrClusterContainer.h>
0029 #include <trackbase/TrkrClusterIterationMapv1.h>
0030 #include <trackbase/TrkrDefs.h>
0031 
0032 #include <trackbase_historic/ActsTransformations.h>
0033 #include <trackbase_historic/TrackSeedContainer.h>
0034 #include <trackbase_historic/TrackSeed_v2.h>
0035 #include <trackbase_historic/TrackSeedHelper.h>
0036 
0037 #include <fun4all/Fun4AllReturnCodes.h>
0038 
0039 #include <phool/PHTimer.h>
0040 #include <phool/getClass.h>
0041 #include <phool/phool.h>  // for PHWHERE
0042 
0043 #include <TSystem.h>
0044 
0045 #include <Geant4/G4SystemOfUnits.hh>
0046 
0047 #include <Eigen/Core>
0048 #include <Eigen/Dense>
0049 
0050 #include <omp.h>
0051 
0052 #include <cmath>
0053 #include <filesystem>
0054 #include <iostream>
0055 #include <syncstream>
0056 #include <vector>
0057 
0058 // anonymous namespace for local functions
0059 namespace
0060 {
0061   // square
0062   template <class T>
0063   inline constexpr T square(const T& x)
0064   {
0065     return x * x;
0066   }
0067 }  // namespace
0068 
0069 using keylist = std::vector<TrkrDefs::cluskey>;
0070 
0071 PHSimpleKFProp::PHSimpleKFProp(const std::string& name)
0072   : SubsysReco(name)
0073 {}
0074 
0075 //______________________________________________________
0076 int PHSimpleKFProp::End(PHCompositeNode* /*unused*/)
0077 {
0078   return Fun4AllReturnCodes::EVENT_OK;
0079 }
0080 
0081 //______________________________________________________
0082 int PHSimpleKFProp::InitRun(PHCompositeNode* topNode)
0083 {
0084   int ret = get_nodes(topNode);
0085   if (ret != Fun4AllReturnCodes::EVENT_OK)
0086   {
0087     return ret;
0088   }
0089 
0090   // load magnetic field from node tree
0091   /* note: if field is not found it is created with default configuration, as defined in PHFieldUtility */
0092   const auto field_map = PHFieldUtility::GetFieldMapNode(nullptr, topNode);
0093 
0094   // alice kalman filter
0095   fitter = std::make_unique<ALICEKF>(_cluster_map, field_map, _min_clusters_per_track, _max_sin_phi, Verbosity());
0096   fitter->setNeonFraction(Ne_frac);
0097   fitter->setArgonFraction(Ar_frac);
0098   fitter->setCF4Fraction(CF4_frac);
0099   fitter->setNitrogenFraction(N2_frac);
0100   fitter->setIsobutaneFraction(isobutane_frac);
0101   fitter->useFixedClusterError(_use_fixed_clus_err);
0102   fitter->setFixedClusterError(0, _fixed_clus_err.at(0));
0103   fitter->setFixedClusterError(1, _fixed_clus_err.at(1));
0104   fitter->setFixedClusterError(2, _fixed_clus_err.at(2));
0105 
0106   // properly set constField in ALICEKF, based on PHFieldConfig
0107   const auto field_config = PHFieldUtility::GetFieldConfigNode(nullptr, topNode);
0108   if( field_config->get_field_config() == PHFieldConfig::kFieldUniform )
0109   { fitter->setConstBField(field_config->get_field_mag_z()); }
0110 
0111   // assign number of threads
0112   std::cout << "PHSimpleKFProp::InitRun - m_num_threads: " << m_num_threads << std::endl;
0113   if( m_num_threads >= 1 ) { omp_set_num_threads( m_num_threads ); }
0114 
0115   return Fun4AllReturnCodes::EVENT_OK;
0116 }
0117 
0118 //___________________________________________________________________________
0119 int PHSimpleKFProp::get_nodes(PHCompositeNode* topNode)
0120 {
0121 
0122   // acts geometry
0123   m_tgeometry = findNode::getClass<ActsGeometry>(topNode, "ActsGeometry");
0124   if (!m_tgeometry)
0125   {
0126     std::cout << "No Acts tracking geometry, exiting." << std::endl;
0127     return Fun4AllReturnCodes::ABORTEVENT;
0128   }
0129 
0130   // tpc global position wrapper
0131   m_globalPositionWrapper.loadNodes(topNode);
0132 
0133   // clusters
0134   if (_use_truth_clusters)
0135   {
0136     _cluster_map = findNode::getClass<TrkrClusterContainer>(topNode, "TRKR_CLUSTER_TRUTH");
0137   }
0138   else
0139   {
0140     _cluster_map = findNode::getClass<TrkrClusterContainer>(topNode, "TRKR_CLUSTER");
0141   }
0142   if (!_cluster_map)
0143   {
0144     std::cerr << PHWHERE << " ERROR: Can't find node TRKR_CLUSTER" << std::endl;
0145     return Fun4AllReturnCodes::ABORTEVENT;
0146   }
0147 
0148   // tracks
0149   _track_map = findNode::getClass<TrackSeedContainer>(topNode, "TpcTrackSeedContainer");
0150   if (!_track_map)
0151   {
0152     std::cerr << PHWHERE << " ERROR: Can't find TrackSeedContainer " << std::endl;
0153     return Fun4AllReturnCodes::ABORTEVENT;
0154   }
0155 
0156   // tpc grometry
0157   auto geom_container = findNode::getClass<PHG4TpcGeomContainer>(topNode, "TPCGEOMCONTAINER");
0158   if (!geom_container)
0159   {
0160     std::cerr << PHWHERE << "ERROR: Can't find node TPCGEOMCONTAINER" << std::endl;
0161     return Fun4AllReturnCodes::ABORTRUN;
0162   }
0163 
0164   for (int i = 7; i <= 54; i++)
0165   {
0166     radii.push_back(geom_container->GetLayerCellGeom(i)->get_radius());
0167   }
0168 
0169   return Fun4AllReturnCodes::EVENT_OK;
0170 }
0171 
0172 int PHSimpleKFProp::process_event(PHCompositeNode* topNode)
0173 {
0174   if (_n_iteration != 0)
0175   {
0176     _iteration_map = findNode::getClass<TrkrClusterIterationMapv1>(topNode, "CLUSTER_ITERATION_MAP");
0177     if (!_iteration_map)
0178     {
0179       std::cerr << PHWHERE << "Cluster Iteration Map missing, aborting." << std::endl;
0180       return Fun4AllReturnCodes::ABORTEVENT;
0181     }
0182   }
0183 
0184   PHTimer timer("KFPropTimer");
0185   timer.restart();
0186 
0187   // check number of seeds against maximum
0188   if(_max_seeds > 0 && _track_map->size() > _max_seeds)
0189   {
0190     std::cout << "number of TPC seeds: " << _track_map->size() << std::endl;
0191     std::cout << PHWHERE << "number of TPC seeds > " << _max_seeds << " aborting event." << std::endl;
0192     return Fun4AllReturnCodes::ABORTEVENT;
0193   }
0194 
0195   const auto globalPositions = PrepareKDTrees();
0196   if (Verbosity())
0197   { std::cout << "PHSimpleKFProp::process_event - PrepareKDTrees time: " << timer.elapsed() << " ms" << std::endl; }
0198 
0199   // list of cluster chains
0200   std::vector<std::vector<TrkrDefs::cluskey>> new_chains;
0201   std::vector<TrackSeed_v2> unused_tracks;
0202 
0203   timer.restart();
0204   #pragma omp parallel
0205   {
0206     if (Verbosity())
0207     {
0208       std::osyncstream(std::cout)
0209         << "PHSimpleKFProp -"
0210         << " num_threads: " << omp_get_num_threads()
0211         << " this thread: " << omp_get_thread_num()
0212         << std::endl;
0213     }
0214 
0215     PHTimer timer_mp("KFPropTimer_parallel");
0216 
0217     std::vector<std::vector<TrkrDefs::cluskey>> local_chains;
0218     std::vector<TrackSeed_v2> local_unused;
0219 
0220     #pragma omp for schedule(static)
0221     for (size_t track_it = 0; track_it != _track_map->size(); ++track_it)
0222     {
0223       if (Verbosity())
0224       {
0225         std::osyncstream(std::cout)
0226           << "PHSimpleKFProp -"
0227           << " num_threads: " << omp_get_num_threads()
0228           << " this thread: " << omp_get_thread_num()
0229           << " processing seed " << track_it << std::endl;
0230       }
0231 
0232       // if not a TPC track, ignore
0233       auto track = _track_map->get(track_it);
0234       const bool is_tpc = std::any_of(
0235         track->begin_cluster_keys(),
0236         track->end_cluster_keys(),
0237         [](const TrkrDefs::cluskey& key)
0238       { return TrkrDefs::getTrkrId(key) == TrkrDefs::tpcId; });
0239 
0240       if (is_tpc)
0241       {
0242 
0243         // copy list of seed cluster keys
0244         std::vector<std::vector<TrkrDefs::cluskey>> keylist_A(1);
0245         std::copy(track->begin_cluster_keys(), track->end_cluster_keys(), std::back_inserter(keylist_A[0]));
0246 
0247         // copy seed clusters position into local map
0248         std::map<TrkrDefs::cluskey, Acts::Vector3> trackClusPositions;
0249         std::transform(track->begin_cluster_keys(), track->end_cluster_keys(), std::inserter(trackClusPositions, trackClusPositions.end()),
0250           [globalPositions](const auto& key)
0251         { return std::make_pair(key, globalPositions.at(key)); });
0252 
0253         /// Can't circle fit a seed with less than 3 clusters, skip it
0254         if (keylist_A[0].size() < 3)
0255         {
0256           continue;
0257         }
0258 
0259         /// This will by definition return a single pair with each vector
0260         /// in the pair length 1 corresponding to the seed info
0261         std::vector<float> trackChi2;
0262 
0263         timer_mp.restart();
0264         auto seedpair = fitter->ALICEKalmanFilter(keylist_A, false, trackClusPositions, trackChi2);
0265 
0266         if (Verbosity() > 3)
0267         {
0268           std::cout << "PHSimpleKFProp::process_event - single track ALICEKF time " << timer_mp.elapsed() << " ms" << std::endl;
0269         }
0270 
0271         timer_mp.restart();
0272 
0273         /// circle fit back to update track parameters
0274         TrackSeedHelper::circleFitByTaubin(track, trackClusPositions, 7, 55);
0275         TrackSeedHelper::lineFit(track, trackClusPositions, 7, 55);
0276         track->set_phi(TrackSeedHelper::get_phi(track, trackClusPositions));
0277         if (Verbosity() > 3)
0278         {
0279           std::cout << "PHSimpleKFProp::process_event - single track circle fit time " << timer_mp.elapsed() << " ms" << std::endl;
0280         }
0281 
0282         if (seedpair.first.empty()|| seedpair.second.empty())
0283         {
0284           continue;
0285         }
0286 
0287         if (Verbosity())
0288         {
0289           std::cout << "is tpc track" << std::endl;
0290         }
0291 
0292         timer_mp.restart();
0293 
0294         if (Verbosity())
0295         {
0296           std::cout << "propagate first round" << std::endl;
0297         }
0298 
0299         auto preseed = PropagateTrack(track, PropagationDirection::Inward, seedpair.second.at(0), globalPositions);
0300         if (Verbosity())
0301         {
0302           std::cout << "preseed size " << preseed.size() << std::endl;
0303         }
0304 
0305         std::vector<std::vector<TrkrDefs::cluskey>> kl = {preseed};
0306         if (Verbosity())
0307         {
0308           std::cout << "kl size " << kl.size() << std::endl;
0309         }
0310         std::vector<float> pretrackChi2;
0311 
0312         auto prepair = fitter->ALICEKalmanFilter(kl, false, globalPositions, pretrackChi2);
0313         if (prepair.first.empty() || prepair.second.empty())
0314         {
0315           continue;
0316         }
0317 
0318         std::reverse(kl.at(0).begin(), kl.at(0).end());
0319 
0320         auto pretrack = prepair.first.at(0);
0321 
0322         // copy seed clusters position into local map
0323         std::map<TrkrDefs::cluskey, Acts::Vector3> pretrackClusPositions;
0324         std::transform(pretrack.begin_cluster_keys(), pretrack.end_cluster_keys(), std::inserter(pretrackClusPositions, pretrackClusPositions.end()),
0325           [globalPositions](const auto& key)
0326           { return std::make_pair(key, globalPositions.at(key)); });
0327 
0328         // fit seed
0329         TrackSeedHelper::circleFitByTaubin(&pretrack,pretrackClusPositions, 7, 55);
0330         TrackSeedHelper::lineFit(&pretrack, pretrackClusPositions, 7, 55);
0331         pretrack.set_phi(TrackSeedHelper::get_phi(&pretrack, pretrackClusPositions));
0332 
0333         prepair.second.at(0).SetDzDs(-prepair.second.at(0).GetDzDs());
0334         const auto finalchain = PropagateTrack(&pretrack, kl.at(0), PropagationDirection::Outward, prepair.second.at(0), globalPositions);
0335 
0336         if (finalchain.size() > kl.at(0).size())
0337         {
0338           local_chains.push_back(std::move(finalchain));
0339         }
0340         else
0341         {
0342           local_chains.push_back(std::move(kl.at(0)));
0343         }
0344 
0345         if (Verbosity() > 3)
0346         {
0347           std::cout << "PHSimpleKFProp::process_event - propagate track time " << timer_mp.elapsed() << " ms" << std::endl;
0348         }
0349       }
0350       else
0351       {
0352         if (Verbosity())
0353         {
0354           std::cout << "is NOT tpc track" << std::endl;
0355         }
0356         local_unused.emplace_back(*track);
0357       }
0358     }
0359 
0360     // sort list and remove duplicates
0361     std::sort(local_chains.begin(),local_chains.end());
0362     local_chains.erase(std::unique(local_chains.begin(),local_chains.end()),local_chains.end());
0363 
0364     // Critical sections to merge thread-local results
0365     #pragma omp critical
0366     {
0367       new_chains.reserve(new_chains.size()+local_chains.size());
0368       new_chains.insert(new_chains.end(), std::make_move_iterator(local_chains.begin()), std::make_move_iterator(local_chains.end()));
0369 
0370       unused_tracks.reserve(unused_tracks.size()+local_unused.size());
0371       unused_tracks.insert(unused_tracks.end(), std::make_move_iterator(local_unused.begin()), std::make_move_iterator(local_unused.end()));
0372     }
0373   }
0374   if (Verbosity())
0375   { std::cout << "PHSimpleKFProp::process_event - first seed loop time: " << timer.elapsed() << " ms" << std::endl; }
0376 
0377   // sort merged list and remove duplicates
0378   timer.restart();
0379   std::sort(new_chains.begin(),new_chains.end());
0380   new_chains.erase(std::unique(new_chains.begin(),new_chains.end()),new_chains.end());
0381 
0382   if (Verbosity())
0383   { std::cout << "PHSimpleKFProp::process_event - first cleanup time: " << timer.elapsed() << " ms" << std::endl; }
0384 
0385   if( Verbosity() )
0386   {
0387     // print all seeds
0388     std::cout << "PHSimpleKFProp::process_event - new_chains size: " << new_chains.size() << std::endl;
0389     for( const auto& chain:new_chains )
0390     {
0391       std::cout << "PHSimpleKFProp::process_event - { ";
0392       for( const auto& key:chain )
0393       {
0394         std::cout << key << " ";
0395       }
0396       std::cout << "}" << std::endl << std::endl;
0397     }
0398   }
0399 
0400   // erase all seeds for size 2 or less
0401   new_chains.erase( std::remove_if( new_chains.begin(), new_chains.end(),
0402     [](const auto& chain) { return chain.size()<3; } ),
0403     new_chains.end() );
0404 
0405   // re-run ALICE Kalman Filter on completed chains
0406   timer.restart();
0407   std::vector<float> trackChi2;
0408   auto seeds = fitter->ALICEKalmanFilter(new_chains, true, globalPositions, trackChi2);
0409   if (Verbosity())
0410   {  std::cout << "PHSimpleKFProp::process_event - ALICEKalmanFilter time: " << timer.elapsed() << " ms" << std::endl; }
0411 
0412   // reset track map
0413   _track_map->Reset();
0414 
0415   //  Move ghost rejection into publishSeeds, so that we don't publish rejected seeds
0416   timer.restart();
0417   if (m_ghostrejection)
0418   {
0419     rejectAndPublishSeeds(seeds.first, globalPositions, trackChi2);
0420   }
0421   else
0422   {
0423     publishSeeds(seeds.first);
0424   }
0425 
0426   // also publish unused seeds (not TPC)
0427   publishSeeds(unused_tracks);
0428 
0429   if (Verbosity())
0430   { std::cout << "PHSimpleKFProp::process_event - publishSeeds time: " << timer.elapsed() << " ms" << std::endl; }
0431 
0432   return Fun4AllReturnCodes::EVENT_OK;
0433 }
0434 
0435 Acts::Vector3 PHSimpleKFProp::getGlobalPosition(TrkrDefs::cluskey key, TrkrCluster* cluster) const
0436 {
0437   // get global position from Acts transform
0438   return _pp_mode ?
0439     m_tgeometry->getGlobalPosition(key, cluster):
0440     m_globalPositionWrapper.getGlobalPositionDistortionCorrected( key, cluster, 0 );
0441 }
0442 
0443 PositionMap PHSimpleKFProp::PrepareKDTrees()
0444 {
0445   PositionMap globalPositions;
0446   //***** convert clusters to kdhits, and divide by layer
0447   std::vector<std::vector<std::vector<double>>> kdhits;
0448   kdhits.resize(58);
0449   if (!_cluster_map)
0450   {
0451     std::cout << "WARNING: (tracking.PHTpcTrackerUtil.convert_clusters_to_hits) cluster map is not provided" << std::endl;
0452     return globalPositions;
0453   }
0454 
0455   for (const auto& hitsetkey : _cluster_map->getHitSetKeys(TrkrDefs::TrkrId::tpcId))
0456   {
0457     auto range = _cluster_map->getClusters(hitsetkey);
0458     for (TrkrClusterContainer::ConstIterator it = range.first; it != range.second; ++it)
0459     {
0460       const auto& [cluskey,cluster] = *it;
0461       if (!cluster)
0462       {
0463         continue;
0464       }
0465 
0466       // skip hits used in a previous iteration
0467       if (_n_iteration && _iteration_map && _iteration_map->getIteration(cluskey) > 0)
0468       {
0469         continue;
0470       }
0471 
0472       const auto globalpos = getGlobalPosition(cluskey, cluster);
0473       globalPositions.emplace(cluskey, globalpos);
0474 
0475       const int layer = TrkrDefs::getLayer(cluskey);
0476       std::vector<double> kdhit{ globalpos.x(), globalpos.y(),  globalpos.z(), 0 };
0477       const uint64_t key = cluskey;
0478       std::memcpy(&kdhit[3], &key, sizeof(key));
0479 
0480       //      HINT: way to get original uint64_t value from double:
0481       //
0482       //      LOG_DEBUG("tracking.PHTpcTrackerUtil.convert_clusters_to_hits")
0483       //        << "orig: " << cluster->getClusKey() << ", readback: " << (*((int64_t*)&kdhit[3]));
0484 
0485       kdhits[layer].push_back(std::move(kdhit));
0486     }
0487   }
0488   _ptclouds.resize(kdhits.size());
0489   _kdtrees.resize(kdhits.size());
0490   for (size_t l = 0; l < kdhits.size(); ++l)
0491   {
0492     if (Verbosity() > 1)
0493     {
0494       std::cout << "l: " << l << std::endl;
0495     }
0496     _ptclouds[l] = std::make_shared<KDPointCloud<double>>();
0497     _ptclouds[l]->pts = std::move(kdhits[l]);
0498 
0499     _kdtrees[l] = std::make_shared<nanoflann::KDTreeSingleIndexAdaptor<nanoflann::L2_Simple_Adaptor<double, KDPointCloud<double>>, KDPointCloud<double>, 3>>(3, *(_ptclouds[l]), nanoflann::KDTreeSingleIndexAdaptorParams(10));
0500     _kdtrees[l]->buildIndex();
0501   }
0502 
0503   return globalPositions;
0504 }
0505 
0506 bool PHSimpleKFProp::TransportAndRotate(
0507   double old_radius,
0508   double new_radius,
0509   double& phi,
0510   GPUTPCTrackParam& kftrack,
0511   GPUTPCTrackParam::GPUTPCTrackFitParam& fp) const
0512 {
0513   if (Verbosity() > 2)
0514   {
0515     std::cout << "old_radius " << old_radius << ", new_radius " << new_radius << std::endl;
0516   }
0517   const float transport_spacing = .05;  // max radial distance between transport points
0518   const int Ndivisions = floor(std::abs(new_radius - old_radius) / transport_spacing);
0519   if (Verbosity() > 2)
0520   {
0521     std::cout << "Ndivisions: " << Ndivisions << std::endl;
0522   }
0523   for (int i = 1; i <= Ndivisions + 1; i++)
0524   {
0525     if (std::isnan(kftrack.GetX()) ||
0526         std::isnan(kftrack.GetY()) ||
0527         std::isnan(kftrack.GetZ()))
0528     {
0529       if (Verbosity() > 1)
0530       {
0531         std::cout << "position is NaN, exiting" << std::endl;
0532       }
0533       return false;
0534     }
0535 
0536     if (new_radius > 78.)
0537     {
0538       if (Verbosity() > 1)
0539       {
0540         std::cout << "outside TPC, exiting" << std::endl;
0541       }
0542       return false;
0543     }
0544 
0545     double r_div;
0546     if (i == Ndivisions + 1)
0547     {
0548       r_div = new_radius;
0549     }
0550     else if (old_radius < new_radius)
0551     {
0552       r_div = old_radius + transport_spacing * i;
0553     }
0554     else
0555     {
0556       r_div = old_radius - transport_spacing * i;
0557     }
0558 
0559     if (Verbosity() > 2)
0560     {
0561       std::cout << "transporting to intermediate radius " << r_div << std::endl;
0562     }
0563 
0564     // track state position relative to radial direction
0565     const double tX = kftrack.GetX();
0566     const double tY = kftrack.GetY();
0567     const double tz = kftrack.GetZ();
0568 
0569     // track state global position (including tz above)
0570     const double tx = tX * cos(phi) - tY * sin(phi);
0571     const double ty = tX * sin(phi) + tY * cos(phi);
0572 
0573     const double Bz = _Bzconst * fitter->get_Bz(tx, ty, tz);
0574 
0575     kftrack.CalculateFitParameters(fp);
0576 
0577     // transport to radius
0578     if (!kftrack.TransportToXWithMaterial(r_div, fp, Bz, 1.))
0579     {
0580       if (Verbosity() > 1)
0581       {
0582         std::cout << "transport failed" << std::endl;
0583       }
0584       return false;
0585     }
0586     // rotate track state reference frame
0587     const double new_tX = kftrack.GetX();
0588     const double new_tY = kftrack.GetY();
0589 
0590     const double new_tx = new_tX * cos(phi) - new_tY * sin(phi);
0591     const double new_ty = new_tX * sin(phi) + new_tY * cos(phi);
0592     const double new_phi = atan2(new_ty, new_tx);
0593     const double alpha = new_phi - phi;
0594 
0595     if (!kftrack.Rotate(alpha, 1.))
0596     {
0597       if (Verbosity() > 1)
0598       {
0599         std::cout << "rotate failed" << std::endl;
0600       }
0601       return false;
0602     }
0603     phi = new_phi;
0604   }
0605 
0606   if (Verbosity() > 1)
0607   {
0608     const double final_tX = kftrack.GetX();
0609     const double final_tY = kftrack.GetY();
0610     const double final_tx = final_tX * cos(phi) - final_tY * sin(phi);
0611     const double final_ty = final_tX * sin(phi) + final_tY * cos(phi);
0612     const double final_tz = kftrack.GetZ();
0613     std::cout << "track position after transport: (" << final_tx << ", " << final_ty << ", " << final_tz << ")" << std::endl;
0614   }
0615   return true;
0616 }
0617 
0618 //___________________________________________________________________________________________
0619 bool PHSimpleKFProp::PropagateStep(
0620   unsigned int& current_layer,
0621   double& current_phi,
0622   PropagationDirection& direction,
0623   std::vector<TrkrDefs::cluskey>& propagated_track,
0624   const std::vector<TrkrDefs::cluskey>& ckeys,
0625   GPUTPCTrackParam& kftrack,
0626   GPUTPCTrackParam::GPUTPCTrackFitParam& fp,
0627   const PositionMap& globalPositions) const
0628 {
0629   // give up if position vector is NaN (propagation failed)
0630   if (std::isnan(kftrack.GetX()) ||
0631       std::isnan(kftrack.GetY()) ||
0632       std::isnan(kftrack.GetZ()))
0633   {
0634     if (Verbosity() > 1)
0635     {
0636       std::cout << "position is NaN, exiting loop" << std::endl;
0637     }
0638     return false;
0639   }
0640 
0641   if (Verbosity() > 1)
0642   {
0643     std::cout << "current layer: " << current_layer << std::endl;
0644   }
0645   if (Verbosity() > 5)
0646   {
0647     std::cout << "original seed size: " << ckeys.size() << std::endl;
0648   }
0649 
0650   // CosPhi is the projection of the pt onto the radial direction
0651   // based on the sign of CosPhi, decide whether to propagate outward or inward
0652   int next_layer;
0653   if (direction == PropagationDirection::Outward)
0654   {
0655     next_layer = current_layer + 1;
0656   }
0657   else
0658   {
0659     next_layer = current_layer - 1;
0660   }
0661 
0662   if (Verbosity() > 1)
0663   {
0664     std::cout << "next layer: " << next_layer << std::endl;
0665   }
0666 
0667   // give up if next layer is outside the TPC (propagation complete)
0668   if (next_layer < 7 || next_layer > 54)
0669   {
0670     if (Verbosity() > 1)
0671     {
0672       std::cout << "reached end of TPC, exiting loop" << std::endl;
0673     }
0674     return false;
0675   }
0676 
0677   // track state position relative to radial direction
0678   const double tX = kftrack.GetX();
0679   const double tY = kftrack.GetY();
0680   const double tz = kftrack.GetZ();
0681 
0682   // track state global position (including tz above)
0683   const double tx = tX * cos(current_phi) - tY * sin(current_phi);
0684   const double ty = tX * sin(current_phi) + tY * cos(current_phi);
0685 
0686   if (Verbosity() > 1)
0687   {
0688     std::cout << std::endl;
0689     std::cout << "track position: (" << tx << ", " << ty << ", " << tz << ")" << std::endl;
0690   }
0691 
0692   if (Verbosity() > 1)
0693   {
0694     std::cout << "transporting to radius: " << radii[next_layer - 7] << std::endl;
0695   }
0696 
0697   if (!TransportAndRotate(kftrack.GetX(), radii[next_layer - 7], current_phi, kftrack, fp))
0698   {
0699     if (std::isnan(kftrack.GetX()) ||
0700         std::isnan(kftrack.GetY()) ||
0701         std::isnan(kftrack.GetZ()))
0702     {
0703       return false;
0704     }
0705 
0706     if (Verbosity() > 1)
0707     {
0708       std::cout << "track turned around near X=" << kftrack.GetX() << std::endl;
0709     }
0710 
0711     // TransportAndRotate failure indicates that track has turned around before it reaches next layer
0712     // The track parameters don't update in that last step, so we're likely somewhere between two layers
0713     // So, first, we turn the track around ourselves, setting it to its next intersection at its current radius
0714 
0715     // basically circle project in xy, linear project in z
0716     double pt = 1. / std::abs(kftrack.GetQPt());
0717     double end_tx = kftrack.GetX() * cos(current_phi) - kftrack.GetY() * sin(current_phi);
0718     double end_ty = kftrack.GetX() * sin(current_phi) + kftrack.GetY() * cos(current_phi);
0719     double end_tz = kftrack.GetZ();
0720     if (Verbosity() > 1)
0721     {
0722       std::cout << "current parameters: pt=" << pt << ", (x, y, z) = (" << end_tx << ", " << end_ty << ", " << end_tz << ")" << std::endl;
0723     }
0724     // pt[GeV] = 0.3 B[T] R[m]
0725     double R = 100. * pt / (0.3 * fitter->get_Bz(end_tx, end_ty, end_tz));
0726     if (Verbosity() > 1)
0727     {
0728       std::cout << "R=" << R << std::endl;
0729     }
0730     double pX = pt * kftrack.GetCosPhi();
0731     double pY = pt * kftrack.GetSinPhi();
0732     if (Verbosity() > 1)
0733     {
0734       std::cout << "(pX, pY) = (" << pX << ", " << pY << ")" << std::endl;
0735     }
0736     double px = pX * cos(current_phi) - pY * sin(current_phi);
0737     double py = pX * sin(current_phi) + pY * cos(current_phi);
0738     double tangent_phi = std::atan2(py, px);
0739     double center_phi = 0;
0740     if (kftrack.GetQPt() > 0)
0741     {
0742       center_phi = tangent_phi + M_PI / 2.;
0743     }
0744     else
0745     {
0746       center_phi = tangent_phi - M_PI / 2.;
0747     }
0748     if (center_phi > M_PI)
0749     {
0750       center_phi -= 2. * M_PI;
0751     }
0752     if (center_phi < -M_PI)
0753     {
0754       center_phi += 2. * M_PI;
0755     }
0756     double xc = end_tx - R * cos(center_phi);
0757     double yc = end_ty - R * sin(center_phi);
0758     if (Verbosity() > 1)
0759     {
0760       std::cout << "(xc, yc) = (" << xc << ", " << yc << ")" << std::endl;
0761     }
0762     auto circle_output = TrackFitUtils::circle_circle_intersection(
0763       std::sqrt(square(kftrack.GetX()) + square(kftrack.GetY())), R, xc, yc);
0764     // pick the furthest point from current track position
0765     double new_tx;
0766     double new_ty;
0767     double xplus = std::get<0>(circle_output);
0768     double yplus = std::get<1>(circle_output);
0769     double xminus = std::get<2>(circle_output);
0770     double yminus = std::get<3>(circle_output);
0771     if (Verbosity() > 1)
0772     {
0773       std::cout << "circle-circle intersection: (" << xplus << ", " << yplus << "), (" << xminus << ", " << yminus << ")" << std::endl;
0774     }
0775 
0776     if (std::sqrt(square(end_tx - xplus) + square(end_ty - yplus)) > std::sqrt(square(end_tx - xminus) + square(end_ty - yminus)))
0777     {
0778       new_tx = xplus;
0779       new_ty = yplus;
0780     }
0781     else
0782     {
0783       new_tx = xminus;
0784       new_ty = yminus;
0785     }
0786 
0787     if (Verbosity() > 1)
0788     {
0789       std::cout << "track now at (" << new_tx << ", " << new_ty << ")" << std::endl;
0790     }
0791     const double rot_phi = atan2(new_ty, new_tx);
0792     // double rot_alpha = rot_phi - current_phi;
0793 
0794     // new track point is existing track point rotated by alpha
0795     const double new_tX = new_tx * cos(rot_phi) + new_ty * sin(rot_phi);
0796     const double new_tY = -new_tx * sin(rot_phi) + new_ty * cos(rot_phi);
0797     const double new_centerphi = atan2(new_ty - yc, new_tx - xc);
0798     double dcenterphi = new_centerphi - center_phi;
0799     if (dcenterphi > M_PI)
0800     {
0801       dcenterphi = 2. * M_PI - dcenterphi;
0802     }
0803     if (dcenterphi < -M_PI)
0804     {
0805       dcenterphi = 2. * M_PI + dcenterphi;
0806     }
0807     const double ds = R * std::abs(dcenterphi);
0808     const double dz = kftrack.GetDzDs() * ds;
0809 
0810     current_phi = rot_phi;
0811     kftrack.SetX(new_tX);
0812     kftrack.SetY(new_tY);
0813     kftrack.SetZ(end_tz + dz);
0814 
0815     kftrack.SetSignCosPhi(-kftrack.GetSignCosPhi());
0816 
0817     // now finish transport back down to current layer
0818     if (!TransportAndRotate(kftrack.GetX(), radii[current_layer - 7], current_phi, kftrack, fp))
0819     {
0820       return false;
0821     }
0822 
0823     if (direction == PropagationDirection::Outward)
0824     {
0825       direction = PropagationDirection::Inward;
0826     }
0827     else
0828     {
0829       direction = PropagationDirection::Outward;
0830     }
0831 
0832     // track landed in same layer for this step
0833     next_layer = current_layer;
0834   }
0835 
0836   // account for distortions
0837   if(!_pp_mode)
0838   {
0839     if (Verbosity() > 1)
0840     {
0841       std::cout << "doing distortion correction" << std::endl;
0842     }
0843 
0844     // The distortion corrected cluster positions in globalPos are not at the layer radius
0845     // We want to project to the radius appropriate for the globalPos values
0846     // Get the distortion correction for the projection point, and calculate the radial increment
0847 
0848     const double uncorr_tX = kftrack.GetX();
0849     const double uncorr_tY = kftrack.GetY();
0850     const double uncorr_tx = uncorr_tX * cos(current_phi) - uncorr_tY * sin(current_phi);
0851     const double uncorr_ty = uncorr_tX * sin(current_phi) + uncorr_tY * cos(current_phi);
0852     const double uncorr_tz = kftrack.GetZ();
0853 
0854     Acts::Vector3 proj_pt(uncorr_tx, uncorr_ty, uncorr_tz);
0855 
0856     const double proj_radius = sqrt(square(uncorr_tx) + square(uncorr_ty));
0857     if (proj_radius > 78.0)
0858     {
0859       // projection is bad, no cluster will be found
0860       return false;
0861     }
0862 
0863     if (Verbosity() > 2)
0864     {
0865       std::cout << "distortion correction for (" << tx << ", " << ty << ", " << tz << "), layer " << next_layer << ", radius " << proj_radius << std::endl;
0866     }
0867 
0868     // apply distortion corrections
0869     proj_pt = m_globalPositionWrapper.applyDistortionCorrections(proj_pt);
0870 
0871     // calculate radius
0872     const double dist_radius = sqrt(square(proj_pt[0]) + square(proj_pt[1]));
0873 
0874     if (Verbosity() > 2)
0875     {
0876       std::cout << "after correction: (" << proj_pt[0] << ", " << proj_pt[1] << ", " << proj_pt[2] << "), radius " << dist_radius << std::endl;
0877     }
0878 
0879     if (!TransportAndRotate(kftrack.GetX(), dist_radius, current_phi, kftrack, fp))
0880     {
0881       return false;
0882     }
0883 
0884     if (std::isnan(kftrack.GetX()) ||
0885         std::isnan(kftrack.GetY()) ||
0886         std::isnan(kftrack.GetZ()))
0887     {
0888       return false;
0889     }
0890   }
0891 
0892   const double new_tX = kftrack.GetX();
0893   const double new_tY = kftrack.GetY();
0894   const double new_tx = new_tX * cos(current_phi) - new_tY * sin(current_phi);
0895   const double new_ty = new_tX * sin(current_phi) + new_tY * cos(current_phi);
0896   const double new_tz = kftrack.GetZ();
0897 
0898   // search for closest available cluster within window
0899   double query_pt[3] = {new_tx, new_ty, new_tz};
0900   std::vector<long unsigned int> index_out(1);
0901   std::vector<double> distance_out(1);
0902   int n_results = _kdtrees[next_layer]->knnSearch(&query_pt[0], 1, &index_out[0], &distance_out[0]);
0903 
0904   // if no results, then no cluster to add, but propagation is not necessarily done
0905   if (!n_results)
0906   {
0907     if (Verbosity() > 1)
0908     {
0909       std::cout << "no clusters found in search window, moving on" << std::endl;
0910     }
0911     current_layer = next_layer;
0912     return true;
0913   }
0914   const std::vector<double>& point = _ptclouds[next_layer]->pts[index_out[0]];
0915   TrkrDefs::cluskey closest_ckey = (*((int64_t*) &point[3]));
0916   TrkrCluster* clusterCandidate = _cluster_map->findCluster(closest_ckey);
0917   const auto candidate_globalpos = globalPositions.at(closest_ckey);
0918   const double cand_x = candidate_globalpos(0);
0919   const double cand_y = candidate_globalpos(1);
0920   const double cand_z = candidate_globalpos(2);
0921 
0922   if (Verbosity() > 1)
0923   {
0924     std::cout << "found closest cluster candidate at (" << cand_x << ", " << cand_y << ", " << cand_z << ")" << std::endl;
0925   }
0926 
0927   // get cluster and track state position errors
0928   const double tYerr = std::sqrt(kftrack.GetCov(0));
0929   const double txerr = std::abs(tYerr * sin(current_phi));
0930   const double tyerr = std::abs(tYerr * cos(current_phi));
0931   const double tzerr = sqrt(kftrack.GetCov(5));
0932 
0933   if (Verbosity() > 1)
0934   {
0935     std::cout << "track state position errors: (" << txerr << ", " << tyerr << ", " << tzerr << ")" << std::endl;
0936   }
0937 
0938   const double cand_xerr = sqrt(fitter->getClusterError(clusterCandidate, closest_ckey, candidate_globalpos, 0, 0));
0939   const double cand_yerr = sqrt(fitter->getClusterError(clusterCandidate, closest_ckey, candidate_globalpos, 1, 1));
0940   const double cand_zerr = sqrt(fitter->getClusterError(clusterCandidate, closest_ckey, candidate_globalpos, 2, 2));
0941 
0942   if (Verbosity() > 1)
0943   {
0944     std::cout << "cluster position errors: (" << cand_xerr << ", " << cand_yerr << ", " << cand_zerr << ")" << std::endl;
0945   }
0946 
0947   // add cluster if its position is within error-based window
0948   if (std::abs(new_tx - cand_x) < _max_dist * std::sqrt(square(txerr) + square(cand_xerr)) &&
0949       std::abs(new_ty - cand_y) < _max_dist * std::sqrt(square(tyerr) + square(cand_yerr)) &&
0950       std::abs(new_tz - cand_z) < _max_dist * std::sqrt(square(tzerr) + square(cand_zerr)))
0951   {
0952     if (Verbosity() > 1)
0953     {
0954       std::cout << "added cluster" << std::endl;
0955     }
0956     propagated_track.push_back(closest_ckey);
0957 
0958     // don't re-filter clusters that are already in original seed
0959     if (std::find(ckeys.begin(), ckeys.end(), closest_ckey) == ckeys.end())
0960     {
0961       const double cand_Y = -cand_x * std::sin(current_phi) + cand_y * std::cos(current_phi);
0962       const double cand_xycov2 = fitter->getClusterError(clusterCandidate, closest_ckey, candidate_globalpos, 0, 1);
0963       const double cand_Yerr2 = cand_xerr * cand_xerr * sin(current_phi) * sin(current_phi) + cand_xycov2 * sin(current_phi) * cos(current_phi) + cand_yerr * cand_yerr * cos(current_phi) * cos(current_phi);
0964       const double cand_zerr2 = cand_zerr * cand_zerr;
0965 
0966       if (Verbosity() > 1)
0967       {
0968         std::cout << "Filtering cluster with Y=" << cand_Y << ", z=" << cand_z << ", Yerr2=" << cand_Yerr2 << ", zerr2=" << cand_zerr2 << std::endl;
0969       }
0970 
0971       kftrack.Filter(cand_Y, cand_z, cand_Yerr2, cand_zerr2, _max_sin_phi);
0972     }
0973   }
0974 
0975   // update layer
0976   current_layer = next_layer;
0977 
0978   if (Verbosity() > 1)
0979   {
0980     std::cout << "track current parameters:" << std::endl;
0981     std::cout << "(X, Y, Z) = (" << kftrack.GetX() << ", " << kftrack.GetY() << ", " << kftrack.GetZ() << ")" << std::endl;
0982     std::cout << "pt = " << 1. / fabs(kftrack.GetQPt()) << std::endl;
0983     std::cout << "sinPhi = " << kftrack.GetSinPhi() << std::endl;
0984     std::cout << "cosPhi = " << kftrack.GetCosPhi() << std::endl;
0985     std::cout << "dzds = " << kftrack.GetDzDs() << std::endl;
0986   }
0987 
0988   // propagation step done
0989   return true;
0990 }
0991 
0992 //_________________________________________________________________
0993 std::vector<TrkrDefs::cluskey> PHSimpleKFProp::PropagateTrack(TrackSeed* track, PropagationDirection direction, GPUTPCTrackParam& aliceSeed, const PositionMap& globalPositions) const
0994 {
0995   // extract cluster list
0996   std::vector<TrkrDefs::cluskey> ckeys;
0997   if (direction == PropagationDirection::Inward)
0998   {
0999     std::reverse_copy(track->begin_cluster_keys(), track->end_cluster_keys(), std::back_inserter(ckeys));
1000   } else {
1001     std::copy(track->begin_cluster_keys(), track->end_cluster_keys(), std::back_inserter(ckeys));
1002   }
1003   return PropagateTrack(track, ckeys, direction, aliceSeed, globalPositions);
1004 }
1005 
1006 //_________________________________________________________________
1007 std::vector<TrkrDefs::cluskey> PHSimpleKFProp::PropagateTrack(TrackSeed* track, std::vector<TrkrDefs::cluskey>& ckeys, PropagationDirection direction, GPUTPCTrackParam& aliceSeed, const PositionMap& globalPositions) const
1008 {
1009   if (direction == PropagationDirection::Inward)
1010   {
1011     aliceSeed.SetDzDs(-aliceSeed.GetDzDs());
1012   }
1013 
1014   const auto track_pos = TrackSeedHelper::get_xyz(track);
1015   double track_x = track_pos.x();
1016   double track_y = track_pos.y();
1017   double track_z = track_pos.z();
1018 
1019   if (Verbosity() > 1)
1020   {
1021     std::cout << "layers:" << std::endl;
1022     for (auto& c : ckeys)
1023     {
1024       std::cout << (int) TrkrDefs::getLayer(c) << ", ";
1025     }
1026     std::cout << std::endl;
1027   }
1028 
1029   if (Verbosity() > 1)
1030   {
1031     std::cout << "track (x,y,z) = (" << track_x << ", " << track_y << ", " << track_z << ")" << std::endl;
1032   }
1033 
1034   double track_px = track->get_px();
1035   double track_py = track->get_py();
1036   double track_pz = track->get_pz();
1037 
1038   if (Verbosity() > 1)
1039   {
1040     std::cout << "track (px,py,pz) = (" << track_px << ", " << track_py << ", " << track_pz << ")" << std::endl;
1041   }
1042 
1043   std::vector<Acts::Vector3> trkGlobPos;
1044   for (const auto& ckey : ckeys)
1045   {
1046     if (TrkrDefs::getTrkrId(ckey) == TrkrDefs::tpcId)
1047     {
1048       trkGlobPos.push_back(globalPositions.at(ckey));
1049     }
1050   }
1051 
1052   // want angle of tangent to circle at innermost (i.e. last) cluster
1053   size_t inner_index;
1054   size_t second_index;
1055   if (TrkrDefs::getLayer(ckeys[0]) > TrkrDefs::getLayer(ckeys.back()))
1056   {
1057     inner_index = ckeys.size() - 1;
1058     second_index = ckeys.size() - 2;
1059   }
1060   else
1061   {
1062     inner_index = 0;
1063     second_index = 1;
1064   }
1065 
1066   const double xc = track->get_X0();
1067   const double yc = track->get_Y0();
1068   const double cluster_x = trkGlobPos.at(inner_index)(0);
1069   const double cluster_y = trkGlobPos.at(inner_index)(1);
1070   const double dy = cluster_y - yc;
1071   const double dx = cluster_x - xc;
1072 
1073   double phi = std::atan2(dy, dx);
1074   const double second_dx = trkGlobPos.at(second_index)(0) - xc;
1075   const double second_dy = trkGlobPos.at(second_index)(1) - yc;
1076   const double second_phi = std::atan2(second_dy, second_dx);
1077   const double dphi = second_phi - phi;
1078 
1079   if (dphi > 0)
1080   {
1081     phi += M_PI / 2.0;
1082   }
1083   else
1084   {
1085     phi -= M_PI / 2.0;
1086   }
1087 
1088   const double pt = std::sqrt(square(track_px)+square(track_py));
1089 
1090   // rotate track momentum vector (pz stays the same)
1091   track_px = pt * cos(phi);
1092   track_py = pt * sin(phi);
1093   track_x = trkGlobPos.at(0)(0);
1094   track_y = trkGlobPos.at(0)(1);
1095   track_z = trkGlobPos.at(0)(2);
1096 
1097   if (Verbosity() > 1)
1098   {
1099     std::cout << "new track (x,y,z) = " << track_x << ", " << track_y << ", " << track_z << ")" << std::endl;
1100   }
1101   if (Verbosity() > 1)
1102   {
1103     std::cout << "new track (px,py,pz) = " << track_px << ", " << track_py << ", " << track_pz << ")" << std::endl;
1104   }
1105 
1106   //    }
1107 
1108   double track_pt = sqrt(track_px * track_px + track_py * track_py);
1109   if (Verbosity() > 1)
1110   {
1111     std::cout << "track pt: " << track_pt << std::endl;
1112   }
1113 
1114   // get track parameters
1115   GPUTPCTrackParam kftrack{};
1116   kftrack.InitParam();
1117   kftrack.setNeonFraction(Ne_frac);
1118   kftrack.setArgonFraction(Ar_frac);
1119   kftrack.setCF4Fraction(CF4_frac);
1120   kftrack.setNitrogenFraction(N2_frac);
1121   kftrack.setIsobutaneFraction(isobutane_frac);
1122 
1123   float track_phi = atan2(track_y, track_x);
1124   if (Verbosity() > 1)
1125   {
1126     std::cout << "track phi: " << track_phi << std::endl;
1127     std::cout << "track charge: " << track->get_charge() << std::endl;
1128   }
1129   kftrack.SetQPt(track->get_charge() / track_pt);
1130 
1131   float track_pX = track_px * std::cos(track_phi) + track_py * std::sin(track_phi);
1132   float track_pY = -track_px * std::sin(track_phi) + track_py * std::cos(track_phi);
1133 
1134   kftrack.SetSignCosPhi(track_pX / track_pt);
1135   kftrack.SetSinPhi(track_pY / track_pt);
1136   kftrack.SetDzDs(track_pz / sqrt(track_pt * track_pt + track_pz * track_pz));
1137 
1138   if (Verbosity() > 1)
1139   {
1140     std::cout << "track pX = " << track_pX << ", pY = " << track_pY << ", CosPhi = " << track_pX / track_pt << ", signCosPhi = " << kftrack.GetSignCosPhi() << std::endl;
1141   }
1142   /*
1143     // Y = y
1144     // Z = z
1145     // SinPhi = py/sqrt(px^2+py^2)
1146     // DzDs = pz/sqrt(px^2+py^2)
1147     // QPt = 1/sqrt(px^2+py^2)
1148 
1149     const double track_pt3 = std::pow(track_pt, 3.);
1150 
1151     Eigen::Matrix<double, 6, 5> Jrot;
1152     Jrot(0, 0) = 0;  // dY/dx
1153     Jrot(1, 0) = 1;  // dY/dy
1154     Jrot(2, 0) = 0;  // dY/dz
1155     Jrot(3, 0) = 0;  // dY/dpx
1156     Jrot(4, 0) = 0;  // dY/dpy
1157     Jrot(5, 0) = 0;  // dY/dpz
1158 
1159     Jrot(0, 1) = 0;  // dZ/dx
1160     Jrot(1, 1) = 0;  // dZ/dy
1161     Jrot(2, 1) = 1;  // dZ/dz
1162     Jrot(3, 1) = 0;  // dZ/dpx
1163     Jrot(4, 1) = 0;  // dZ/dpy
1164     Jrot(5, 1) = 0;  // dZ/dpz
1165 
1166     Jrot(0, 2) = 0;                                 // dSinPhi/dx
1167     Jrot(1, 2) = 0;                                 // dSinPhi/dy
1168     Jrot(2, 2) = 0;                                 // dSinPhi/dz
1169     Jrot(3, 2) = -track_py * track_px / track_pt3;  // dSinPhi/dpx
1170     Jrot(4, 2) = track_px * track_px / track_pt3;   // dSinPhi/dpy
1171     Jrot(5, 2) = 0;                                 // dSinPhi/dpz
1172 
1173     Jrot(0, 3) = 0;                                 // dDzDs/dx
1174     Jrot(1, 3) = 0;                                 // dDzDs/dy
1175     Jrot(2, 3) = 0;                                 // dDzDs/dz
1176     Jrot(3, 3) = -track_px * track_pz / track_pt3;  // dDzDs/dpx
1177     Jrot(4, 3) = -track_py * track_pz / track_pt3;  // dDzDs/dpy
1178     Jrot(5, 3) = 1. / track_pt;                     // dDzDs/dpz
1179 
1180     Jrot(0, 4) = 0;                      // dQPt/dx
1181     Jrot(1, 4) = 0;                      // dQPt/dy
1182     Jrot(2, 4) = 0;                      // dQPt/dz
1183     Jrot(3, 4) = -track_px / track_pt3;  // dQPt/dpx
1184     Jrot(4, 4) = -track_py / track_pt3;  // dQPt/dpy
1185     Jrot(5, 4) = 0;                      // dQPt/dpz
1186 
1187     Eigen::Matrix<double, 5, 5> kfCov = Jrot.transpose() * xyzCov * Jrot;
1188 
1189     int ctr = 0;
1190     for (int i = 0; i < 5; i++)
1191     {
1192       for (int j = 0; j < 5; j++)
1193       {
1194         if (i >= j)
1195         {
1196           kftrack.SetCov(ctr, kfCov(i, j));
1197           ctr++;
1198         }
1199       }
1200     }
1201   */
1202   std::vector<TrkrDefs::cluskey> propagated_track;
1203 
1204   kftrack.SetX(track_x * std::cos(track_phi) + track_y * std::sin(track_phi));
1205   kftrack.SetY(-track_x * std::sin(track_phi) + track_y * std::cos(track_phi));
1206   kftrack.SetZ(track_z);
1207 
1208   if (kftrack.GetSignCosPhi() < 0)
1209   {
1210     kftrack.SetSinPhi(-kftrack.GetSinPhi());
1211     kftrack.SetDzDs(-kftrack.GetDzDs());
1212     // kftrack.SetQPt(-kftrack.GetQPt());
1213     kftrack.SetCov(3, -kftrack.GetCov(3));
1214     kftrack.SetCov(4, -kftrack.GetCov(4));
1215     kftrack.SetCov(6, -kftrack.GetCov(6));
1216     kftrack.SetCov(7, -kftrack.GetCov(7));
1217     kftrack.SetCov(10, -kftrack.GetCov(10));
1218     kftrack.SetCov(11, -kftrack.GetCov(11));
1219   }
1220 
1221   if (Verbosity() > 1)
1222   {
1223     std::cout << "initial track params:" << std::endl;
1224     std::cout << "X: " << kftrack.GetX() << std::endl;
1225     std::cout << "Y: " << kftrack.GetY() << std::endl;
1226     std::cout << "Z: " << kftrack.GetZ() << std::endl;
1227     std::cout << "SinPhi: " << kftrack.GetSinPhi() << std::endl;
1228     std::cout << "DzDs: " << kftrack.GetDzDs() << std::endl;
1229     std::cout << "QPt: " << kftrack.GetQPt() << std::endl;
1230     std::cout << "cov: " << std::endl;
1231     for (int i = 0; i < 15; i++)
1232     {
1233       std::cout << kftrack.GetCov(i) << ", ";
1234     }
1235     std::cout << std::endl;
1236   }
1237 
1238   // get layer for each cluster
1239   std::vector<unsigned int> layers;
1240   std::transform(ckeys.begin(), ckeys.end(), std::back_inserter(layers), [](const TrkrDefs::cluskey& key)
1241                  { return TrkrDefs::getLayer(key); });
1242 
1243   double old_phi = track_phi;
1244   unsigned int old_layer = TrkrDefs::getLayer(ckeys[0]);
1245   if (Verbosity() > 1)
1246   {
1247     std::cout << "first layer: " << old_layer << std::endl;
1248     std::cout << "cluster (x,y,z) = (" << globalPositions.at(ckeys[0])(0) << ", " << globalPositions.at(ckeys[0])(1) << ", " << globalPositions.at(ckeys[0])(2) << ")" << std::endl;
1249   }
1250 
1251   propagated_track.push_back(ckeys[0]);
1252 
1253   GPUTPCTrackLinearisation kfline(aliceSeed);
1254   GPUTPCTrackParam::GPUTPCTrackFitParam fp{};
1255   aliceSeed.CalculateFitParameters(fp);
1256 
1257   for (int step = 0; step <= _max_propagation_steps; ++step)
1258   {
1259     if (Verbosity() > 1)
1260     {
1261       std::cout << std::endl
1262                 << "------------------------" << std::endl
1263                 << "step " << step << std::endl;
1264     }
1265     if (!PropagateStep(old_layer, old_phi, direction, propagated_track, ckeys, aliceSeed, fp, globalPositions))
1266     {
1267       break;
1268     }
1269   }
1270 
1271   if (Verbosity() > 1)
1272   {
1273     std::cout << "propagated track has " << propagated_track.size() << " clusters, at layers: ";
1274     for (auto c : propagated_track)
1275     {
1276       std::cout << (int) TrkrDefs::getLayer(c) << ", ";
1277     }
1278     std::cout << std::endl;
1279   }
1280   return propagated_track;
1281 }
1282 
1283 std::vector<keylist> PHSimpleKFProp::RemoveBadClusters(const std::vector<keylist>& chains, const PositionMap& /* globalPositions */) const
1284 {
1285   if (Verbosity())
1286   {
1287     std::cout << "removing bad clusters" << std::endl;
1288   }
1289   std::vector<keylist> clean_chains;
1290   /*
1291     Hugo: this whole code just copy chains of size >= 3 to the output,
1292     because of the cut on outliers being commented out.
1293     Replaced with a simpler version
1294   */
1295   //   for(const keylist& chain : chains)
1296   //   {
1297   //     if(chain.size()<3) continue;
1298   //
1299   //     keylist clean_chain;
1300   //
1301   //
1302   //     std::vector<std::pair<double,double>> xy_pts;
1303   //     std::vector<std::pair<double,double>> rz_pts;
1304   //     for(const TrkrDefs::cluskey& ckey : chain)
1305   //     {
1306   //       auto global = globalPositions.at(ckey);
1307   //       xy_pts.push_back(std::make_pair(global(0),global(1)));
1308   //       float r = sqrt(square( global(0) ) + square( global(1) ));
1309   //       rz_pts.emplace_back( r,global(2) );
1310   //     }
1311   //     if(Verbosity()) std::cout << "chain size: " << chain.size() << std::endl;
1312   //
1313   //     //fit a circle through x,y coordinates and calculate residuals
1314   //     const auto [R, X0, Y0] = TrackFitUtils::circle_fit_by_taubin( xy_pts );
1315   //     const std::vector<double> xy_resid = TrackFitUtils::getCircleClusterResiduals(xy_pts,R,X0,Y0);
1316   //
1317   //     // fit a line through r,z coordinates and calculate residuals
1318   //     const auto [A, B] = TrackFitUtils::line_fit( rz_pts );
1319   //     const std::vector<double> rz_resid = TrackFitUtils::getLineClusterResiduals(rz_pts,A,B);
1320   //
1321   //     for(size_t i=0;i<chain.size();i++)
1322   //     {
1323   //       //if(xy_resid[i]>_xy_outlier_threshold) continue;
1324   //       clean_chain.push_back(chain[i]);
1325   //     }
1326   //
1327   //     clean_chains.push_back(clean_chain);
1328   //     if(Verbosity()) std::cout << "pushed clean chain with " << clean_chain.size() << " clusters" << std::endl;
1329   //   }
1330 
1331   // simpler version
1332   std::copy_if(chains.begin(), chains.end(), std::back_inserter(clean_chains),
1333                [](const keylist& chain)
1334                { return chain.size() >= 3; });
1335 
1336   return clean_chains;
1337 }
1338 
1339 void PHSimpleKFProp::rejectAndPublishSeeds(std::vector<TrackSeed_v2>& seeds, const PositionMap& positions, std::vector<float>& trackChi2)
1340 {
1341 
1342   PHTimer timer("KFPropTimer");
1343 
1344   // now do the ghost rejection *before* publishing the seeds to the _track_map
1345   timer.restart();
1346 
1347   // testing with presets for rejection
1348   PHGhostRejection rejector(Verbosity(), seeds);
1349   rejector.set_phi_cut(_ghost_phi_cut);
1350   rejector.set_eta_cut(_ghost_eta_cut);
1351   rejector.set_x_cut(_ghost_x_cut);
1352   rejector.set_y_cut(_ghost_y_cut);
1353   rejector.set_z_cut(_ghost_z_cut);
1354   // If you want to reject tracks (before they are are made) can set them here:
1355   // rejector.set_min_pt_cut(0.2);
1356   // rejector.set_must_span_sectors(true);
1357   // rejector.set_min_clusters(8);
1358 
1359   // first path over seeds to marks those to be removed due to cluster size
1360   for (unsigned int itrack = 0; itrack < seeds.size(); ++itrack)
1361   { rejector.cut_from_clusters(itrack); }
1362 
1363   #pragma omp parallel
1364   {
1365 
1366     #pragma omp for schedule(static)
1367     for (unsigned int itrack = 0; itrack < seeds.size(); ++itrack)
1368     {
1369       // cut tracks with too-few clusters (or that don;t span a sector boundary, if desired)
1370       if (rejector.is_rejected(itrack))
1371       { continue; }
1372 
1373       auto& seed = seeds[itrack];
1374       /// The ALICEKF gives a better charge determination at high pT
1375       const int q = seed.get_charge();
1376 
1377       PositionMap local;
1378       std::transform(seed.begin_cluster_keys(), seed.end_cluster_keys(), std::inserter(local, local.end()),
1379         [positions](const auto& key)
1380         { return std::make_pair(key, positions.at(key)); });
1381       TrackSeedHelper::circleFitByTaubin(&seed,local, 7, 55);
1382       TrackSeedHelper::lineFit(&seed,local, 7, 55);
1383       seed.set_phi(TrackSeedHelper::get_phi(&seed,local));
1384       seed.set_qOverR(std::abs(seed.get_qOverR()) * q);
1385     }
1386 
1387   }
1388 
1389   if (Verbosity())
1390   { std::cout << "PHSimpleKFProp::rejectAndPublishSeeds - circle fit: " << timer.elapsed() << " ms" << std::endl; }
1391 
1392   // now do the ghost rejection *before* publishing the seeds to the _track_map
1393   timer.restart();
1394   rejector.find_ghosts(trackChi2);
1395   if (Verbosity())
1396   { std::cout << "PHSimpleKFProp::rejectAndPublishSeeds - ghost rejection: " << timer.elapsed() << " ms" << std::endl; }
1397 
1398   timer.restart();
1399   for (unsigned int itrack = 0; itrack < seeds.size(); ++itrack)
1400   {
1401     if (rejector.is_rejected(itrack))
1402     {
1403       if (Verbosity() > 0)
1404       {
1405         std::cout << " Seed " << ((int) itrack) << " rejected. Not getting published." << std::endl;
1406       }
1407       continue;
1408     }
1409     auto& seed = seeds[itrack];
1410     _track_map->insert(&seed);
1411 
1412     int q = seed.get_charge();
1413     if (Verbosity() > 0)
1414     {
1415       std::cout << "Publishing seed " << ((int) itrack)
1416                 << " q " << q
1417                 << " qOverR " << std::abs(seed.get_qOverR()) * q
1418                 << " x " << TrackSeedHelper::get_x(&seed)
1419                 << " y " << TrackSeedHelper::get_y(&seed)
1420                 << " z " << TrackSeedHelper::get_z(&seed)
1421                 << " pT " << seed.get_pt()
1422                 << " eta " << seed.get_eta()
1423                 << " phi " << seed.get_phi()
1424                 << std::endl;
1425     }
1426   }
1427   if (Verbosity())
1428   { std::cout << "PHSimpleKFProp::rejectAndPublishSeeds - publication: " << timer.elapsed() << " ms" << std::endl; }
1429 
1430 }
1431 
1432 void PHSimpleKFProp::publishSeeds(const std::vector<TrackSeed_v2>& seeds)
1433 {
1434   for (const auto& seed : seeds)
1435   {
1436     _track_map->insert(&seed);
1437   }
1438 }