ATLAS Offline Software
Loading...
Searching...
No Matches
MuonR4::FastReco::GlobalPatternFinder Class Reference

Standalone module to handle global pattern recognition. More...

#include <GlobalPatternFinder.h>

Collaboration diagram for MuonR4::FastReco::GlobalPatternFinder:

Classes

struct  Config
 Configuration object for the patter finder. More...
struct  HitPayload
 Base class for hit struct containing hit information. More...
struct  SearchTreeData
 Structure to hold the search tree data. More...
struct  CandidateHit
 Small wrapper for candidate hits used to build patterns. More...
struct  PatternState
 Pattern state object storing pattern information during construction. More...
struct  PatternPrintView
 A view of the pattern state for printing purposes. More...

Public Types

using StIndex = Muon::MuonStationIndex::StIndex
 Type alias for the station index.
using LayerIndex = Muon::MuonStationIndex::LayerIndex
 Type alias for the station layer index.

Public Member Functions

 GlobalPatternFinder (Config &&config, std::unique_ptr< const Acts::Logger > logger=Acts::getDefaultLogger("GlobalPatternFinder", Acts::Logging::Level::INFO))
 Standard constructor.
std::vector< GlobalPattern > findPatterns (const ActsTrk::GeometryContext &gctx, std::span< const SpacePointContainer * > spacepoints) const
 Main methods steering the pattern finding.

Private Types

enum class  SeedCoords : std::uint8_t { eSector , eTheta }
 Abrivation of the seed coordinates. More...
enum class  LayerOrdering : std::int8_t { eSameLayer , eLowerLayer , eHigherLayer }
 Enum to express the logical measurement layer ordering given two hits. More...
using PatternStateVec = std::vector<PatternState>
 Type alias for a vector of pattern states.
using PatHitVisual = MuonValR4::IFastRecoVisualizationTool::PatternHitVisualInfo
 Type alias for the visual information of a pattern.
using SearchTree_t = Acts::KDTree<2, const HitPayload*, double, std::array, 50>
 Definition of the search tree class.

Private Member Functions

SearchTreeData constructTree (const ActsTrk::GeometryContext &gctx, std::span< const SpacePointContainer * > spacepoints) const
 Construct the search tree from the given spacepoint containers.
PatternStateVec findPatternsInEta (const Acts::GeometryContext &gctx, const SearchTree_t &orderedSpacepoints, std::vector< PatHitVisual > *visualInfo=nullptr) const
 Method steering the global pattern building in the bending plane.
void extendPatterns (const Acts::GeometryContext &gctx, PatternStateVec &startPatterns, PatternStateVec &endPatterns, const CandidateHit &testHit, const Amg::Vector3D &beamSpot, std::vector< PatHitVisual > *visualInfo=nullptr) const
 Main function controlling the development of patterns, including pattern branching when necessary.
bool passPatternCuts (const PatternState &pat) const
 Method to check if a pattern passes the quality cuts.
PatternStateVec resolveOverlaps (PatternStateVec &toResolve, std::vector< PatHitVisual > *visualInfo=nullptr) const
 Method to remove overlapping patterns.
void addPhiOnlyHits (const ActsTrk::GeometryContext &gctx, PatternStateVec &patterns) const
 Method to add phi-only measurements to existing PatternStates.
GlobalPattern convertToPattern (const PatternState &candidate) const
 Method to convert a PatternState into a GlobalPattern object.
std::vector< GlobalPattern > convertToPattern (const PatternStateVec &candidates) const
 Method to convert a vector of PatternStates into GlobalPattern objects.
void addVisualInfo (const PatternState &candidate, PatHitVisual::PatternStatus status, std::vector< PatHitVisual > *visualInfo) const
 Helper function to add visual information of a given pattern (which is usually going to be destroyed) to the final container.
const Acts::Logger & logger () const

Static Private Member Functions

static bool isBetter (const PatternState &a, const PatternState &b)
 Method to compare two patterns and define which one is better.
static LayerOrdering checkLayerOrdering (const HitPayload &hit1, const HitPayload &hit2)
 Method to check the logical layer ordering of two hits.
static PatternPrintView brief (const PatternState &p)
 Print the pattern state with brief information.
static PatternPrintView detailed (const PatternState &p)
 Print the pattern state with detailed information.

Private Attributes

SpacePointPerLayerSorter m_spSorter {}
 Spacepoint sorter per logical measurement layer.
Config m_cfg
 Global Pattern Recognition configuration.
std::unique_ptr< const Acts::Logger > m_logger {}
 Logger for the Global Pattern Finder.

Static Private Attributes

static const int s_nStations {Acts::toUnderlying(StIndex::StIndexMax)}
 Number of stations.

Detailed Description

Standalone module to handle global pattern recognition.

This tool performs global pattern recognition as the first step of the Phase-2 fast reconstruction stage. It builds global patterns of precision and non-precision hits using space-points created in upstream algorithms. It first builds patterns in eta and then adds compatible phi-only hits to the patterns. The resulting patterns are returned by the main method of the tool.

Note
The definition and implementation of structs and classes used in the pattern finding are contained in the GlobalPatternFinderDefs.h

Definition at line 28 of file GlobalPatternFinder.h.

Member Typedef Documentation

◆ LayerIndex

Type alias for the station layer index.

Definition at line 33 of file GlobalPatternFinder.h.

◆ PatHitVisual

Type alias for the visual information of a pattern.

Definition at line 111 of file GlobalPatternFinder.h.

◆ PatternStateVec

Type alias for a vector of pattern states.

Definition at line 109 of file GlobalPatternFinder.h.

◆ SearchTree_t

using MuonR4::FastReco::GlobalPatternFinder::SearchTree_t = Acts::KDTree<2, const HitPayload*, double, std::array, 50>
private

Definition of the search tree class.

Definition at line 113 of file GlobalPatternFinder.h.

◆ StIndex

Type alias for the station index.

Definition at line 31 of file GlobalPatternFinder.h.

Member Enumeration Documentation

◆ LayerOrdering

enum class MuonR4::FastReco::GlobalPatternFinder::LayerOrdering : std::int8_t
strongprivate

Enum to express the logical measurement layer ordering given two hits.

Enumerator
eSameLayer 
eLowerLayer 
eHigherLayer 

Definition at line 187 of file GlobalPatternFinder.h.

187 : std::int8_t{
188 eSameLayer,
189 eLowerLayer,
190 eHigherLayer
191 };

◆ SeedCoords

enum class MuonR4::FastReco::GlobalPatternFinder::SeedCoords : std::uint8_t
strongprivate

Abrivation of the seed coordinates.

Enumerator
eSector 

Expanded sector coordinate of the associated spectrometer sector

eTheta 

Global Theta.

Definition at line 115 of file GlobalPatternFinder.h.

115 : std::uint8_t{
117 eSector,
119 eTheta
120 };

Constructor & Destructor Documentation

◆ GlobalPatternFinder()

MuonR4::FastReco::GlobalPatternFinder::GlobalPatternFinder ( Config && config,
std::unique_ptr< const Acts::Logger > logger = Acts::getDefaultLogger(                                    "GlobalPatternFinder", Acts::Logging::Level::INFO) )

Standard constructor.

Parameters
nameName to be printed in the messaging
configConfiguration parameters

Definition at line 22 of file GlobalPatternFinder.cxx.

23 :
25 m_logger{std::move(logger)} {
26 static_assert(std::is_move_assignable_v<PatternState>);
27 static_assert(std::is_move_constructible_v<PatternState>);
28 static_assert(std::is_copy_assignable_v<PatternState>);
29 static_assert(std::is_copy_constructible_v<PatternState>);
30 static_assert(std::is_nothrow_move_constructible_v<PatternState>);
31 static_assert(std::is_nothrow_move_assignable_v<PatternState>);
32};
Config m_cfg
Global Pattern Recognition configuration.
std::unique_ptr< const Acts::Logger > m_logger
Logger for the Global Pattern Finder.

Member Function Documentation

◆ addPhiOnlyHits()

void MuonR4::FastReco::GlobalPatternFinder::addPhiOnlyHits ( const ActsTrk::GeometryContext & gctx,
PatternStateVec & patterns ) const
private

Method to add phi-only measurements to existing PatternStates.

Parameters
gctxGeometry context
patternsVector of pattern states to which to add phi-only hits
Returns
: Vector of added phi-only hits

We use useBeamspot as a flag to indicate whether the pattern line has been determined successfully

We look for phi-only hits in the buckets associated with the pattern

Reject hits from a layer that already contains a phi hit

Definition at line 477 of file GlobalPatternFinder.cxx.

478 {
479 auto computePatternLineInStation = [&](PatternState& pat,
480 const StIndex station) -> bool {
481 const std::vector<CandidateHit>& stationHits {
482 pat.hitsPerStation[Acts::toUnderlying(station)]};
483 if(stationHits.empty()) {
484 return false;
485 }
486
488 pat.useBeamspot = true;
489
490 if (stationHits.size() > 1) {
491 // if we have >= 2 eta hits in the station, we use the furthestmost to define the pattern line
492 const auto [minIt, maxIt] {std::ranges::minmax_element(stationHits, {},
493 [](const CandidateHit& c){ return c.globLayer; })};
494 pat.lineAnchorHit = *minIt;
495 pat.lastInsertedHit = *maxIt;
496 pat.updateLineParameters(gctx.context(), Amg::Vector3D::Zero());
497 }
498 if (pat.useBeamspot) {
499 // if we have only one eta hit or the layer separation is too small, to find the second hit
500 // we use the functionality of anchor hit
501 pat.moveLineAnchorHit(stationHits.front());
502 pat.lastInsertedHit = *std::ranges::max_element(stationHits, {},
503 [&](const CandidateHit& c){
504 return (pat.projToPhiPlane(gctx.context(), *c) -
505 pat.projToPhiPlane(gctx.context(), *pat.lineAnchorHit)).mag(); });
506 pat.updateLineParameters(gctx.context(), Amg::Vector3D::Zero());
507 }
508 return !pat.useBeamspot;
509 };
510
511 PatternStateVec survivingPatterns{};
512 survivingPatterns.reserve(patterns.size());
513 for (PatternState& pat : patterns) {
515 ACTS_VERBOSE(__func__<<"() Search for phi-only hits for pattern: " << brief(pat));
516
517 std::optional<StIndex> patterLineStation{std::nullopt};
518
519 auto projOntoPhiPlane = [&pat](const Amg::Vector3D& pos) -> Amg::Vector3D {
520 return pos - pos.dot(pat.bendPlaneNorm) * pat.bendPlaneNorm;
521 };
522
523 for (const SpacePointBucket* bucket : pat.getParentBuckets()) {
524
525 const Acts::Transform3& localToGlobal {bucket->msSector()->localToGlobalTransform(gctx)};
526 const StIndex station {m_cfg.idHelperSvc->stationIndex(bucket->front()->identify())};
527
528 for (const auto& hit : *bucket) {
529 // We are looking for phi-only hits
530 if (hit->measuresEta()){
531 continue;
532 }
533 ACTS_VERBOSE(__func__<<"() *** Test phi-only hit "<<*hit);
534 HitPayload newHit {gctx.context(), hit.get(), bucket, localToGlobal};
535
537 const std::vector<CandidateHit>& stationHits {
538 pat.hitsPerStation[Acts::toUnderlying(station)]};
539 assert(!stationHits.empty());
540 if (std::ranges::any_of(stationHits, [&](const CandidateHit& h){
541 return h.sp()->measuresPhi() &&
542 hit->msSector() == h.sp()->msSector() &&
543 newHit.locLayer == h->locLayer;
544 }) ||
545 std::ranges::any_of(pat.phiOnlyHits, [&](const HitPayload& h){
546 return station == h.station &&
547 hit->msSector() == h->msSector() &&
548 newHit.locLayer == h.locLayer;
549 })) {
550 ACTS_VERBOSE(__func__<<"() The pattern already has a phi hit in the same layer - skip hit.");
551 continue;
552 }
553
554 if (!pat.isPhiCompatible(newHit)) {
555 ACTS_VERBOSE(__func__<<"() Phi-only hit not compatible");
556 continue;
557 }
558
559 if (!patterLineStation || *patterLineStation != station) {
560 if (!computePatternLineInStation(pat, station)) {
561 ACTS_VERBOSE(__func__<<"() Invalid projection model for station "<<station<<" - skip hit.");
562 continue;
563 }
564 patterLineStation = station;
565 }
566
567 const double stripHalfLength {
568 std::sqrt(hit->covariance()[Acts::toUnderlying(SpacePoint::CovIdx::etaCov)])};
569 const Amg::Vector3D sensorDir {newHit.sensorDir(gctx.context())};
570 const Amg::Vector3D stripLow {
571 projOntoPhiPlane(newHit.position - stripHalfLength * sensorDir)};
572 const Amg::Vector3D stripHigh {
573 projOntoPhiPlane(newHit.position + stripHalfLength * sensorDir)};
574 const Amg::Vector3D stripDirOnPlane {(stripHigh - stripLow).unit()};
575
576 const double stripIntersect {Acts::detail::LineHelper::lineIntersect<3>(
577 pat.linePos, pat.lineDir, stripLow, stripDirOnPlane).pathLength()};
578 const double stripProjLength {(stripHigh - stripLow).mag()};
579
580 ACTS_VERBOSE(__func__<<"() Intersect distance from lower strip edge: "
581 <<stripIntersect<<", proj strip length: "<< (stripHigh - stripLow).mag());
582
583 constexpr double margin {10 * Gaudi::Units::mm};
584 if (stripIntersect < -margin || stripIntersect > (stripProjLength + margin)) {
585 ACTS_VERBOSE(__func__<<"() The pattern falls outside the test hit strip in eta - skip hit.");
586 continue;
587 }
588 pat.phiOnlyHits.push_back(std::move(newHit));
589 pat.nPhiLayers++;
590 pat.updatePatternPhi();
591 }
592 }
593 if (pat.nPhiLayers < m_cfg.minPhiLayers) {
594 ACTS_VERBOSE(__func__<<"() Pattern "<<detailed(pat)
595 <<" has only "<<static_cast<int>(pat.nPhiLayers)
596 <<" phi layers, below the minimum required - reject this pattern.");
597 continue;
598 }
599 survivingPatterns.push_back(std::move(pat));
600 }
601 std::swap(patterns, survivingPatterns);
602}
Scalar mag() const
mag method
detray::unit< scalar_t > unit
bool hit(const Container &ids, int pdgId)
Acts::GeometryContext context() const
static PatternPrintView brief(const PatternState &p)
Print the pattern state with brief information.
Muon::MuonStationIndex::StIndex StIndex
Type alias for the station index.
std::vector< PatternState > PatternStateVec
Type alias for a vector of pattern states.
static PatternPrintView detailed(const PatternState &p)
Print the pattern state with detailed information.
std::vector< std::string > patterns
Definition listroot.cxx:187
Eigen::Matrix< double, 3, 1 > Vector3D
void swap(ElementLinkVector< DOBJ > &lhs, ElementLinkVector< DOBJ > &rhs)
Small wrapper for candidate hits used to build patterns.
Base class for hit struct containing hit information.
Pattern state object storing pattern information during construction.

◆ addVisualInfo()

void MuonR4::FastReco::GlobalPatternFinder::addVisualInfo ( const PatternState & candidate,
PatHitVisual::PatternStatus status,
std::vector< PatHitVisual > * visualInfo ) const
private

Helper function to add visual information of a given pattern (which is usually going to be destroyed) to the final container.

Parameters
candidatePatternState whome visual information is to be added
statusStatus of the pattern (e.g. successfull, failed or overlap)
visualInfoFinal vector of visual information to store the visual information

Definition at line 824 of file GlobalPatternFinder.cxx.

826 {
827 if (!visualInfo) {
828 return;
829 }
830 // First save the buckets
831 std::vector<const SpacePointBucket*> buckets{cache.getParentBuckets()};
832
833 GlobalPattern pattern {convertToPattern(cache)};
834 // Check whether the visual info about this pattern is already in the container
835 if (auto it =std::ranges::find_if(*visualInfo, [&pattern](const auto& v){
836 return v.patternCopy && *v.patternCopy == pattern; }); it != visualInfo->end()) {
837 it->status = status; // Update the status if the pattern is already in the container
838 return;
839 }
840 visualInfo->push_back(*cache.visualInfo);
841
842 std::ranges::copy(buckets, std::back_inserter(visualInfo->back().parentBuckets));
843
844 visualInfo->back().patternCopy = std::make_unique<GlobalPattern>(std::move(pattern));
845 visualInfo->back().status = status;
846}
GlobalPattern convertToPattern(const PatternState &candidate) const
Method to convert a PatternState into a GlobalPattern object.
const IIntersectionCache * cache() const
Retrieve the associated cache block, if it exists.
status
Definition merge.py:16

◆ brief()

GlobalPatternFinder::PatternPrintView MuonR4::FastReco::GlobalPatternFinder::brief ( const PatternState & p)
staticprivate

Print the pattern state with brief information.

Definition at line 608 of file GlobalPatternFinderDefs.cxx.

608 {
609 return {p, /*detailed=*/false};
610 }

◆ checkLayerOrdering()

GlobalPatternFinder::LayerOrdering MuonR4::FastReco::GlobalPatternFinder::checkLayerOrdering ( const HitPayload & hit1,
const HitPayload & hit2 )
staticprivate

Method to check the logical layer ordering of two hits.

Parameters
hit1first hit
hit2second hit
Returns
: the logical measurement layer ordering of the two hits

Hits in the same spectrometer sector

Hits in the same station and different sectors. We can have this case for hits in the overlap region of two adjacent sectors.

Hit in different stations but same station layer. Expected to happen only for Inner and Middle

If both hits are in the middle layer, the one in the barrel comes first

If both hits are in the inner layer, we use the global R, since in large sector BI comes first, while in small sector EI comes first.

If we have one hit in Inner layer for sure it comes first

If we have one hit in Outer layer for sure it comes last

If we have one hit in BarrelExtended and the other in the Middle layer, the former comes first

If we have one hit in Extended (EE) layer and the other in the Middle layer, it depends if the latter is endcap or barrel

Definition at line 763 of file GlobalPatternFinder.cxx.

764 {
765 using enum LayerOrdering;
766 auto getLayerOrdering = [](const bool isLayer1Lower) {
767 return isLayer1Lower ? eLowerLayer : eHigherLayer;
768 };
769 if (hit1 == hit2) {
770 return eSameLayer;
771 }
772 StIndex st1 {hit1.station};
773 StIndex st2 {hit2.station};
774 if (st1 == st2) {
776 if (hit1->msSector() == hit2->msSector()) {
777 if (hit1.locLayer == hit2.locLayer) {
778 return eSameLayer;
779 }
780 return getLayerOrdering(hit1.locLayer < hit2.locLayer);
781 }
784 const double delta {isBarrel(st1)
785 ? hit1.position.perp() - hit2.position.perp()
786 : std::abs(hit1.position.z()) - std::abs(hit2.position.z())};
787 if (std::abs(delta) <= Acts::s_epsilon) {
788 return eSameLayer;
789 }
790 return getLayerOrdering(delta < 0.);
791 }
792 LayerIndex layer1 {toLayerIndex(st1)};
793 LayerIndex layer2 {toLayerIndex(st2)};
794 if (layer1 == layer2) {
796 if (layer1 == LayerIndex::Middle) {
798 return getLayerOrdering(st1 == StIndex::BM);
799 }
800 if (layer1 == LayerIndex::Inner) {
802 return getLayerOrdering(hit1.position.perp() < hit2.position.perp());
803 }
804 throw std::runtime_error("Unexpected to have two pattern-compatible hits one in BO and the other in EO.");
805 }
806 if (layer1 == LayerIndex::Inner || layer2 == LayerIndex::Inner) {
808 return getLayerOrdering(layer1 == LayerIndex::Inner);
809 }
810 if (layer1 == LayerIndex::Outer || layer2 == LayerIndex::Outer) {
812 return getLayerOrdering(layer2 == LayerIndex::Outer);
813 }
814 if (layer1 == LayerIndex::BarrelExtended || layer2 == LayerIndex::BarrelExtended) {
816 return getLayerOrdering(layer1 == LayerIndex::BarrelExtended);
817 }
819 if (layer1 == LayerIndex::Extended) {
820 return getLayerOrdering(st2 == StIndex::EM);
821 }
822 return getLayerOrdering(st1 == StIndex::BM);
823}
LayerOrdering
Enum to express the logical measurement layer ordering given two hits.
Muon::MuonStationIndex::LayerIndex LayerIndex
Type alias for the station layer index.
bool isBarrel(const ChIndex index)
Returns true if the chamber index points to a barrel chamber.
LayerIndex toLayerIndex(ChIndex index)
convert ChIndex into LayerIndex

◆ constructTree()

GlobalPatternFinder::SearchTreeData MuonR4::FastReco::GlobalPatternFinder::constructTree ( const ActsTrk::GeometryContext & gctx,
std::span< const SpacePointContainer * > spacepoints ) const
private

Construct the search tree from the given spacepoint containers.

The hit payloads are stored in a vector, and the tree will contain the index of the hits in the vector. Hits duplicated across overlapping sectors share the same payload. The tree does not contain only-phi hits.

Parameters
gctxGeometry context
spacepointsVector of space point containers
hitPayloadsVector of hit payloads to be filled with the spacepoints.
Returns
: Constructed search tree

First estimate the number of hits

Try to duplicate the hit in the neighboring sectors if it is close to the sector border.
This ensures that we can find patterns crossing the sector borders.

Check whether the hit belongs to the left or right sector as well

Definition at line 651 of file GlobalPatternFinder.cxx.

652 {
653
654 std::vector<HitPayload> hitPayloads{};
655 using SectorProjector = ExpandedSector::SectorProjector;
656 using enum SectorProjector;
658 size_t totalHits = 0;
659 for (const SpacePointContainer* spc : spacepoints) {
660 for (const SpacePointBucket* bucket : *spc) {
661 totalHits += bucket->size();
662 }
663 }
664 hitPayloads.reserve(totalHits);
665
666 for (const SpacePointContainer* spc : spacepoints) {
667 for (const SpacePointBucket* bucket : *spc) {
668
669 const Acts::Transform3& localToGlobal {bucket->msSector()->localToGlobalTransform(gctx)};
670
671 for (const auto& hit : *bucket) {
672 // Ignore only-phi hits and MDT hits if desired
673 if (!hit->measuresEta() || (!m_cfg.useMdtHits && hit->isStraw())) {
674 continue;
675 }
676
677 hitPayloads.emplace_back(gctx.context(), hit.get(), bucket, localToGlobal);
678
679 if (m_logger->level() <= Acts::Logging::Level::VERBOSE) {
680 const HitPayload& newHit {hitPayloads.back()};
681 std::ostringstream oss{};
682 oss<<__func__<<"() Building hit from "<<*hit<<std::endl<<"PhiCov: "<<newHit.phiCov
683 <<", Pos: "<<Amg::toString(newHit.position)<<", SensorDir: "<<Amg::toString(newHit.sensorDir(gctx.context()));
684 if (newHit->isStraw()) {
685 oss<<", discCov: "<<newHit.stripAngle;
686 } else {
687 oss<<"orthogonalStrips: "<<!newHit.nonOrthogonalStrips;
688 }
689 ACTS_VERBOSE(oss.str());
690 }
691 }
692 }
693 }
694
695 SearchTree_t::vector_t treeData{};
696 treeData.reserve(3 * hitPayloads.size());
697
698 uint8_t msSector = hitPayloads.front()->msSector()->sector();
699 const SpacePointBucket* currentBucket = hitPayloads.front().bucket;
700 for (const HitPayload& hit : hitPayloads) {
701 ACTS_VERBOSE(__func__<<"() Spacepoint: " << *hit);
702 const Amg::Vector3D& pos {hit.position};
703 const ExpandedSector hitExpSector {pos.phi()};
704 if (hit.bucket != currentBucket) {
705 currentBucket = hit.bucket;
706 msSector = hit->msSector()->sector();
707 }
708
711 for (const SectorProjector proj : {leftOverlap, center, rightOverlap}) {
713 const ExpandedSector expSect {msSector, proj};
714 if (proj != SectorProjector::center && hit->measuresPhi() && expSect != hitExpSector) {
715 ACTS_VERBOSE("addHitToTree() Hit with "<<hitExpSector<<" is not compatible with "<<expSect);
716 continue;
717 }
718
719 /* Project the hit onto the plane along the sector radial direction.
720 * This allows to remove the bias of hit displacement in phi direction */
721 const Amg::Vector3D planeNormal {expSect.normalDir()};
722 const double projR {hit->measuresPhi()
723 ? pos.perp()
724 : (pos - pos.dot(planeNormal) * planeNormal).perp()};
725
726 std::array<double, 2> coords{};
727 coords[Acts::toUnderlying(SeedCoords::eTheta)] = atan2(projR, pos.z());
728 coords[Acts::toUnderlying(SeedCoords::eSector)] = expSect.sector();
729
730 ACTS_VERBOSE("addHitToTree() Add hit: Z: " << pos.z() << ", R: " << pos.perp()
731 <<", ProjR: "<<projR<< ", Phi: "<< inDeg(pos.phi())
732 <<", SectorPhi: "<< inDeg(expSect.phi())<<" and coordinates ["
733 <<coords[0]<<", "<<coords[1]<<"] to search tree");
734 treeData.emplace_back(std::move(coords), &hit);
735 }
736 }
737 ACTS_VERBOSE(__func__<<"() Create a new tree with "<<treeData.size()
738 <<" entries and "<<hitPayloads.size()<<" hits.");
739 return SearchTreeData{std::move(hitPayloads), SearchTree_t{std::move(treeData)}};
740}
SectorProjector
Enumeration to select the sector projection of the regular MS sector.
Acts::KDTree< 2, const HitPayload *, double, std::array, 50 > SearchTree_t
Definition of the search tree class.
@ eSector
Expanded sector coordinate of the associated spectrometer sector
std::string toString(const Translation3D &translation, int precision=4)
GeoPrimitvesToStringConverter.
DataVector< SpacePointBucket > SpacePointContainer
Abrivation of the space point container type.
constexpr float inDeg(const float rad)

◆ convertToPattern() [1/2]

GlobalPattern MuonR4::FastReco::GlobalPatternFinder::convertToPattern ( const PatternState & candidate) const
private

Method to convert a PatternState into a GlobalPattern object.

Parameters
candidatePatternState to be converted
Returns
: Converted GlobalPattern

Add eta hits

Add phi-only hits

Definition at line 603 of file GlobalPatternFinder.cxx.

603 {
604 GlobalPattern::HitCollection hitPerStation{};
605 std::vector<const SpacePointBucket*> parentBuckets{};
607 for (uint8_t st{0u}; st < s_nStations; ++st) {
608 const auto& hits {cache.hitsPerStation[st]};
609 if (hits.empty()) continue;
610
611 auto& outHits {hitPerStation[static_cast<StIndex>(st)]};
612 outHits.reserve(hits.size());
613
614 std::ranges::for_each(hits, [&outHits, &parentBuckets](const CandidateHit& h){
615 outHits.push_back(h.sp());
616 if (std::ranges::find(parentBuckets, h->bucket) == parentBuckets.end()) {
617 parentBuckets.push_back(h->bucket);
618 }
619 });
620 }
621
623 for (const HitPayload& hit : cache.phiOnlyHits) {
624 hitPerStation[static_cast<StIndex>(hit.station)].push_back(hit.spacePoint);
625 }
626 GlobalPattern pattern{std::move(hitPerStation), std::move(parentBuckets)};
627 pattern.setTheta(cache.patTheta);
628 pattern.setPhi(cache.patPhi);
629 // Set the pattern sector(s) and theta.
630 pattern.setSector(cache.expSect.sector());
631 // Set pattern quality information.
632 pattern.setNPrecisionLayers(cache.nPrecisionLayers);
633 pattern.setNTriggerLayers(cache.nTriggerLayers);
634 pattern.setNPhiLayers(cache.nPhiLayers);
635 pattern.setMeanNormResidual2(cache.getMeanResidual2());
636 return pattern;
637}
static const int s_nStations
Number of stations.
std::unordered_map< StIndex, std::vector< HitType > > HitCollection
m_data push_back(elt)
@ u
Enums for curvilinear frames.
Definition ParamDefs.h:77

◆ convertToPattern() [2/2]

std::vector< GlobalPattern > MuonR4::FastReco::GlobalPatternFinder::convertToPattern ( const PatternStateVec & candidates) const
private

Method to convert a vector of PatternStates into GlobalPattern objects.

Parameters
candidatesPatternStates to be converted
Returns
: Vector of converted GlobalPatterns

Definition at line 640 of file GlobalPatternFinder.cxx.

640 {
641 std::vector<GlobalPattern> patterns{};
642 patterns.reserve(cache.size());
643 std::transform(cache.begin(), cache.end(), std::back_inserter(patterns),
644 [this](const PatternState& cacheEntry) {
645 return convertToPattern(cacheEntry);
646 });
647 return patterns;
648}

◆ detailed()

GlobalPatternFinder::PatternPrintView MuonR4::FastReco::GlobalPatternFinder::detailed ( const PatternState & p)
staticprivate

Print the pattern state with detailed information.

Definition at line 612 of file GlobalPatternFinderDefs.cxx.

612 {
613 return {p, /*detailed=*/true};
614 }

◆ extendPatterns()

void MuonR4::FastReco::GlobalPatternFinder::extendPatterns ( const Acts::GeometryContext & gctx,
PatternStateVec & startPatterns,
PatternStateVec & endPatterns,
const CandidateHit & testHit,
const Amg::Vector3D & beamSpot,
std::vector< PatHitVisual > * visualInfo = nullptr ) const
private

Main function controlling the development of patterns, including pattern branching when necessary.

It tests pattern compatibility of a set of active patterns (patterns produced from the same seed hit) against one test hit. At the end, activePatterns contains the surviving patterns.

Parameters
startPatternsVector of active patterns to be extended
endPatternsVector to store the surviving patterns after testing against the test hit.
testHitHit to be tested against the patterns
beamSpotBeam spot position, needed when the pattern line cannot be reliably defined from the pattern hits
visualInfoPointer to visual information for pattern visualization (nullptr if the VisualizationTool is disabled)

Check the pattern has not already missed too many layers compared to other patterns.

Prunes pattern hypotheses within groups sharing the same last-hit layer. This step reduces branching by
keeping only the best-scoring pattern within each last-hit equivalence group, while preserving all patterns when the last-hit layer matches the reference layer (to allow further branching).

Check angular compatibility of the test hit and the pattern

TO DO: Study feasibility of loosening the criteria for low-confidence hits with OR

If hit is compatible but with poor confidence, we create both a pattern with the hit and a pattern without the hit, to keep also the possibility of rejecting this hit in the next iterations. First we make sure that the low-confidence pattern is original, i.e. accumulating not seen hits

Add the new pattern to the list of next patterns

Update visual information of the original pattern

Branch the pattern: we clone it and overwrite the existing hit with the test hit

Update visual information

Definition at line 254 of file GlobalPatternFinder.cxx.

259 {
260 endPatterns.clear();
261 ACTS_VERBOSE(__func__<<"() *** Test "<<testHit<<" against " << startPatterns.size() << " active patterns.");
262
263 // Compute the minimum number of missed layer hits among the active patterns,
264 // to use as reference for pruning patterns with too many missed layers.
265 auto missedLayers = [&testHit](const PatternState& pat) -> unsigned {
266 return std::abs(pat.lastInsertedHit.globLayer - testHit.globLayer);
267 };
268
269 unsigned minMissedLayers {std::numeric_limits<unsigned>::max()};
270 std::ranges::for_each(startPatterns, [&missedLayers, &minMissedLayers](const PatternState& pat){
271 minMissedLayers = std::min(minMissedLayers, missedLayers(pat));
272 });
273
274 const bool shouldPrune {startPatterns.size() > 1 &&
275 std::ranges::any_of(startPatterns, [](const PatternState& p){
276 return p.nBendingLayers() > 2; })};
277
278 for (auto [i, pat] : Acts::enumerate(startPatterns)) {
279 if (pat.isOverlap) {
281 continue;
282 }
284 if (pat.lastInsertedHit->station == testHit->station &&
285 missedLayers(pat) > std::max(m_cfg.maxMissLayersInStation, minMissedLayers)) {
286 ACTS_VERBOSE(__func__<<"() Pattern " << detailed(pat) << "\nhas missed " << (int)missedLayers(pat)
287 << " layer hits, above the max allowed - abort pattern.");
289 continue;
290 }
294 if (shouldPrune && pat.lastInsertedHit.globLayer != testHit.globLayer &&
295 std::ranges::find_if(std::next(startPatterns.begin(), i + 1), startPatterns.end(), [&](PatternState& p){
296 if (p.lastInsertedHit != pat.lastInsertedHit || p.isOverlap) return false;
297
298 if (isBetter(pat, p)) {
299 ACTS_VERBOSE("extendPatterns() Pruning: "<<detailed(pat)<<"\nis BETTER than "<<detailed(p));
300 p.isOverlap = true;
301 return false;
302 }
303 ACTS_VERBOSE("extendPatterns() Pruning: "<<detailed(p)<<"\nis BETTER than "<<detailed(pat));
304 return true; }) != startPatterns.end()) {
306 continue;
307 }
309 const auto [residual, resSigma, result] {pat.checkLineComp(gctx,testHit, beamSpot)};
310 switch (result) {
313 const bool lowConfidenceRes {resSigma > m_cfg.lowConfidenceResSigma &&
314 residual / resSigma > 2.};
315 if (lowConfidenceRes) {
316 ACTS_VERBOSE(__func__<<"() Low-confidence hit: residual pull "<<residual / resSigma);
320 if (std::ranges::any_of(endPatterns, [&testHit, &pat](const PatternState& p) {
321 return p.lastInsertedHit == testHit &&
322 (p.prevLayerHit == pat.lastInsertedHit || p.nBendingLayers() > (pat.nBendingLayers() + 1u)); })) {
323 ACTS_VERBOSE(__func__<<"() Forking leads to existing pattern - reject.");
324 break;
325 }
327 endPatterns.push_back(pat);
328 endPatterns.back().addHit(testHit, residual, resSigma);
330 if (visualInfo) {
331 pat.visualInfo->discardedHits.push_back(testHit.sp());
332 }
333 break;
334 }
335 ACTS_VERBOSE(__func__<<"() Hit compatible - add to pattern. Residual pull "<<residual / resSigma);
336 pat.addHit(testHit, residual, resSigma);
337 break;
338 }
340 /* Check first if the branched pattern already exists*/
341 if (std::ranges::any_of(endPatterns, [&testHit, &pat](const PatternState& p) {
342 return p.lastInsertedHit == testHit && p.prevLayerHit == pat.prevLayerHit; })) {
343 ACTS_VERBOSE(__func__<<"() Hit compatible & on same layer of last added hit - branched pattern already exists.");
344 break;
345 }
347 ACTS_VERBOSE(__func__<<"() Hit compatible & on same layer of last added hit - branch pattern.");
348 endPatterns.push_back(pat);
349 endPatterns.back().overWriteHit(testHit, residual, resSigma);
350
352 if (visualInfo) {
353 pat.visualInfo->discardedHits.push_back(testHit.sp());
354 }
355 break;
356 }
358 ACTS_VERBOSE(__func__<<"() Hit is not compatible with the pattern - reject hit.");
359 if (visualInfo) {
360 pat.visualInfo->discardedHits.push_back(testHit.sp());
361 }
362 break;
363 }
364 }
365 endPatterns.push_back(std::move(pat));
366 }
367 startPatterns.clear();
368};
void addVisualInfo(const PatternState &candidate, PatHitVisual::PatternStatus status, std::vector< PatHitVisual > *visualInfo) const
Helper function to add visual information of a given pattern (which is usually going to be destroyed)...
@ eRejectHit
Test failed, discard the hit.
@ eBranchPattern
Test successfull with multiple pattern hits on same layer, branch the pattern.
@ eAddHit
Test successfull, add hit to pattern.

◆ findPatterns()

std::vector< GlobalPattern > MuonR4::FastReco::GlobalPatternFinder::findPatterns ( const ActsTrk::GeometryContext & gctx,
std::span< const SpacePointContainer * > spacepoints ) const

Main methods steering the pattern finding.

Given the space-point containers, it creates the search tree,
builds patterns in eta, attach compatible only-phi measurements, and convert PatternStates into GlobalPatterns

Parameters
gctxGeometry context
spacepointsVector of space point containers
Returns
: Vector of found patterns

Create the search tree by ordering hits in theta and expanded spectrometer sector and find patterns in eta. The hit payloads are stored in a vector, and the tree will contain the index of the hits in the vector.

Add phi-only hits to the patterns

Add the successfull pattern to visual info, as we won't touch it again

Plot patterns

Definition at line 36 of file GlobalPatternFinder.cxx.

37 {
41 const SearchTreeData treeData {constructTree(gctx, spacepoints)};
42
43 auto visualInfo {m_cfg.visionTool ? std::make_unique<std::vector<PatHitVisual>>() : nullptr};
44 PatternStateVec patterns{findPatternsInEta(gctx.context(), treeData.tree, visualInfo.get())};
45
48
49 for (const PatternState& pat : patterns) {
52 }
53
55 if (visualInfo) {
56 m_cfg.visionTool->plotPatternBuckets(Gaudi::Hive::currentContext(), "GlobPatFind_", std::move(*visualInfo));
57 }
59}
SearchTreeData constructTree(const ActsTrk::GeometryContext &gctx, std::span< const SpacePointContainer * > spacepoints) const
Construct the search tree from the given spacepoint containers.
void addPhiOnlyHits(const ActsTrk::GeometryContext &gctx, PatternStateVec &patterns) const
Method to add phi-only measurements to existing PatternStates.
PatternStateVec findPatternsInEta(const Acts::GeometryContext &gctx, const SearchTree_t &orderedSpacepoints, std::vector< PatHitVisual > *visualInfo=nullptr) const
Method steering the global pattern building in the bending plane.

◆ findPatternsInEta()

GlobalPatternFinder::PatternStateVec MuonR4::FastReco::GlobalPatternFinder::findPatternsInEta ( const Acts::GeometryContext & gctx,
const SearchTree_t & orderedSpacepoints,
std::vector< PatHitVisual > * visualInfo = nullptr ) const
private

Method steering the global pattern building in the bending plane.

Parameters
orderedSpacepointsSearch tree with spacepoints ordered by their corresponding coordinates
visualInfoPointer to visual information for pattern visualization (nullptr if the VisualizationTool is disabled). Needed to add visual info about pattern candidates discarded during the building stage.
Returns
: resulting vector of PatternStates successfully built

Define candidate hit buffer

Define two PatternState buffers to avoid reallocations

TODO: Retrieve the beamspot if desired

Helper function to count existing patterns containing a hit

Parameters
hitThe hit to check
coordsThe coordinates of the hit
Returns
The number of existing patterns containing the hit

We try to build a pattern in eta starting from every hit in the three

Get the seed hit

Check the seed is in the current seeding layer, and if seeding from MDT hits is enabled

check how many existing patterns contain this hit

Define the search range.

Search hits with same expanded sector

Define theta window size. While a middle-layer seed already constrains the track direction more tightly (the line must connect to hits on both sides), inner- and outer-layer seeds are more loosely constraining, and we need a larger theta window with double size to achieve the same angular acceptance as inner/outer seeds

Search for compatible spacepoints with the seed and check if there are enough to build a pattern

Check that the candidate hits extend at least in two layers

Sort the compatible spacepoints by global logical layer

If the two hits are in the same layer, sort them by local y coordinate.

Assign global layer number. This will avoid re-computing it many times later

Start pattern building from the seed

Helper function to extend a given pattern with a set of hits. We can have pattern branching when a pattern is compatible with multiple hits on the same layer, increasing the number of active patterns. For each new hit, extendPatterns will try to extend every active pattern and remove the ones not meeting continuation criteria.

Parameters
beginThe iterator pointing to the first hit to process.
endThe iterator pointing to the end of the hit range.
toExtendThe pattern to extend.
Returns
The vector of resulting patterns.

First search for compatible hits from the seed layer onwards

When inverting the search direction, update last inserted hit and line anchor

Then try to proceed toward the innermost layer

Ensure there are no overlaps

Definition at line 61 of file GlobalPatternFinder.cxx.

63 {
64 constexpr auto thetaIdx {Acts::toUnderlying(SeedCoords::eTheta)};
65 constexpr auto sectorIdx {Acts::toUnderlying(SeedCoords::eSector)};
66
68 std::vector<CandidateHit> candidateHits{};
69 candidateHits.reserve(100);
70
72 PatternStateVec startPatternBuff{}, endPatternBuff{};
73 startPatternBuff.reserve(10);
74 endPatternBuff.reserve(10);
75
77 const Amg::Vector3D beamSpot{Amg::Vector3D::Zero()};
78
79 PatternStateVec outPatterns{};
80 outPatterns.reserve(10);
85 auto countPatterns = [this](const PatternStateVec& patterns,
86 const HitPayload& hit,
87 const SearchTree_t::coordinate_t& coords) -> uint8_t {
88 return std::ranges::count_if(patterns, [&](const PatternState& pattern){
89 if (std::abs(pattern.patTheta - coords[thetaIdx]) > 2.*m_cfg.thetaSearchWindow ||
90 !pattern.expSect.isNeighbour(
91 ExpandedSector{static_cast<std::int8_t>(coords[sectorIdx])})) {
92 return false;
93 }
94 return pattern.isInPattern(hit);
95 });
96 };
97 using enum SeedCoords;
98 for (const auto seedingLayer : m_cfg.layerSeedings) {
100 for (const auto& [seedCoords, seedPtr] : orderedSpacepoints) {
102 const HitPayload& seed {*seedPtr};
104 const LayerIndex seedLayer {toLayerIndex(seed.station)};
105 if (seedLayer != seedingLayer || (seed->isStraw() && !m_cfg.seedFromMdt)) {
106 continue;
107 }
108 ACTS_VERBOSE(__func__<<"() New seed hit "<<*seed<<", coordinates ["<<seedCoords[0]<<", "<<seedCoords[1]<<"]");
110 uint8_t nExistingPatterns {countPatterns(outPatterns, seed, seedCoords)};
111 if (nExistingPatterns >= m_cfg.maxSeedAttempts) {
112 // Try first to resolve overlaps and re-count the number of patterns containing the seed
113 outPatterns = resolveOverlaps(outPatterns, visualInfo);
114 nExistingPatterns = countPatterns(outPatterns,seed, seedCoords);
115 if (nExistingPatterns >= m_cfg.maxSeedAttempts) {
116 ACTS_VERBOSE(__func__<<"() Seed has already been used in "
117 <<static_cast<int>(nExistingPatterns)
118 <<" patterns, which is above the limit - skip this seed.");
119 continue;
120 }
121 }
123 SearchTree_t::range_t selectRange{};
125 selectRange[sectorIdx].shrink(seedCoords[sectorIdx] - 0.1, seedCoords[sectorIdx] + 0.1);
129 const double thetaHalfWindow {(seedLayer == LayerIndex::Inner || seedLayer == LayerIndex::Outer)
130 ? m_cfg.thetaSearchWindow : 0.5*m_cfg.thetaSearchWindow};
131 selectRange[thetaIdx].shrink(seedCoords[thetaIdx] - thetaHalfWindow, seedCoords[thetaIdx] + thetaHalfWindow);
133 candidateHits.clear();
134 orderedSpacepoints.rangeSearchMapDiscard(selectRange, [&](const SearchTree_t::coordinate_t& /*coords*/,
135 const HitPayload* hit) {
136 candidateHits.emplace_back(hit, 0u);
137 });
138 if (candidateHits.size() < m_cfg.minTriggerLayers + m_cfg.minPrecisionLayers) {
139 ACTS_VERBOSE(__func__<<"() Found "<<candidateHits.size()<<" candidate hits, below minimum required - skip seed.");
140 continue;
141 }
143 if (std::ranges::none_of(candidateHits, [this, seedLayer](const CandidateHit& c){
144 return m_cfg.idHelperSvc->layerIndex(c.sp()->identify()) != seedLayer; }) ) {
145 ACTS_VERBOSE(__func__<<"() All candidates in same station layer, and we need at least two - skip seed.");
146 continue;
147 }
149 std::ranges::sort(candidateHits, [](const CandidateHit& c1, const CandidateHit& c2){
151 if (ordering == LayerOrdering::eSameLayer) {
153 return c1.sp()->localPosition().y() < c2.sp()->localPosition().y();
154 }
156 });
158 for (std::size_t i {1}; i < candidateHits.size(); ++i) {
159 candidateHits[i].globLayer = candidateHits[i - 1].globLayer +
160 (checkLayerOrdering(*candidateHits[i - 1], *candidateHits[i]) != LayerOrdering::eSameLayer);
161 }
162 if (candidateHits.back().globLayer + 1u < (m_cfg.minTriggerLayers + m_cfg.minPrecisionLayers)) {
163 ACTS_VERBOSE(__func__<<"() Found "<<candidateHits.size()<<" candidate hits on "
164 <<static_cast<int>(candidateHits.back().globLayer + 1u)
165 <<" layers, below the minimum required - skip this seed.");
166 continue;
167 }
168 if (m_logger->level() <= Acts::Logging::Level::VERBOSE) {
169 ACTS_VERBOSE(__func__<<"() Found "<< candidateHits.size()<<" candidate hits: ");
170 for (const auto& c : candidateHits) {
171 ACTS_VERBOSE(__func__<<"() \t**"<<c);
172 }
173 }
174
176 const auto seedItr {std::ranges::find_if(candidateHits,
177 [&seed](const CandidateHit& c){ return *c == seed; })};
178 assert(seedItr != candidateHits.end());
179 const CandidateHit& seedCand {*seedItr};
180
181 PatternState patternSeed{seedCand, static_cast<std::int8_t>(seedCoords[sectorIdx]), &m_cfg, m_logger.get()};
182 if (visualInfo) {
183 patternSeed.visualInfo = std::make_unique<PatHitVisual>(
184 seed.spacePoint, seedCoords[thetaIdx] - thetaHalfWindow, seedCoords[thetaIdx] + thetaHalfWindow);
185 }
186
195 auto processHitRange = [&](const auto begin,
196 const auto end,
197 PatternState&& toExtend) -> PatternStateVec {
198 startPatternBuff.clear();
199 startPatternBuff.push_back(std::move(toExtend));
200
201 for (auto testItr = begin; testItr != end; ++testItr) {
202 const CandidateHit& testHit {*testItr};
203 if (testHit.globLayer == seedCand.globLayer) {
204 continue; // skip hits on the same layer as the seed
205 }
206 extendPatterns(gctx, startPatternBuff, endPatternBuff, testHit, beamSpot, visualInfo);
207 // Swap the buffers for the next iteration
208 std::swap(startPatternBuff, endPatternBuff);
209 }
210 return startPatternBuff.size() > 1
211 ? resolveOverlaps(startPatternBuff, visualInfo)
212 : PatternStateVec{std::move(startPatternBuff.back())};
213 };
214
216 PatternStateVec forwardExtended {processHitRange(std::next(seedItr), candidateHits.end(), std::move(patternSeed))};
217
219 ACTS_VERBOSE(__func__<<"() Finished forward search, found "<<forwardExtended.size()<<" forward patterns, start backward search.");
220 PatternStateVec backwardExtended{};
221 backwardExtended.reserve(2*forwardExtended.size());
222
223 for (PatternState& pat : forwardExtended) {
224 ACTS_VERBOSE(__func__<<"() Start backward search for pattern "<<detailed(pat));
225 pat.moveLineAnchorHit(seedCand);
226 pat.lastInsertedHit = seedCand;
227
229 std::ranges::move(
230 processHitRange(std::reverse_iterator(seedItr), candidateHits.rend(), std::move(pat)),
231 std::back_inserter(backwardExtended)
232 );
233 }
235 if (backwardExtended.size() > 1) {
236 backwardExtended = resolveOverlaps(backwardExtended, visualInfo);
237 }
238
239 for (PatternState& pat : backwardExtended) {
240 pat.meanNormResidual2 /= pat.nBendingLayers();
241 if (!passPatternCuts(pat)) {
243 continue;
244 }
245 ACTS_VERBOSE(__func__<<"() Add new pattern "<<detailed(pat));
246 pat.isFinalized = true;
247 outPatterns.push_back(std::move(pat));
248 }
249 }
250 }
251 ACTS_VERBOSE(__func__<<"() Found in total "<<outPatterns.size()<<" patterns in eta before overlap removal");
252 return resolveOverlaps(outPatterns, visualInfo);
253}
static LayerOrdering checkLayerOrdering(const HitPayload &hit1, const HitPayload &hit2)
Method to check the logical layer ordering of two hits.
PatternStateVec resolveOverlaps(PatternStateVec &toResolve, std::vector< PatHitVisual > *visualInfo=nullptr) const
Method to remove overlapping patterns.
bool passPatternCuts(const PatternState &pat) const
Method to check if a pattern passes the quality cuts.
SeedCoords
Abrivation of the seed coordinates.
void extendPatterns(const Acts::GeometryContext &gctx, PatternStateVec &startPatterns, PatternStateVec &endPatterns, const CandidateHit &testHit, const Amg::Vector3D &beamSpot, std::vector< PatHitVisual > *visualInfo=nullptr) const
Main function controlling the development of patterns, including pattern branching when necessary.
return m_collEvts back().back().max_entries
AthConfigFlags beamSpot(AthConfigFlags flags, str instanceName, str recoMode)

◆ isBetter()

bool MuonR4::FastReco::GlobalPatternFinder::isBetter ( const PatternState & a,
const PatternState & b )
staticprivate

Method to compare two patterns and define which one is better.

Parameters
afirst pattern
bsecond pattern
Returns
: true if pattern a is better than pattern b, false otherwise

For patterns that differ by 1–2 layers, don't sacrifice fit quality
unless the extra layers are genuinely comparable. For a ≥3-layer difference,
the multiplicity advantage is strong enough to dominate.

Definition at line 741 of file GlobalPatternFinder.cxx.

741 {
742 const double resA {a.getMeanResidual2()};
743 const double resB {b.getMeanResidual2()};
744 const double resDiff {
745 std::abs(resA - resB) / std::max(resA, resB)
746 };
747 const int nLayerDiff {a.nBendingLayers() - b.nBendingLayers()};
748 const int nPrecLayDiff {a.nPrecisionLayers - b.nPrecisionLayers};
749
753 if ((nLayerDiff == 0 && nPrecLayDiff == 0) ||
754 (std::abs(nLayerDiff) < 3 && resDiff > 0.1)) {
755 return resA < resB;
756 }
757 if (nLayerDiff == 0) {
758 return nPrecLayDiff > 0;
759 }
760 return nLayerDiff > 0;
761}
static Double_t a

◆ logger()

const Acts::Logger & MuonR4::FastReco::GlobalPatternFinder::logger ( ) const
inlineprivate

Definition at line 220 of file GlobalPatternFinder.h.

220 {
221 return *m_logger;
222 }

◆ passPatternCuts()

bool MuonR4::FastReco::GlobalPatternFinder::passPatternCuts ( const PatternState & pat) const
private

Method to check if a pattern passes the quality cuts.

Parameters
patternPattern to be checked
Returns
: true if the pattern passes the cuts, false otherwise

Check that the pattern meets the minimum requirements for trigger and precision layers

Check requirement on the residual

Definition at line 369 of file GlobalPatternFinder.cxx.

369 {
371 if (pat.nTriggerLayers < m_cfg.minTriggerLayers ||
372 pat.nPrecisionLayers < m_cfg.minPrecisionLayers ||
373 std::ranges::count_if(pat.hitsPerStation,
374 [this](const auto& hits) { return hits.size() >= m_cfg.minStationLayers; }) < 2) {
375 ACTS_VERBOSE(__func__<<"() Pattern " << detailed(pat) << "\ndoes not meet minimum layer requirements - reject.");
376 return false;
377 }
379 if (pat.meanNormResidual2 > m_cfg.meanNormRes2Cut) {
380 ACTS_VERBOSE(__func__<<"() Pattern " << detailed(pat) << "\ndoes not meet the mean norm residual2 cut - reject.");
381 return false;
382 }
383 return true;
384}

◆ resolveOverlaps()

GlobalPatternFinder::PatternStateVec MuonR4::FastReco::GlobalPatternFinder::resolveOverlaps ( PatternStateVec & toResolve,
std::vector< PatHitVisual > * visualInfo = nullptr ) const
private

Method to remove overlapping patterns.

Parameters
toResolvepattern to be resolved
visualInfoPointer to visual information for pattern visualization (nullptr if the VisualizationTool is disabled)
Returns
: resolved patterns

Check if two patterns overlap in space

Check first the geometrical overlap

Check the angular difference between the seed hits

If we reach here, the patterns can overlap geometrically, so check the hit content

Overlap if more than 50% of the hits of the smaller pattern are shared

Determine best pattern

Definition at line 386 of file GlobalPatternFinder.cxx.

387 {
388 ACTS_VERBOSE(__func__<<"() Resolving overlaps among "<<toResolve.size()<<" patterns.");
389 PatternStateVec outputPatterns{};
390 outputPatterns.reserve(toResolve.size());
392 auto areOverlapping = [this](const PatternState& a, const PatternState& b) {
394 if(!a.expSect.isNeighbour(b.expSect)) {
395 return false;
396 }
398 if (std::abs(a.patTheta - b.patTheta) > 2.*m_cfg.thetaSearchWindow) {
399 return false;
400 }
401 if (a.nPhiLayers > 0 && b.nPhiLayers > 0) {
402 if (std::abs(P4Helpers::deltaPhi(a.patPhi, b.patPhi)) > 5.*Gaudi::Units::deg) {
403 return false;
404 }
405 } else if (a.nPhiLayers > 0) {
406 if (!sectorMap.insideSector(b.expSect.msSector(), a.patPhi) ||
407 !sectorMap.insideSector(b.expSect.adjacentMsSector(), a.patPhi)) {
408 return false;
409 }
410 } else if (b.nPhiLayers > 0) {
411 if (!sectorMap.insideSector(a.expSect.msSector(), b.patPhi) ||
412 !sectorMap.insideSector(a.expSect.adjacentMsSector(), b.patPhi)) {
413 return false;
414 }
415 }
417 std::size_t nSharedHits{0}, nSharedStations{0};
418 for (std::size_t st{0u}; st < s_nStations; ++st) {
419 const auto& hitsA {a.hitsPerStation[st]};
420 const auto& hitsB {b.hitsPerStation[st]};
421 if (hitsA.empty() || hitsB.empty()) {
422 continue;
423 }
424 const std::size_t nSharedInStation = std::ranges::count_if(hitsA, [&](const CandidateHit& hitA){
425 return std::ranges::any_of(hitsB, [&hitA](const CandidateHit& hitB) {
426 return hitA.sp()->primaryMeasurement() == hitB.sp()->primaryMeasurement();
427 });
428 });
429 nSharedHits += nSharedInStation;
430 if (nSharedInStation >= m_cfg.minStationLayers) {
431 nSharedStations++;
432 }
433 }
435 const std::size_t minHits {std::min(a.nBendingLayers(), b.nBendingLayers())};
436 const std::size_t minStations {std::min(a.nStations(/*onlyGoodStations=*/ true), b.nStations(/*onlyGoodStations=*/ true))};
437 return nSharedHits >= 0.5 *minHits && nSharedStations >= std::min(2ul, minStations);
438 };
440 auto isBetterOverlap = [](const PatternState& a, const PatternState& b) {
441 const int nGoodStationDiff {a.nStations(/*onlyGoodStations=*/ true) - b.nStations(/*onlyGoodStations=*/ true)};
442 if (nGoodStationDiff != 0) {
443 return nGoodStationDiff > 0;
444 }
445 return isBetter(a,b);
446 };
447
448 for (auto it = toResolve.begin(); it != toResolve.end(); ++it) {
449 if (it->isOverlap) {
450 // If already marked as overlap, add to visual info, and discard the pattern
452 continue;
453 }
454 for (auto jt = std::next(it); jt != toResolve.end(); ++jt) {
455 if (jt->isOverlap || !areOverlapping(*it, *jt)) {
456 continue;
457 }
458 if (isBetterOverlap(*it, *jt)) {
459 ACTS_VERBOSE(__func__<<"() Pattern "<<detailed(*it)<<"\nis BETTER than "<<detailed(*jt));
460 jt->isOverlap = true;
461 } else {
462 it->isOverlap = true;
463 ACTS_VERBOSE(__func__<<"() Pattern "<<detailed(*jt)<<"\nis BETTER than "<<detailed(*it));
464 break;
465 }
466 }
467 if (!it->isOverlap) {
468 outputPatterns.push_back( std::move(*it));
469 } else {
470 // If overlap, add to visual info, as the pattern will be discarded
472 }
473 }
474 ACTS_VERBOSE(__func__<<"() Patterns surviving overlap removal: "<< outputPatterns.size());
475 return outputPatterns;
476}
static bool isBetter(const PatternState &a, const PatternState &b)
Method to compare two patterns and define which one is better.
double deltaPhi(double phiA, double phiB)
delta Phi in range [-pi,pi[
Definition P4Helpers.h:34

Member Data Documentation

◆ m_cfg

Config MuonR4::FastReco::GlobalPatternFinder::m_cfg
private

Global Pattern Recognition configuration.

Definition at line 217 of file GlobalPatternFinder.h.

◆ m_logger

std::unique_ptr<const Acts::Logger> MuonR4::FastReco::GlobalPatternFinder::m_logger {}
private

Logger for the Global Pattern Finder.

Definition at line 219 of file GlobalPatternFinder.h.

219{};

◆ m_spSorter

SpacePointPerLayerSorter MuonR4::FastReco::GlobalPatternFinder::m_spSorter {}
private

Spacepoint sorter per logical measurement layer.

Definition at line 214 of file GlobalPatternFinder.h.

214{};

◆ s_nStations

const int MuonR4::FastReco::GlobalPatternFinder::s_nStations {Acts::toUnderlying(StIndex::StIndexMax)}
staticprivate

Number of stations.

Definition at line 99 of file GlobalPatternFinder.h.

99{Acts::toUnderlying(StIndex::StIndexMax)};

The documentation for this class was generated from the following files: