9 #include "art/Utilities/ToolMacros.h"
10 #include "art/Utilities/make_tool.h"
11 #include "art_root_io/TFileService.h"
12 #include "cetlib/cpu_timer.h"
13 #include "cetlib/search_path.h"
14 #include "fhiclcpp/ParameterSet.h"
15 #include "messagefacility/MessageLogger/MessageLogger.h"
31 #include <Eigen/Dense>
48 namespace lar_cluster3d {
64 void configure(fhicl::ParameterSet
const& pset)
override;
143 using BestNodeMap = std::unordered_map<const reco::ClusterHit3D*, BestNodeTuple>;
163 using MinMaxPoints = std::pair<reco::ProjectedPoint, reco::ProjectedPoint>;
187 std::unique_ptr<lar_cluster3d::IClusterParametersBuilder>
192 : fPCAAlg(pset.
get<fhicl::ParameterSet>(
"PrincipalComponentsAlg"))
193 , fkdTree(pset.
get<fhicl::ParameterSet>(
"kdTree"))
212 art::ServiceHandle<geo::Geometry const>
geometry;
218 fClusterBuilder = art::make_tool<lar_cluster3d::IClusterParametersBuilder>(
219 pset.get<fhicl::ParameterSet>(
"ClusterParamsBuilder"));
253 cet::cpu_timer theClockBuildClusters;
260 for (
auto& clusterParams : clusterParametersList) {
281 RunPrimsAlgorithm(clusterParams.getHitPairListPtr(), topNode, daughterParametersList);
284 cet::cpu_timer theClockBuildClusters;
292 theClockBuildClusters.stop();
298 for (
auto& daughterParams : daughterParametersList)
304 theClockBuildClusters.stop();
309 mf::LogDebug(
"MSTPathFinder") <<
">>>>> Cluster Path finding done" << std::endl;
321 if (hitPairList.empty())
return;
324 cet::cpu_timer theClockDBScan;
330 size_t clusterIdx(0);
336 reco::HitPairListPtr::const_iterator freeHitItr = hitPairList.begin();
357 for (reco::EdgeList::iterator curEdgeItr = curEdgeList.begin();
358 curEdgeItr != curEdgeList.end();) {
360 curEdgeItr = curEdgeList.erase(curEdgeItr);
366 curClusterHitList->push_back(lastAddedHit);
370 float bestDistance(1.5);
378 for (
auto& pair : CandPairList) {
381 pair.first * lastAddedHit->
getHitChiSquare() * pair.second->getHitChiSquare();
383 curEdgeList.push_back(
reco::EdgeTuple(lastAddedHit, pair.second, edgeWeight));
388 if (curEdgeList.empty()) {
389 std::cout <<
"-----------------------------------------------------------------------------"
392 std::cout <<
"**> Cluster idx: " << clusterIdx++ <<
" has " << curClusterHitList->size()
393 <<
" hits" << std::endl;
396 freeHitItr = std::find_if(freeHitItr, hitPairList.end(), [](
const auto&
hit) {
401 if (freeHitItr == hitPairList.end())
break;
403 std::cout <<
"##################################################################>"
404 "Processing another cluster"
410 curCluster = &clusterParametersList.back();
414 lastAddedHit = *freeHitItr++;
419 curEdgeList.sort([](
const auto&
left,
const auto&
right) {
420 return std::get<2>(
left) < std::get<2>(
right);
426 (*curEdgeMap)[std::get<0>(curEdge)].push_back(curEdge);
427 (*curEdgeMap)[std::get<1>(curEdge)].push_back(
428 reco::EdgeTuple(std::get<1>(curEdge), std::get<0>(curEdge), std::get<2>(curEdge)));
431 lastAddedHit = std::get<1>(curEdge);
436 theClockDBScan.stop();
448 float bestQuality(0.);
449 float aveNumEdges(0.);
450 size_t maxNumEdges(0);
451 size_t nIsolatedHits(0);
454 cet::cpu_timer theClockPathFinding;
464 for (
const auto&
hit : hitPairList) {
471 tempList.push_front(std::get<0>(curEdgeMap[
hit].
front()));
473 if (quality > bestQuality) {
474 longestCluster = tempList;
475 bestQuality = quality;
481 aveNumEdges += float(curEdgeMap[
hit].
size());
482 maxNumEdges = std::max(maxNumEdges, curEdgeMap[
hit].
size());
485 aveNumEdges /= float(hitPairList.size());
486 std::cout <<
"----> # isolated hits: " << nIsolatedHits
487 <<
", longest branch: " << longestCluster.size()
488 <<
", cluster size: " << hitPairList.size() <<
", ave # edges: " << aveNumEdges
489 <<
", max: " << maxNumEdges << std::endl;
491 if (!longestCluster.empty()) {
492 hitPairList = longestCluster;
493 for (
const auto&
hit : hitPairList) {
494 for (
const auto& edge : curEdgeMap[
hit])
495 bestEdgeList.emplace_back(edge);
498 std::cout <<
" ====> new cluster size: " << hitPairList.size() << std::endl;
502 theClockPathFinding.stop();
515 cet::cpu_timer theClockPathFinding;
537 float alpha = std::min(
float(1.), std::max(
float(0.001), pcaWidth / pcaLen));
543 for (
const auto& hit3D : curCluster) {
545 if (!curEdgeMap[hit3D].
empty() && curEdgeMap[hit3D].
size() == 1) {
546 Eigen::Vector3f pcaToHitVec(hit3D->getPosition()[0] - pcaCenter(0),
547 hit3D->getPosition()[1] - pcaCenter(1),
548 hit3D->getPosition()[2] - pcaCenter(2));
552 isolatedPointList.emplace_back(pcaToHit(2), pcaToHit(1), hit3D);
556 std::cout <<
"************* Finding best path with A* in cluster *****************"
558 std::cout <<
"**> There are " << curCluster.size() <<
" hits, " << isolatedPointList.size()
559 <<
" isolated hits, the alpha parameter is " << alpha << std::endl;
560 std::cout <<
"**> PCA len: " << pcaLen <<
", wid: " << pcaWidth <<
", height: " << pcaHeight
561 <<
", ratio: " << pcaHeight / pcaWidth << std::endl;
564 if (isolatedPointList.size() > 1) {
566 isolatedPointList.sort([](
const auto&
left,
const auto&
right) {
568 std::numeric_limits<float>::epsilon()) ?
577 std::cout <<
"**> Sorted " << isolatedPointList.size()
584 float cost(std::numeric_limits<float>::max());
591 <<
" hits, " << clusterParams.
getBestEdgeList().size() <<
" edges" << std::endl;
607 theClockPathFinding.stop();
637 bestNodeMap[startNode] =
642 while (!openList.empty()) {
644 reco::HitPairListPtr::iterator currentNodeItr = openList.begin();
647 if (openList.size() > 1)
648 currentNodeItr = std::min_element(
649 openList.begin(), openList.end(), [bestNodeMap](
const auto& next,
const auto& best) {
650 return std::get<2>(bestNodeMap.at(next)) < std::get<2>(bestNodeMap.at(best));
657 if (currentNode == goalNode) {
666 openList.erase(currentNodeItr);
670 const BestNodeTuple& currentNodeTuple = bestNodeMap.at(currentNode);
671 float currentNodeScore = std::get<1>(currentNodeTuple);
676 for (
const auto& curEdge : curEdgeList) {
681 float tentative_gScore = currentNodeScore + std::get<2>(curEdge);
684 BestNodeMap::iterator candNodeItr = bestNodeMap.find(candHit3D);
686 if (candNodeItr == bestNodeMap.end()) { openList.push_back(candHit3D); }
687 else if (tentative_gScore > std::get<1>(candNodeItr->second))
693 bestNodeMap[candHit3D] =
694 BestNodeTuple(currentNode, tentative_gScore, tentative_gScore + guessToTarget);
708 while (std::get<0>(bestNodeMap.at(goalNode)) != goalNode) {
713 pathNodeList.push_front(goalNode);
714 bestEdgeList.push_front(bestEdge);
719 pathNodeList.push_front(goalNode);
728 float& showMeTheMoney)
const
733 reco::Hit3DToEdgeMap::const_iterator edgeListItr = curEdgeMap.find(std::get<1>(curEdge));
735 showMeTheMoney = std::numeric_limits<float>::max();
737 if (edgeListItr != curEdgeMap.end() && !edgeListItr->second.empty()) {
741 for (
const auto& edge : edgeListItr->second) {
743 if (std::get<1>(edge) == std::get<0>(curEdge))
continue;
746 if (std::get<1>(edge) == goalNode) {
747 bestNodeList.push_back(goalNode);
748 bestEdgeList.push_back(edge);
749 showMeTheMoney = std::get<2>(edge);
754 float currentCost(0.);
758 if (currentCost < std::numeric_limits<float>::max()) {
759 showMeTheMoney = std::get<2>(edge) + currentCost;
765 if (showMeTheMoney < std::numeric_limits<float>::max()) {
777 const Eigen::Vector3f& node1Pos = node1->
getPosition();
778 const Eigen::Vector3f& node2Pos = node2->
getPosition();
779 float deltaNode[] = {
780 node1Pos[0] - node2Pos[0], node1Pos[1] - node2Pos[1], node1Pos[2] - node2Pos[2]};
783 return std::sqrt(deltaNode[0] * deltaNode[0] + deltaNode[1] * deltaNode[1] +
784 deltaNode[2] * deltaNode[2]);
808 float& bestTreeQuality)
const
811 float bestQuality(0.);
812 float curEdgeWeight = std::max(0.3, std::get<2>(curEdge));
813 float curEdgeProj(1. / curEdgeWeight);
815 reco::Hit3DToEdgeMap::const_iterator edgeListItr = hitToEdgeMap.find(std::get<1>(curEdge));
817 if (edgeListItr != hitToEdgeMap.end()) {
819 const Eigen::Vector3f& firstHitPos = std::get<0>(curEdge)->getPosition();
820 const Eigen::Vector3f& secondHitPos = std::get<1>(curEdge)->getPosition();
821 float curEdgeVec[] = {secondHitPos[0] - firstHitPos[0],
822 secondHitPos[1] - firstHitPos[1],
823 secondHitPos[2] - firstHitPos[2]};
824 float curEdgeMag = std::sqrt(curEdgeVec[0] * curEdgeVec[0] + curEdgeVec[1] * curEdgeVec[1] +
825 curEdgeVec[2] * curEdgeVec[2]);
827 curEdgeMag = std::max(
float(0.1), curEdgeMag);
829 for (
const auto& edge : edgeListItr->second) {
831 if (std::get<1>(edge) == std::get<0>(curEdge))
continue;
837 if (quality > bestQuality) {
838 hitPairListPtr = tempList;
839 bestQuality = quality;
840 curEdgeProj = 1. / curEdgeMag;
845 hitPairListPtr.push_front(std::get<1>(curEdge));
847 bestTreeQuality += bestQuality + curEdgeProj;
849 return hitPairListPtr;
861 size_t nStartedWith(hitPairVector.size());
862 size_t nRejectedHits(0);
867 for (
const auto& hit3D : hitPairVector) {
869 size_t n2DHitsIn3DHit(0);
870 size_t nThisClusterOnly(0);
871 size_t nOtherCluster(0);
874 const std::set<const reco::ClusterHit3D*>* otherClusterHits = 0;
876 for (
const auto& hit2D : hit3D->getHits()) {
877 if (!hit2D)
continue;
881 if (hit2DToClusterMap[hit2D].
size() < 2)
882 nThisClusterOnly = hit2DToClusterMap[hit2D][&clusterParams].
size();
884 for (
const auto& clusterHitMap : hit2DToClusterMap[hit2D]) {
885 if (clusterHitMap.first == &clusterParams)
continue;
887 if (clusterHitMap.second.size() > nOtherCluster) {
888 nOtherCluster = clusterHitMap.second.size();
890 otherClusterHits = &clusterHitMap.second;
896 if (n2DHitsIn3DHit < 3 && nThisClusterOnly > 1 && nOtherCluster > 0) {
897 bool skip3DHit(
false);
899 for (
const auto& otherHit3D : *otherClusterHits) {
900 size_t nOther2DHits(0);
902 for (
const auto& otherHit2D : otherHit3D->getHits()) {
903 if (!otherHit2D)
continue;
908 if (nOther2DHits > 2) {
915 if (skip3DHit)
continue;
918 goodHits.emplace_back(hit3D);
921 std::cout <<
"###>> Input " << nStartedWith <<
" hits, rejected: " << nRejectedHits
924 hitPairVector.resize(goodHits.size());
925 std::copy(goodHits.begin(), goodHits.end(), hitPairVector.begin());
936 std::string minuses(level / 2,
'-');
937 std::string indent(level / 2,
' ');
951 for (
const auto& hit3D : hitPairListPtr) {
952 Eigen::Vector3f pcaToHitVec(hit3D->getPosition()[0] - pcaCenter(0),
953 hit3D->getPosition()[1] - pcaCenter(1),
954 hit3D->getPosition()[2] - pcaCenter(2));
957 pointList.emplace_back(pcaToHit(1), pcaToHit(2), hit3D);
961 pointList.sort([](
const auto&
left,
const auto&
right) {
963 std::numeric_limits<float>::epsilon()) ?
969 std::vector<ConvexHull> convexHullVec;
970 std::vector<reco::ProjectedPointList> rejectedListVec;
971 bool increaseDepth(pointList.size() > 3);
972 float lastArea(std::numeric_limits<float>::max());
974 while (increaseDepth) {
979 const ConvexHull& convexHull = convexHullVec.back();
983 increaseDepth =
false;
986 if (convexHullVec.size() < 2 || convexHull.
getConvexHullArea() < 0.8 * lastArea) {
987 for (
auto& point : convexHullPoints) {
988 pointList.remove(point);
989 rejectedList.emplace_back(point);
998 while (!convexHullVec.empty() && convexHullVec.back().getConvexHullArea() < 0.5) {
999 convexHullVec.pop_back();
1000 rejectedListVec.pop_back();
1004 if (!convexHullVec.empty()) {
1005 size_t nRejectedTotal(0);
1008 for (
const auto& rejectedList : rejectedListVec) {
1009 nRejectedTotal += rejectedList.size();
1011 for (
const auto& rejectedPoint : rejectedList) {
1012 if (convexHullVec.back().findNearestDistance(rejectedPoint) > 0.5)
1013 locHitPairListPtr.remove(std::get<2>(rejectedPoint));
1024 for (
auto& curPoint : convexHullVec.back().getConvexHull()) {
1025 if (curPoint == lastPoint)
continue;
1030 float distBetweenPoints = (curPoint3D->getPosition()[0] - lastPoint3D->
getPosition()[0]) *
1031 (curPoint3D->getPosition()[0] - lastPoint3D->
getPosition()[0]) +
1032 (curPoint3D->getPosition()[1] - lastPoint3D->
getPosition()[1]) *
1033 (curPoint3D->getPosition()[1] - lastPoint3D->
getPosition()[1]) +
1034 (curPoint3D->getPosition()[2] - lastPoint3D->
getPosition()[2]) *
1035 (curPoint3D->getPosition()[2] - lastPoint3D->
getPosition()[2]);
1037 distBetweenPoints = std::sqrt(distBetweenPoints);
1041 convexHullPointList.push_back(curPoint);
1042 edgeMap[lastPoint3D].push_back(edge);
1043 edgeMap[curPoint3D].push_back(edge);
1044 edgeList.emplace_back(edge);
1046 lastPoint = curPoint;
1053 for (
const auto& point : extremePoints)
1054 extremePointList.push_back(point);
1060 for (
const auto& kink : kinkPoints)
1061 kinkPointList.push_back(kink);
1072 float largestDistance(0.);
1077 reco::EdgeList::const_iterator firstEdgeItr = convexHull.begin();
1079 while (firstEdgeItr != convexHull.end()) {
1080 reco::EdgeList::const_iterator nextEdgeItr = firstEdgeItr;
1086 while (++nextEdgeItr != convexHull.end()) {}
1089 return largestDistance;
reco::HitPairListPtr & getBestHitPairListPtr()
void RunPrimsAlgorithm(const reco::HitPairListPtr &, kdTree::KdTreeNode &, reco::ClusterParametersList &) const
Driver for Prim's algorithm.
void PCAAnalysis_3D(const reco::HitPairListPtr &hitPairList, reco::PrincipalComponents &pca, bool skeletonOnly=false) const
std::unordered_map< const reco::ClusterHit3D *, BestNodeTuple > BestNodeMap
std::tuple< const reco::ClusterHit3D *, float, float > BestNodeTuple
size_t fMinTinyClusterSize
Minimum size for a "tiny" cluster.
void FindBestPathInCluster(reco::ClusterParameters &) const
Algorithm to find the best path through the given cluster.
const geo::GeometryCore * geometry
const Eigen::Vector3f getPosition() const
void initializeHistograms(art::TFileDirectory &) override
Interface for initializing histograms if they are desired Note that the idea is to put hisgtograms in...
std::list< ProjectedPoint > ProjectedPointList
Declaration of signal hit object.
reco::HitPairListPtr DepthFirstSearch(const reco::EdgeTuple &, const reco::Hit3DToEdgeMap &, float &) const
a depth first search to find longest branches
std::pair< MinMaxPoints, MinMaxPoints > MinMaxPointPair
~MSTPathFinder()
Destructor.
std::list< Point > PointList
The list of the projected points.
std::list< KdTreeNode > KdTreeNodeList
Implements a kdTree for use in clustering.
size_t FindNearestNeighbors(const reco::ClusterHit3D *, const KdTreeNode &, CandPairList &, float &) const
std::size_t size(FixedBins< T, C > const &) noexcept
reco::EdgeList & getBestEdgeList()
reco::Hit3DToEdgeMap & getHit3DToEdgeMap()
reco::HitPairListPtr & getHitPairListPtr()
float fConvexHullMinSep
Min hit separation to conisder in convex hull.
MSTPathFinder(const fhicl::ParameterSet &)
Constructor.
unsigned int getStatusBits() const
TimeValues
enumerate the possible values for time checking if monitoring timing
IClusterModAlg interface class definiton.
void PruneAmbiguousHits(reco::ClusterParameters &, reco::Hit2DToClusterMap &) const
Prune the obvious ambiguous hits.
Implements a ConvexHull for use in clustering.
std::list< EdgeTuple > EdgeList
const EigenValues & getEigenValues() const
reco::PrincipalComponents & getFullPCA()
reco::Hit3DToEdgeMap & getConvexHullEdgeMap()
void LeastCostPath(const reco::EdgeTuple &, const reco::ClusterHit3D *, reco::ClusterParameters &, float &) const
Find the lowest cost path between two nodes using MST edges.
void ModifyClusters(reco::ClusterParametersList &) const override
Scan an input collection of clusters and modify those according to the specific implementing algorith...
reco::ProjectedPointList & getConvexHullExtremePoints()
reco::ConvexHullKinkTupleList & getConvexHullKinkPoints()
Define a container for working with the convex hull.
std::unique_ptr< lar_cluster3d::IClusterParametersBuilder > fClusterBuilder
Common cluster builder tool.
const Eigen::Vector3f & getAvePosition() const
Path checking algorithm has seen this hit.
void ReconstructBestPath(const reco::ClusterHit3D *, BestNodeMap &, reco::HitPairListPtr &, reco::EdgeList &) const
std::list< ConvexHullKinkTuple > ConvexHullKinkTupleList
std::tuple< const reco::ClusterHit3D *, const reco::ClusterHit3D *, double > EdgeTuple
std::list< const reco::ClusterHit3D * > HitPairListPtr
float getConvexHullArea() const
recover the area of the convex hull
The geometry of one entire detector, as served by art.
const PointList & getConvexHull() const
recover the list of convex hull vertices
PrincipalComponentsAlg fPCAAlg
float DistanceBetweenNodes(const reco::ClusterHit3D *, const reco::ClusterHit3D *) const
float fConvexHullKinkAngle
Angle to declare a kink in convex hull calc.
std::list< CandPair > CandPairList
ConvexHull class definiton.
This header file defines the interface to a principal components analysis designed to be used within ...
Encapsulate the geometry of a wire.
geo::Geometry const * fGeometry
float findConvexHullEndPoints(const reco::EdgeList &, const reco::ClusterHit3D *, const reco::ClusterHit3D *) const
reco::ConvexHull & getConvexHull()
bool fEnableMonitoring
Data members to follow.
Encapsulate the construction of a single detector plane.
This provides an art tool interface definition for 3D Cluster algorithms.
std::vector< float > fTimeVector
void AStar(const reco::ClusterHit3D *, const reco::ClusterHit3D *, float alpha, kdTree::KdTreeNode &, reco::ClusterParameters &) const
Algorithm to find shortest path between two 3D hits.
std::pair< reco::ProjectedPoint, reco::ProjectedPoint > MinMaxPoints
Add ability to build the convex hull (these needs to be split out! )
std::unordered_map< const reco::ClusterHit2D *, ClusterToHitPairSetMap > Hit2DToClusterMap
float getTimeToExecute() const override
If monitoring, recover the time to execute a particular function.
float getHitChiSquare() const
std::unordered_map< const reco::ClusterHit3D *, reco::EdgeList > Hit3DToEdgeMap
void configure(fhicl::ParameterSet const &pset) override
Interface for configuring the particular algorithm tool.
float getTimeToExecute() const
reco::ProjectedPointList & getConvexHullPointList()
void buildConvexHull(reco::ClusterParameters &, reco::HitPairListPtr &, int level=0) const
reco::ProjectedPointList & getProjectedPointList()
KdTreeNode & BuildKdTree(Hit3DVec::iterator, Hit3DVec::iterator, KdTreeNodeList &, int depth=0) const
Given an input set of ClusterHit3D objects, build a kd tree structure.
reco::EdgeList & getConvexHullEdgeList()
bool empty(FixedBins< T, C > const &) noexcept
std::tuple< float, float, const reco::ClusterHit3D * > ProjectedPoint
Projected coordinates and pointer to hit.
art framework interface to geometry description
BEGIN_PROLOG could also be cout
std::list< ClusterParameters > ClusterParametersList
const EigenVectors & getEigenVectors() const
void setStatusBit(unsigned bits) const