11 #include "art/Framework/Services/Registry/ServiceHandle.h"
12 #include "fhiclcpp/ParameterSet.h"
13 #include "messagefacility/MessageLogger/MessageLogger.h"
30 #include "Eigen/Dense"
31 #include "Eigen/Eigenvalues"
32 #include "Eigen/Geometry"
33 #include "Eigen/Jacobi"
38 namespace lar_cluster3d {
42 m_parallel = pset.get<
float>(
"ParallelLines", 0.00001);
43 m_geometry = art::ServiceHandle<geo::Geometry const>{}.get();
74 float doca3DScl)
const
106 int maxIterations(3);
110 maxRange = sclFctr * 0.5 * (maxRange + aveDoca);
113 int totalRejects(numRejHits);
114 int maxRejects(0.4 * hitPairVector.size());
117 while (maxIterations-- && numRejHits > 0 && totalRejects < maxRejects) {
143 bool skeletonOnly)
const
154 Eigen::Vector3d meanPos(Eigen::Vector3d::Zero());
155 double meanWeightSum(0.);
159 double minimumDeltaPeakSig(0.00001);
165 std::vector<double> hitChiSquareVec;
167 hitChiSquareVec.resize(hitPairVector.size());
171 hitChiSquareVec.begin(),
172 [](
const auto&
hit) {
return hit->getHitChiSquare(); });
173 std::sort(hitChiSquareVec.begin(), hitChiSquareVec.end());
175 size_t numToKeep = 0.8 * hitChiSquareVec.size();
177 hitChiSquareVec.resize(numToKeep);
179 double aveValue = std::accumulate(hitChiSquareVec.begin(), hitChiSquareVec.end(), double(0.)) /
180 double(hitChiSquareVec.size());
181 double rms = std::sqrt(std::inner_product(hitChiSquareVec.begin(),
182 hitChiSquareVec.end(),
183 hitChiSquareVec.begin(),
186 [aveValue](
const auto&
left,
const auto&
right) {
187 return (
left - aveValue) * (
right - aveValue);
189 double(hitChiSquareVec.size()));
191 minimumDeltaPeakSig = std::max(minimumDeltaPeakSig, aveValue - rms);
195 for (
const auto&
hit : hitPairVector) {
201 double weight = std::max(minimumDeltaPeakSig,
202 double(
hit->getHitChiSquare()));
205 meanPos(0) +=
hit->getPosition()[0] * weight;
206 meanPos(1) +=
hit->getPosition()[1] * weight;
207 meanPos(2) +=
hit->getPosition()[2] * weight;
210 meanWeightSum += weight;
213 meanPos /= meanWeightSum;
222 double weightSum(0.);
225 for (
const auto&
hit : hitPairVector) {
230 double weight = 1. / std::max(minimumDeltaPeakSig,
231 double(
hit->getHitChiSquare()));
233 double x = (
hit->getPosition()[0] - meanPos(0)) * weight;
234 double y = (
hit->getPosition()[1] - meanPos(1)) * weight;
235 double z = (
hit->getPosition()[2] - meanPos(2)) * weight;
237 weightSum += weight * weight;
250 sig << xi2, xiyi, xizi, xiyi, yi2, yizi, xizi, yizi, zi2;
252 sig *= 1. / weightSum;
254 Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eigenMat(sig);
256 if (eigenMat.info() == Eigen::ComputationInfo::Success) {
262 eigenMat.eigenvectors().transpose().cast<
float>();
265 if (std::isnan(recobEigenVals[0])) {
266 std::cout <<
"==> Third eigenvalue returns a nan" << std::endl;
268 recobEigenVals[0] = 0.;
271 recobEigenVecs.row(0) = recobEigenVecs.row(1).cross(recobEigenVecs.row(2));
276 true, numPairsInt, recobEigenVals, recobEigenVecs, meanPos.cast<
float>());
279 mf::LogDebug(
"Cluster3D") <<
"PCA decompose failure, numPairs = " << numPairsInt << std::endl;
290 bool updateAvePos)
const
304 float aveHitDoca(0.);
305 Eigen::Vector3f avePosUpdate(0., 0., 0.);
316 std::vector<Eigen::Vector3f> hitPosVec;
319 for (
const auto& hit3D : hitPairVector) {
321 for (
const auto&
hit : hit3D->getHits()) {
329 double wirePosArr[3] = {0., 0., 0.};
332 Eigen::Vector3f wireCenter(wirePosArr[0], wirePosArr[1], wirePosArr[2]);
333 Eigen::Vector3f wireDirVec(
337 float hitPeak(
hit->getHit()->PeakTime());
339 Eigen::Vector3f wirePos(
345 Eigen::Vector3f xAxis(1., 0., 0.);
346 Eigen::Vector3f planeNormal =
347 xAxis.cross(wireDirVec);
349 float docaInPlane(wirePos[0] - avePosition[0]);
350 float arcLenToPlane(0.);
351 float cosAxisToPlaneNormal = axisDirVec.dot(planeNormal);
353 Eigen::Vector3f axisPlaneIntersection = wirePos;
354 Eigen::Vector3f hitPosTVec = wirePos;
356 if (fabs(cosAxisToPlaneNormal) > 0.) {
357 Eigen::Vector3f deltaPos = wirePos - avePosition;
359 arcLenToPlane = deltaPos.dot(planeNormal) / cosAxisToPlaneNormal;
360 axisPlaneIntersection = avePosition + arcLenToPlane * axisDirVec;
361 docaInPlane = wirePos[0] - axisPlaneIntersection[0];
363 Eigen::Vector3f axisToInter = axisPlaneIntersection - wirePos;
364 float arcLenToDoca = axisToInter.dot(wireDirVec);
366 hitPosTVec += arcLenToDoca * wireDirVec;
370 Eigen::Vector3f wVec = avePosition - wirePos;
373 float a(axisDirVec.dot(axisDirVec));
374 float b(axisDirVec.dot(wireDirVec));
375 float c(wireDirVec.dot(wireDirVec));
376 float d(axisDirVec.dot(wVec));
377 float e(wireDirVec.dot(wVec));
379 float den(
a * c - b * b);
385 arcLen1 = (b *
e - c * d) / den;
386 arcLen2 = (
a *
e - b * d) / den;
389 mf::LogDebug(
"Cluster3D") <<
"** Parallel lines, need a solution here" << std::endl;
400 Eigen::Vector3f hitPos = wirePos + arcLen2 * wireDirVec;
401 Eigen::Vector3f axisPos = avePosition + arcLen1 * axisDirVec;
402 float deltaX = hitPos(0) - axisPos(0);
403 float deltaY = hitPos(1) - axisPos(1);
404 float deltaZ = hitPos(2) - axisPos(2);
405 float doca2 = deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;
406 float doca = sqrt(doca2);
410 aveHitDoca += fabs(docaInPlane);
425 hit->setDocaToAxis(fabs(docaInPlane));
426 hit->setArcLenToPoca(arcLenToPlane);
430 if (
hit->getStatusBits() & 0x80) {
continue; }
434 avePosUpdate += hitPosTVec;
436 hitPosVec.push_back(hitPosTVec);
443 avePosUpdate /= float(nHits);
446 aveHitDoca /= float(nHits);
448 if (updateAvePos) { avePosition = avePosUpdate; }
451 for (
auto& hitPos : hitPosVec) {
453 float x = hitPos[0] - avePosition[0];
454 float y = hitPos[1] - avePosition[1];
455 float z = hitPos[2] - avePosition[2];
470 sig << xi2, xiyi, xizi, xiyi, yi2, yizi, xizi, yizi, zi2;
472 sig *= 1. / (nHits - 1);
474 Eigen::SelfAdjointEigenSolver<Eigen::Matrix3f> eigenMat(sig);
476 if (eigenMat.info() == Eigen::ComputationInfo::Success) {
482 eigenMat.eigenvectors().transpose().cast<
float>();
486 true, nHits, recobEigenVals, recobEigenVecs, avePosition, aveHitDoca);
489 mf::LogDebug(
"Cluster3D") <<
"PCA decompose failure, numPairs = " << nHits << std::endl;
505 Eigen::Vector3f avePosition(
513 for (
const auto* clusterHit3D : hitPairVector) {
515 clusterHit3D->clearStatusBits(0x80);
519 Eigen::Vector3f clusPos(clusterHit3D->getPosition()[0],
520 clusterHit3D->getPosition()[1],
521 clusterHit3D->getPosition()[2]);
524 Eigen::Vector3f clusToHitVec = clusPos - avePosition;
527 float arclenToPoca = clusToHitVec.dot(axisDirVec);
530 Eigen::Vector3f docaPos = avePosition + arclenToPoca * axisDirVec;
533 Eigen::Vector3f docaPosToClusPos = clusPos - docaPos;
534 float docaToAxis = docaPosToClusPos.norm();
536 aveDoca3D += docaToAxis;
539 clusterHit3D->setDocaToAxis(docaToAxis);
540 clusterHit3D->setArclenToPoca(arclenToPoca);
544 aveDoca3D /= float(hitPairVector.size());
560 Eigen::Vector3f avePosition(
568 float aveHitDoca(0.);
571 for (
const auto*
hit : hit2DListPtr) {
580 double wirePosArr[3] = {0., 0., 0.};
583 Eigen::Vector3f wireCenter(wirePosArr[0], wirePosArr[1], wirePosArr[2]);
584 Eigen::Vector3f wireDirVec(
588 Eigen::Vector3f wirePos(
hit->getXPosition(), wireCenter[1], wireCenter[2]);
591 Eigen::Vector3f xAxis(1., 0., 0.);
592 Eigen::Vector3f planeNormal =
593 xAxis.cross(wireDirVec);
595 float arcLenToPlane(0.);
596 float docaInPlane(wirePos[0] - avePosition[0]);
597 float cosAxisToPlaneNormal = axisDirVec.dot(planeNormal);
599 Eigen::Vector3f axisPlaneIntersection = wirePos;
602 if (fabs(cosAxisToPlaneNormal) > 0.) {
603 Eigen::Vector3f deltaPos = wirePos - avePosition;
606 std::min(
float(deltaPos.dot(planeNormal) / cosAxisToPlaneNormal), maxArcLen);
607 axisPlaneIntersection = avePosition + arcLenToPlane * axisDirVec;
609 Eigen::Vector3f axisToInter = axisPlaneIntersection - wirePos;
610 float arcLenToDoca = axisToInter.dot(wireDirVec);
613 if (fabs(arcLenToDoca) > wire_geom.
HalfL()) arcLenToDoca = wire_geom.
HalfL();
618 Eigen::Vector3f docaVec = axisPlaneIntersection - (wirePos + arcLenToDoca * wireDirVec);
619 docaInPlane = docaVec.norm();
622 aveHitDoca += fabs(docaInPlane);
625 hit->setDocaToAxis(fabs(docaInPlane));
626 hit->setArcLenToPoca(arcLenToPlane);
630 aveHitDoca /= float(hit2DListPtr.size());
640 float maxDocaAllowed)
const
650 for (
const auto& hit3D : hitPairVector) {
652 for (
const auto&
hit : hit3D->getHits()) {
654 hit->clearStatusBits(0x80);
656 if (
hit->getDocaToAxis() > maxDocaAllowed) {
657 hit->setStatusBit(0x80);
669 float maxDocaAllowed)
const
679 Eigen::Vector3f avePosition(
684 for (
const auto* clusterHit3D : hitPairVector) {
686 clusterHit3D->clearStatusBits(0x80);
690 Eigen::Vector3f clusPos(clusterHit3D->getPosition()[0],
691 clusterHit3D->getPosition()[1],
692 clusterHit3D->getPosition()[2]);
695 Eigen::Vector3f clusToHitVec = clusPos - avePosition;
698 float arclenToPoca = clusToHitVec.dot(axisDirVec);
701 Eigen::Vector3f docaPos = avePosition + arclenToPoca * axisDirVec;
704 Eigen::Vector3f docaPosToClusPos = clusPos - docaPos;
705 float docaToAxis = docaPosToClusPos.norm();
708 clusterHit3D->setDocaToAxis(docaToAxis);
709 clusterHit3D->setArclenToPoca(arclenToPoca);
712 if (clusterHit3D->getDocaToAxis() > maxDocaAllowed) {
713 clusterHit3D->setStatusBit(0x80);
float m_parallel
means lines are parallel
Geometry description of a TPC wireThe wire is a single straight segment on a wire plane...
process_name opflash particleana ie ie ie z
void setAveHitDoca(double doca) const
std::list< const reco::ClusterHit2D * > Hit2DListPtr
export some data structure definitions
void PCAAnalysis_calc3DDocas(const reco::HitPairListPtr &hitPairVector, const reco::PrincipalComponents &pca) const
int PCAAnalysis_reject3DOutliers(const reco::HitPairListPtr &hitPairVector, const reco::PrincipalComponents &pca, float aveHitDoca) const
Utilities related to art service access.
void PCAAnalysis_3D(const reco::HitPairListPtr &hitPairList, reco::PrincipalComponents &pca, bool skeletonOnly=false) const
PrincipalComponentsAlg(fhicl::ParameterSet const &pset)
Constructor.
process_name opflash particleana ie x
int PCAAnalysis_reject2DOutliers(const reco::HitPairListPtr &hitPairVector, reco::PrincipalComponents &pca, float aveHitDoca) const
Declaration of signal hit object.
CryostatID_t Cryostat
Index of cryostat.
bool operator()(const reco::ClusterHit3D *left, const reco::ClusterHit3D *right) const
float getDocaToAxis() const
const EigenValues & getEigenValues() const
process_name opflash particleana ie ie y
void PCAAnalysis_2D(const detinfo::DetectorPropertiesData &detProp, const reco::HitPairListPtr &hitPairVector, reco::PrincipalComponents &pca, bool updateAvePos=false) const
Eigen::Vector3f EigenValues
const Eigen::Vector3f & getAvePosition() const
bool operator()(const reco::ClusterHit3D *left, const reco::ClusterHit3D *right) const
std::list< const reco::ClusterHit3D * > HitPairListPtr
PlaneID_t Plane
Index of the plane within its TPC.
Definition of data types for geometry description.
const geo::Geometry * m_geometry
This header file defines the interface to a principal components analysis designed to be used within ...
Encapsulate the geometry of a wire.
double ConvertTicksToX(double ticks, int p, int t, int c) const
double HalfL() const
Returns half the length of the wire [cm].
const float getAveHitDoca() const
bool operator()(const reco::ClusterHit3D *left, const reco::ClusterHit3D *right) const
Eigen::Matrix3f EigenVectors
float getArclenToPoca() const
void GetCenter(double *xyz, double localz=0.0) const
Fills the world coordinate of a point on the wire.
TPCID_t TPC
Index of the TPC within its cryostat.
Hit is a "skeleton" hit.
void PCAAnalysis_calc2DDocas(const reco::Hit2DListPtr &hit2DVector, const reco::PrincipalComponents &pca) const
art framework interface to geometry description
BEGIN_PROLOG could also be cout
const EigenVectors & getEigenVectors() const
WireGeo const & WireIDToWireGeo(geo::WireID const &wireid) const
void PCAAnalysis(const detinfo::DetectorPropertiesData &detProp, const reco::HitPairListPtr &hitPairVector, reco::PrincipalComponents &pca, float doca3DScl=3.) const
Run the Principal Components Analysis.