3D cluster association algorithm - #280
Conversation
…rather than average PCA direction
|
Not checked over yet in depth...but this looks very nicely self contained. Could it be split into a LArContent |
| }; | ||
|
|
||
| typedef std::unordered_map<const pandora::Cluster*, ClusterAttr> ClusterAttrMap; | ||
| mutable ClusterAttrMap clusterAttrMap; |
There was a problem hiding this comment.
Add m_ prefix for class member variables
| for (ClusterList::const_iterator iter = pClusterList->begin(), iterEnd = pClusterList->end(); iter != iterEnd; ++iter) | ||
| { | ||
| const Cluster *const pCluster = *iter; | ||
| float clusterLength = LArClusterHelper::GetLength(pCluster); |
| CartesianVector currentClusterInnerCoordinate{ clusterAttrMap.at(pCurrentCluster).innerCoordinate }; | ||
| CartesianVector currentClusterOuterCoordinate{ clusterAttrMap.at(pCurrentCluster).outerCoordinate }; | ||
|
|
||
| CartesianVector testClusterInnerCoordinate{ clusterAttrMap.at(pTestCluster).innerCoordinate }; | ||
| CartesianVector testClusterOuterCoordinate{ clusterAttrMap.at(pTestCluster).outerCoordinate }; |
There was a problem hiding this comment.
And make them reference variables
lhwhitehead
left a comment
There was a problem hiding this comment.
Only minor things from my side. Once you are done with everything, run clang format to polish up things into the style that we use.
AndyChappell
left a comment
There was a problem hiding this comment.
Hi Manoa, Thanks for the PR. I think this is generally looking pretty good. I have a few, mostly minor requests and a couple of queries.
On some more general points, as Leigh noted, we'll ultimately pass this through clang format to get the Pandora style. You can skip that step if you want, as for LArContent PRs, I will apply the clang format is a normal matter of constructing release branches. However if you do choose to apply it yourself, can you ensure that it's isolated to a single commit and make it the last one in response to reviews (this makes it easier to distinguish substantive changes from formatting changes when reviewing updates. Thanks!
A secondary comment - and not anything that I think needs addressing for this PR, but I wonder if some of the sliding fit algorithms might provide some more flexibility versus PCA fits for 3D association?
|
|
||
| //------------------------------------------------------------------------------------------------------------------------------------------ | ||
|
|
||
|
|
| CartesianVector lhsOuterCoordinate(0.f, 0.f, 0.f); | ||
| CartesianVector rhsOuterCoordinate(0.f, 0.f, 0.f); |
There was a problem hiding this comment.
To avoid the extra instantiation, given you don't actually care about these values you could just define
CartesianVector unused(0.f, 0.f, 0.f);
and pass that as the second parameter to both of the extremal coordinate functions below.
| float length; | ||
| pandora::CartesianVector innerCoordinate = pandora::CartesianVector(0.f, 0.f, 0.f); | ||
| pandora::CartesianVector outerCoordinate = pandora::CartesianVector(0.f, 0.f, 0.f); | ||
| pandora::CartesianVector centroid = pandora::CartesianVector(0.f, 0.f, 0.f); | ||
| LArPcaHelper::EigenValues eigenValues = LArPcaHelper::EigenValues(0.f, 0.f, 0.f); | ||
| LArPcaHelper::EigenVectors eigenVectors; |
There was a problem hiding this comment.
Coding style in Pandora is to add a prefix m_ to member variables.
| const Cluster *const pCluster = *iter; | ||
| float clusterLength = LArClusterHelper::GetLength(pCluster); |
There was a problem hiding this comment.
In general (here and elsewhere), when initialising variables, prefer
type varname{value}; over type varname = value;
unless there's a specific reason not to. This has some benefits in terms of avoiding unintended type narrowing, etc. The main exception here would be if you are explicitly calling an object constructor that might otherwise be confused for an initialisation list in the case of {}. The classic here would be a std::vector
std::vector<int> v{10, 20}; Gets you a vector containing the elements 10 and 20
std::vector<int> v(10, 20); Gets you a vector containing 10 elements, all of which are 20
| if( clusterLength < m_minClusterLength ) | ||
| continue; | ||
|
|
||
| this->PCAFit(pCluster); |
There was a problem hiding this comment.
I wonder if this would be better named PopulatePCAAttributes, or PopulateFitAttributes?
There was a problem hiding this comment.
Renamed PCAFit to PopulateFitAttributes
| constexpr float shortClusterLength = 2.50f; | ||
|
|
||
| // Bypass opening angle cut if a short cluster is the extension of a longer one | ||
| if( (closestDistance > 0.5f) || |
There was a problem hiding this comment.
Should 0.5f be a configurable parameter?
There was a problem hiding this comment.
In ND-LAr, 0.5f corresponds to a 4 mm pixel pitch with a 1 mm tolerance. Updated to
const double channelPitch{ 1.3 * LArGeometryHelper::GetWirePitch(this->GetPandora(), m_view) };to make it detector agnostic.
|
|
||
| constexpr float shortClusterLength = 2.50f; | ||
|
|
||
| // Bypass opening angle cut if a short cluster is the extension of a longer one |
There was a problem hiding this comment.
Would this not also bypass if the long cluster is an extension of the short one? Is that what you want?
There was a problem hiding this comment.
Fair. Updated the comment for clarity
// Bypass opening angle check if a short cluster extends a long one (or vice versa)| const CartesianVector innerEndPrimaryAxis { centroid + primaryAxis * primaryAxis.GetDotProduct(innerClusterOuterCoordinate - centroid) }; | ||
| const CartesianVector outerStartPrimaryAxis{ centroid + primaryAxis * primaryAxis.GetDotProduct(outerClusterInnerCoordinate - centroid) }; |
There was a problem hiding this comment.
I can't quite get my head around what this is trying to do
There was a problem hiding this comment.
These lines determine the coordinates of the inner cluster's end and the outer cluster's start along the longest cluster's primary axis, relative to the longest cluster's centroid.
| bool isInnerLongest{ LArClusterHelper::SortByNHits(pInnerCluster, pOuterCluster) && | ||
| (clusterAttrMap.at(pInnerCluster).length > clusterAttrMap.at(pOuterCluster).length) }; | ||
|
|
||
| const CartesianVector centroid{ isInnerLongest ? innerCentroid : outerCentroid }; |
There was a problem hiding this comment.
I think all of these CartesianVectors can be reference variables.
| d("LArThreeDLongitudinalTracks", ThreeViewLongitudinalTracksAlgorithm) \ | ||
| d("LArThreeDAssociation", ThreeDAssociationAlgorithm) \ |
There was a problem hiding this comment.
The header files will be reordered by clang format, so could you swap these two here? (clang format is not allowed to alter these preprocessor blocks).
Just to comment on this latter point - a clusterhelper style refactoring may have some value here - I'll let @CrossR comment on that, but to the more general point of LArRecoND versus LArContent, modulo a couple of points where some hard-coded values could do with becoming configurable, I think this is a case where the algorithm is sufficiently generic that it belongs in LArContent. It seems to be a general purpose association algorithm based on 3D hits, which, those hard-coded values aside, doesn't appear to have anything that is specific to the ND, and could equally apply to the built 3D clusters in wire/strip-based TPCS. Happy to hear thoughts on this. |
AndyChappell
left a comment
There was a problem hiding this comment.
A couple of minor additional requests, and then I think this should be good to go (though looks like there will be merge conflicts to resolve).
| if( m_clusterAttrMap.find(pCurrentCluster) == m_clusterAttrMap.end() ) | ||
| LArClusterHelper::GetExtremalCoordinates(pCurrentCluster, currentClusterInnerCoordinate, currentClusterOuterCoordinate); |
There was a problem hiding this comment.
If any part of the conditional is multiline, enclose each branch in braces, even if that branch is only a single line
|
|
||
| if( m_clusterAttrMap.find(pCurrentCluster) == m_clusterAttrMap.end() ) | ||
| LArClusterHelper::GetExtremalCoordinates(pCurrentCluster, currentClusterInnerCoordinate, currentClusterOuterCoordinate); | ||
| else{ |
There was a problem hiding this comment.
No need to address this here, as the pre-release clang format run will resolve it, but for future reference, Pandora style is to have the opening brace on its own line.
| if ( m_clusterAttrMap.find(pTestCluster) == m_clusterAttrMap.end() ) | ||
| LArClusterHelper::GetExtremalCoordinates(pTestCluster, testClusterInnerCoordinate, testClusterOuterCoordinate); |
There was a problem hiding this comment.
As above re brace enclosure
| const CartesianVector outerCentroid{ clusterAttrMap.at(pOuterCluster).centroid }; | ||
|
|
||
| const float openingAngle{ innerPrimaryAxis.GetCosOpeningAngle(outerPrimaryAxis) }; | ||
| const double channelPitch{ 1.3 * LArGeometryHelper::GetWirePitch(this->GetPandora(), m_view) }; |
There was a problem hiding this comment.
Should 1.3 be a configurable parameter here?
|
I've got two more general / high-level comments (which may be explained if I knew the XML or some of the more underlying code):
|
|
Hi @CrossR. I'm not really sure where/how to reply to your comment, so I'm writing here 😅.
Good catch! It's only used in one place, so it can be a local variable instead. It's only used to retrieve the wire pitch not to do any reco logic. |
… consolidate cluster attribute population into PopulateFitAttributes
|
Edit to my previous comment. Ugh, I forgot that |
An algorithm to associate clusters in 3D. It is a simple approach inspired by the (2D) longitudinal association algorithm, but it removes the reliance and constraints on pseudo-layer to better support 3D reconstruction