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

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

#include <GlobalPatternFinder.h>

Inheritance diagram for MuonR4::FastReco::GlobalPatternFinder:
Collaboration diagram for MuonR4::FastReco::GlobalPatternFinder:

Classes

struct  Config
 Configuration object. More...
struct  HitPayload
 Hit information stored during pattern building. More...
struct  PatternState
 Pattern state object storing pattern information during construction. More...
struct  LineCompatibilityResult
 : Small struct to encapsulate the checkLineCompatibility result 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.
using SpacePointContainerVec = std::vector<const SpacePointContainer*>
 Abrivation for a vector of space-point containers.
using PatternVec = std::vector<GlobalPattern>
 Abrivation for a vector of global patterns.
using BucketPerContainer = std::unordered_map<const SpacePointContainer*, std::vector<const SpacePointBucket*>>
 Abrivation for a collection of space-point buckets grouped by their corresponding input container.
using PatternHitVisualInfo = MuonValR4::IFastRecoVisualizationTool::PatternHitVisualInfo
 Type alias for the visual information of a pattern.
using PatternHitVisualInfoVec = std::vector<PatternHitVisualInfo>
 Abrivation for a vector of visual information objects.

Public Member Functions

 GlobalPatternFinder (const std::string &name, Config &&config)
 Standard constructor.
PatternVec findPatterns (const ActsTrk::GeometryContext &gctx, const SpacePointContainerVec &spacepoints, BucketPerContainer &outBuckets) const
 Main methods steering the pattern finding.
bool msgLvl (const MSG::Level lvl) const
 Test the output level.
MsgStream & msg () const
 The standard message stream.
MsgStream & msg (const MSG::Level lvl) const
 The standard message stream.
void setLevel (MSG::Level lvl)
 Change the current logging level.

Private Types

enum class  SeedCoords : std::uint8_t { eSector , eTheta }
 Abrivation of the seed coordinates. More...
enum class  CompatibilityResult : std::int8_t { eAddHit = 0 , eBranchPattern = 1 , eRejectHit = -1 }
 : Enum for the possible outcomes of the line compatibility test of one pattern against one test hit More...
enum class  LayerOrdering : std::int8_t { eSameLayer , eLowerLayer , eHigherLayer }
 Enum to express the logical measurement layer ordering given two hits. More...
using SearchTree_t = Acts::KDTree<2, HitPayload, double, std::array, 5>
 Definition of the search tree class.
using TreeNode = std::pair<SearchTree_t::coordinate_t, HitPayload>
 Type alias for a tree node, formed by a hit payload and its indexing coordinates.
using PatternStateVec = std::vector<PatternState>

Private Member Functions

SearchTree_t constructTree (const ActsTrk::GeometryContext &gctx, const SpacePointContainerVec &spacepoints) const
 Method to construct the search tree by filling it up with spacepoints from the given containers.
PatternStateVec findPatternsInEta (const SearchTree_t &orderedSpacepoints, PatternHitVisualInfoVec *visualInfo=nullptr) const
 Method steering the building of patterns in eta.
void extendPatterns (PatternStateVec &activePatterns, const HitPayload &test, const HitPayload &seed, const HitPayload &prevCandidate, PatternHitVisualInfoVec *visualInfo=nullptr) const
 Function testing pattern compatibility of a set of active patterns (patterns produced from the same seed hit) against one test hit.
LineCompatibilityResult checkLineCompatibility (const HitPayload &seed, const HitPayload &test, const PatternState &pat) const
 Method to check the line compatibility of a test hit with a given pattern.
LineCompatibilityResult computeResidual (const HitPayload &seed, const HitPayload &test, const PatternState &pat, const uint8_t patHitIdx, const Amg::Vector3D &beamSpot) const
 Helper method to compute the residual of a test hit against a reference pattern hit and check whether it is within the acceptance window.
bool isBetter (const PatternState &a, const PatternState &b) const
 Operator to compare two patterns.
PatternStateVec resolveOverlaps (PatternStateVec &&toResolve, PatternHitVisualInfoVec *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.
bool isPhiCompatible (const double testPhi, const PatternState &pattern) const
 Method to check the phi compatibility of a test hit with a given pattern.
GlobalPattern convertToPattern (const PatternState &candidate) const
 Method to convert a PatternState into a GlobalPattern object.
PatternVec convertToPattern (const PatternStateVec &candidates) const
 Method to convert a vector of PatternStates into GlobalPattern objects.
void addVisualInfo (const PatternState &candidate, PatternHitVisualInfo::PatternStatus status, PatternHitVisualInfoVec *visualInfo) const
 Helper function to add visual information of a given pattern (which is usually going to be destroyed) to the final container.
void initMessaging () const
 Initialize our message level and MessageSvc.

Static Private Member Functions

static LayerOrdering checkLayerOrdering (const HitPayload &hit1, const HitPayload &hit2)
 Method to check the logical layer ordering of two hits.

Private Attributes

SpacePointPerLayerSorter m_spSorter {}
 Spacepoint sorter per logical measurement layer.
Config m_cfg
 Global Pattern Recognition configuration.
std::string m_nm
 Message source name.
boost::thread_specific_ptr< MsgStream > m_msg_tls
 MsgStream instance (a std::cout like with print-out levels).
std::atomic< IMessageSvc * > m_imsg { nullptr }
 MessageSvc pointer.
std::atomic< MSG::Level > m_lvl { MSG::NIL }
 Current logging level.
std::atomic_flag m_initialized ATLAS_THREAD_SAFE = ATOMIC_FLAG_INIT
 Messaging initialized (initMessaging).

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.

Definition at line 29 of file GlobalPatternFinder.h.

Member Typedef Documentation

◆ BucketPerContainer

Abrivation for a collection of space-point buckets grouped by their corresponding input container.

Definition at line 40 of file GlobalPatternFinder.h.

◆ LayerIndex

Type alias for the station layer index.

Definition at line 34 of file GlobalPatternFinder.h.

◆ PatternHitVisualInfo

◆ PatternHitVisualInfoVec

Abrivation for a vector of visual information objects.

Definition at line 44 of file GlobalPatternFinder.h.

◆ PatternStateVec

Definition at line 274 of file GlobalPatternFinder.h.

◆ PatternVec

Abrivation for a vector of global patterns.

Definition at line 38 of file GlobalPatternFinder.h.

◆ SearchTree_t

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

Definition of the search tree class.

Definition at line 164 of file GlobalPatternFinder.h.

◆ SpacePointContainerVec

Abrivation for a vector of space-point containers.

Definition at line 36 of file GlobalPatternFinder.h.

◆ StIndex

Type alias for the station index.

Definition at line 32 of file GlobalPatternFinder.h.

◆ TreeNode

using MuonR4::FastReco::GlobalPatternFinder::TreeNode = std::pair<SearchTree_t::coordinate_t, HitPayload>
private

Type alias for a tree node, formed by a hit payload and its indexing coordinates.

Definition at line 166 of file GlobalPatternFinder.h.

Member Enumeration Documentation

◆ CompatibilityResult

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

: Enum for the possible outcomes of the line compatibility test of one pattern against one test hit

Enumerator
eAddHit 

Test successfull, add the hit to the pattern.

eBranchPattern 

Test successfull with multiple pattern hits in the same logical measurement layer, branch the pattern.

eRejectHit 

Test failed, discard the hit.

Definition at line 303 of file GlobalPatternFinder.h.

303 : std::int8_t{
305 eAddHit = 0,
307 eBranchPattern = 1,
309 eRejectHit = -1,
310 };

◆ 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 371 of file GlobalPatternFinder.h.

371 : std::int8_t{
372 eSameLayer,
373 eLowerLayer,
374 eHigherLayer
375 };

◆ 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 168 of file GlobalPatternFinder.h.

168 : std::uint8_t{
170 eSector,
172 eTheta
173 };

Constructor & Destructor Documentation

◆ GlobalPatternFinder()

MuonR4::FastReco::GlobalPatternFinder::GlobalPatternFinder ( const std::string & name,
Config && config )

Standard constructor.

Parameters
nameName to be printed in the messaging
configConfiguration parameters

Definition at line 52 of file GlobalPatternFinder.cxx.

52 :
54 m_cfg{config} {
55 static_assert(std::is_move_assignable_v<PatternState>);
56 static_assert(std::is_move_constructible_v<PatternState>);
57 static_assert(std::is_copy_assignable_v<PatternState>);
58 static_assert(std::is_copy_constructible_v<PatternState>);
59 static_assert(std::is_nothrow_move_constructible_v<PatternState>);
60 static_assert(std::is_nothrow_move_assignable_v<PatternState>);
61};
AthMessaging(IMessageSvc *msgSvc, const std::string &name)
Constructor.
Config m_cfg
Global Pattern Recognition configuration.

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
: void

Struct to model the projection of the pattern line onto the phi strip in a certain station

Parameters
refLayReference layer coordinate (e.g. R or Z) increasing with the layer number
refStripReference strip coordinate (normal to the layer coordinate, along the strip direction)
invSlopeInverse slope, defined as deltaStrip / deltaLay

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

Definition at line 569 of file GlobalPatternFinder.cxx.

570 {
571 constexpr auto covIdxEta {Acts::toUnderlying(SpacePoint::CovIdx::etaCov)};
576 struct PhiStripProjectionModel {
577 StIndex station{};
578 double refLay{0.};
579 double refStrip{0.};
580 double invSlope{0.};
581 bool isBarrel{false};
582 bool isValid{false};
583
584 double project(const Amg::Vector3D& pos) const {
585 const double layCoord = isBarrel ? pos.perp() : pos.z();
586 return refStrip + (layCoord - refLay) * invSlope;
587 }
588 double residual(const Amg::Vector3D& pos) const {
589 const double stripCoord = isBarrel ? pos.z() : pos.perp();
590 return std::abs(project(pos) - stripCoord);
591 }
592 };
593 auto makeProjectionModel = [this](const PatternState& pat, const StIndex station) {
594 PhiStripProjectionModel result{};
595 result.station = station;
596 const std::vector<HitPayload>& hits {pat.hitsPerStation.at(station)};
597 if (hits.empty()) return result;
598 const bool isBarrel {hits.front().hit->msSector()->barrel()};
599 // Define the coordinate across the layers, i.e. orthogonal to the strip
600 const auto layCoord = [isBarrel](const HitPayload& hit) {
601 return (isBarrel) ? hit.R : hit.Z;
602 };
603 // Define the coordinate along the strip, i.e. orthogonal to the measureent layers
604 const auto stripCoord = [isBarrel](const HitPayload& hit) {
605 return (isBarrel) ? hit.Z : hit.R;
606 };
607
608 const HitPayload* sp1 {nullptr};
609 const HitPayload* sp2 {nullptr};
610 if (hits.size() > 1) {
611 // if we have two eta hits in the station, we use the furthestmost to define the pattern line
612 for (const HitPayload& hit : hits) {
613 if (!hit->measuresEta()) continue;
614 if (!sp1 || layCoord(hit) < layCoord(*sp1)) {
615 sp1 = &hit;
616 }
617 if (!sp2 || layCoord(hit) > layCoord(*sp2)) {
618 sp2 = &hit;
619 }
620 }
621 } else {
622 // if we have only one eta hit, we use it and look for the closest eta hit in the closest station to define the pattern line
623 sp1 = &hits.front();
624 auto layDistanceFromSp1 = [&layCoord,&sp1](const HitPayload& hit) {
625 return std::abs(layCoord(hit) - layCoord(*sp1));
626 };
627 const std::vector<HitPayload>& closestSt {std::ranges::min_element(pat.hitsPerStation,
628 [&layDistanceFromSp1, &station](const auto& st1, const auto& st2){
629 if (st1.first == station) return false;
630 if (st2.first == station) return true;
631 return layDistanceFromSp1(st1.second.front()) < layDistanceFromSp1(st2.second.front());
632 })->second};
633 for (const HitPayload& hit : closestSt) {
634 if (!hit->measuresEta()) continue;
635 if (!sp2 || layDistanceFromSp1(hit) < layDistanceFromSp1(*sp2)) {
636 sp2 = &hit;
637 }
638 }
639 }
640 if (!sp1 || !sp2 ) return result;
641 const double deltaLay {layCoord(*sp2) - layCoord(*sp1)};
642 if (std::abs(deltaLay) < m_cfg.minLayerSeparation) return result;
643
644 result.refLay = layCoord(*sp1);
645 result.refStrip = stripCoord(*sp1);
646 result.invSlope = (stripCoord(*sp2) - stripCoord(*sp1)) / deltaLay;
647 result.isBarrel = isBarrel;
648 result.isValid = true;
649 return result;
650 };
651
652 PatternStateVec survivingPatterns{};
653 survivingPatterns.reserve(patterns.size());
654 for (PatternState& pat : patterns) {
656 ATH_MSG_VERBOSE(__func__<<"() Search for phi-only hits for pattern: " << pat);
657 // Projection model of pattern line onto a given phi strip
658 std::optional<PhiStripProjectionModel> patProjOnStrip{};
659 for (const auto& [spc, bucketVec] : pat.bucketsPerContainer) {
660 for (const SpacePointBucket* bucket : bucketVec) {
661 const Amg::Transform3D& localToGlobal {bucket->msSector()->localToGlobalTransform(gctx)};
662 const StIndex station {m_cfg.idHelperSvc->stationIndex(bucket->front()->identify())};
663 // If the projection model is not valid, we will use the pattern theta. We cache the local Y in glob frame
664 const Amg::Vector3D locY {localToGlobal.linear() * Amg::Vector3D::UnitY()};
665
666 for (const auto& hit : *bucket) {
667 // We are looking for phi-only hits
668 if (hit->measuresEta()){
669 continue;
670 }
671 ATH_MSG_VERBOSE(__func__<<"() *** Test phi-only hit "<<*hit);
672 // Check phi compatibility
673 const Amg::Vector3D locPosTest {hit->localPosition()};
674 const Amg::Vector3D globPosTest {localToGlobal * locPosTest};
675 const double globPhi {globPosTest.phi()};
676 if (!isPhiCompatible(globPhi, pat)) {
677 ATH_MSG_VERBOSE(__func__<<"() Phi-only hit not compatible");
678 continue;
679 }
680 // Check there are not other phi hits in the same layer
681 const uint8_t layNum = m_spSorter.sectorLayerNum(*hit);
682 if (auto it {pat.hitsPerStation.find(station)}; it != pat.hitsPerStation.end()) {
683 if (std::ranges::any_of(it->second, [&](const HitPayload& h){
684 return h->measuresPhi() && hit->msSector() == h->msSector() && layNum == h.layerNum; })) {
685 ATH_MSG_VERBOSE(__func__<<"() The pattern already has a phi hit in the same layer - skip this test hit.");
686 continue;
687 }
688 }
689 // Check eta compatibility. Try first to use the pattern line projection model
690 bool isEtaCompatible {false};
691 const double sigmaEta {std::sqrt(hit->covariance()[covIdxEta])};
692 if (!patProjOnStrip.has_value() || patProjOnStrip->station != station) {
693 patProjOnStrip = makeProjectionModel(pat, station);
694 }
695 if(patProjOnStrip->isValid) {
696 isEtaCompatible = patProjOnStrip->residual(globPosTest) <= 1.1*sigmaEta;
697 ATH_MSG_VERBOSE(__func__<<"() Distance pattern line from strip center: "<<patProjOnStrip->residual(globPosTest)<<", strip half-length: "<<sigmaEta<<", isCompatible: "<<isEtaCompatible);
698 } else {
699 // Check pattern theta against global theta at the boundaries of the phi strip
700 double thetaMin {(globPosTest - sigmaEta * locY).theta()};
701 double thetaMax {(globPosTest + sigmaEta * locY).theta()};
702 if (thetaMax < thetaMin) {
703 std::swap(thetaMin, thetaMax);
704 }
705 isEtaCompatible = std::max(pat.theta - thetaMax, thetaMin - pat.theta) < 0.5 * m_cfg.thetaSearchWindow;
706 ATH_MSG_VERBOSE(__func__<<"() Pattern theta "<<inDegrees(pat.theta)<<", strip theta window: ["<<inDegrees(thetaMin)<<", "<<inDegrees(thetaMax)<<"]");
707
708 }
709 if (!isEtaCompatible) {
710 ATH_MSG_VERBOSE(__func__<<"() The pattern falls outside the test hit strip in eta - skip this test hit.");
711 continue;
712 }
713 // Create the hit payload and add the hit to the pattern
714 ATH_MSG_VERBOSE(__func__<<"() Phi-only hit compatible - add it to the pattern.");
715 pat.addHit(HitPayload{hit.get(), station, layNum, globPhi}, 0., 0.);
716 }
717 }
718 }
719 if (pat.nPhiHits < m_cfg.minPhiHits) {
720 ATH_MSG_VERBOSE(__func__<<"() Pattern "<< pat<<" has only "<<pat.nPhiHits<<" phi hits, below the minimum required - reject this pattern.");
721 continue;
722 }
723 pat.finalizePatternPhi();
724 survivingPatterns.push_back(std::move(pat));
725 }
726 std::swap(patterns, survivingPatterns);
727}
Scalar theta() const
theta method
#define ATH_MSG_VERBOSE(x)
T_ResultType project(ParameterMapping::type< N > parameter_map, const T_Matrix &matrix)
bool isPhiCompatible(const double testPhi, const PatternState &pattern) const
Method to check the phi compatibility of a test hit with a given pattern.
Muon::MuonStationIndex::StIndex StIndex
Type alias for the station index.
std::vector< PatternState > PatternStateVec
SpacePointPerLayerSorter m_spSorter
Spacepoint sorter per logical measurement layer.
std::vector< std::string > patterns
Definition listroot.cxx:187
Eigen::Affine3d Transform3D
Eigen::Matrix< double, 3, 1 > Vector3D
StIndex
enum to classify the different station layers in the muon spectrometer
bool isBarrel(const ChIndex index)
Returns true if the chamber index points to a barrel chamber.
@ locY
local cartesian
Definition ParamDefs.h:38
void swap(ElementLinkVector< DOBJ > &lhs, ElementLinkVector< DOBJ > &rhs)
Hit information stored during pattern building.
Pattern state object storing pattern information during construction.

◆ addVisualInfo()

void MuonR4::FastReco::GlobalPatternFinder::addVisualInfo ( const PatternState & candidate,
PatternHitVisualInfo::PatternStatus status,
PatternHitVisualInfoVec * 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 1044 of file GlobalPatternFinder.cxx.

1046 {
1047 if (!visualInfo) {
1048 return;
1049 }
1050 GlobalPattern pattern {convertToPattern(cache)};
1051 // Check whether the visual info about this pattern is already in the container
1052 if (auto it =std::ranges::find_if(*visualInfo, [&pattern](const auto& v){
1053 return v.patternCopy && *v.patternCopy == pattern; }); it != visualInfo->end()) {
1054 it->status = status; // Update the status if the pattern is already in the container
1055 return;
1056 }
1057 visualInfo->push_back(*cache.visualInfo);
1058 auto& bucketVec {visualInfo->back().parentBuckets};
1059 for (const auto& [_, buckets] : cache.bucketsPerContainer) {
1060 bucketVec.insert(bucketVec.end(), buckets.begin(), buckets.end());
1061 }
1062 visualInfo->back().patternCopy = std::make_unique<GlobalPattern>(std::move(pattern));
1063 visualInfo->back().status = status;
1064}
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

◆ 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 845 of file GlobalPatternFinder.cxx.

846 {
847 auto getLayerOrdering = [](const bool isLayer1Lower) {
848 return isLayer1Lower ? eLowerLayer : eHigherLayer;
849 };
850 if (hit1 == hit2) {
851 return eSameLayer;
852 }
854 if (hit1->msSector() == hit2->msSector()) {
855 if (hit1.layerNum == hit2.layerNum) {
856 return eSameLayer;
857 } else {
858 return getLayerOrdering(hit1.layerNum < hit2.layerNum);
859 }
860 }
861 StIndex st1 {hit1.station};
862 StIndex st2 {hit2.station};
864 if (st1 == st2) {
865 return getLayerOrdering(hit1->msSector()->barrel() ? hit1.R < hit2.R : std::abs(hit1.Z) < std::abs(hit2.Z));
866 }
867 LayerIndex layer1 {toLayerIndex(st1)};
868 LayerIndex layer2 {toLayerIndex(st2)};
869 if (layer1 == layer2) {
871 if (layer1 == LayerIndex::Middle) {
873 return getLayerOrdering(st1 == StIndex::BM);
874 } else if (layer1 == LayerIndex::Inner) {
876 return getLayerOrdering(hit1.R < hit2.R);
877 } else {
878 throw std::runtime_error("Unexpected to have two pattern-compatible hits one in BO and the other in EO.");
879 }
880 }
881 if (layer1 == LayerIndex::Inner || layer2 == LayerIndex::Inner) {
883 return getLayerOrdering(layer1 == LayerIndex::Inner);
884 }
885 if (layer1 == LayerIndex::Outer || layer2 == LayerIndex::Outer) {
887 return getLayerOrdering(layer2 == LayerIndex::Outer);
888 }
889 if (layer1 == LayerIndex::BarrelExtended || layer2 == LayerIndex::BarrelExtended) {
891 return getLayerOrdering(layer1 == LayerIndex::BarrelExtended);
892 }
894 if (layer1 == LayerIndex::Extended) {
895 return getLayerOrdering(st2 == StIndex::EM);
896 }
897 return getLayerOrdering(st1 == StIndex::BM);
898}
Muon::MuonStationIndex::LayerIndex LayerIndex
Type alias for the station layer index.
LayerIndex toLayerIndex(ChIndex index)
convert ChIndex into LayerIndex

◆ checkLineCompatibility()

GlobalPatternFinder::LineCompatibilityResult MuonR4::FastReco::GlobalPatternFinder::checkLineCompatibility ( const HitPayload & seed,
const HitPayload & test,
const PatternState & pat ) const
private

Method to check the line compatibility of a test hit with a given pattern.

Parameters
seedseed hit information
testtest hit information
patternpattern to be extended
Returns
: result of the test, including the computed line residual and acceptance window

Safety check that the test hit is not on the same layer as the seed

Here we can retrieve the beamspot if desired

Helper function to make the result

Parameters
patHitIdxPattern hit index to use to compute the pattern line
isNewLayerFlag indicating if the test hit is in a new layer
Returns
: Line compatibility result

Fetch the last inserted hit

Test hit coincides with the last inserted hit

We add the test hit to the pattern if they are consecutive MDT hits

sTGCs: if the test hit is a trigger hit (Pad) and the last inserted is precision (strip), we keep the precision hit

If they are not consecutive MDT hits && we have no inserted hits yet (last hit = seed), we give priority to the seed and reject the test hit

Find first previously inserted hit on a different layer from test, starting counting from the second-to-last hit

Definition at line 337 of file GlobalPatternFinder.cxx.

339 {
340 // We test hits in the same **expanded** sector, so we need just to compare hit's phi with the pattern's phi, if both available
341 if (test->measuresPhi() && pat.nPhiHits &&
342 std::abs(CxxUtils::deltaPhi(pat.phi, test.phi)) > m_cfg.phiTolerance) {
343 ATH_MSG_VERBOSE(__func__<<"() The pattern with phi = "<<inDegrees(pat.phi)
344 <<" is not compatible with the test hit with phi "<<inDegrees(test.phi));
346 }
348 if (checkLayerOrdering(test, seed) == eSameLayer) {
349 ATH_MSG_VERBOSE(__func__<<"() Test hit is on the same layer as the seed - reject.");
351 }
352
354 const Amg::Vector3D beamSpot{Amg::Vector3D::Zero()};
355
360 auto makeResult = [&](const uint8_t patHitIdx,
361 const bool isNewLayer) -> LineCompatibilityResult {
362 LineCompatibilityResult res {computeResidual(seed, test, pat, patHitIdx, beamSpot)};
363 if (res.residual < res.accWindow) {
365 }
366 return res;
367 };
368
370 const HitPayload& lastPatHit {pat.getNthLastHit(1u)};
371 const int nMeas {pat.nBendingTriggerHits + pat.nPrecisionHits};
372
373 /*************** Test hit is on a new layer — draw line from seed to lastHit */
374 if(checkLayerOrdering(test, lastPatHit) != eSameLayer) {
375 return makeResult(1u, /*isNewLayer*/ true);
376 }
377 /*************** Test hit is on the same layer as the last inserted hit ***************/
379 if (test == lastPatHit) {
380 ATH_MSG_VERBOSE(__func__<<"() Test hit is the same as last inserted hit - rejecting.");
382 }
384 if (areConsecutiveMdt(*test, *lastPatHit)) {
385 ATH_MSG_VERBOSE(__func__<<"() Consecutive MDT hits on the same layer - accepting.");
386 return LineCompatibilityResult{CompatibilityResult::eAddHit, pat.lastResidual, pat.lastAcceptWindow};
387 }
389 if (!test.isPrecision && lastPatHit.isPrecision) {
390 ATH_MSG_VERBOSE(__func__<<"() Test hit is a trigger hit and last inserted hit is precision on the same layer - keep the precision hit.");
392 }
394 if (nMeas == 1) {
395 ATH_MSG_VERBOSE(__func__<<"() Test hit on same layer as seed with no prior hits, and they are not consecutive MDT hits - rejecting.");
397 }
399 uint8_t n {2u};
400 while (n < nMeas &&
401 checkLayerOrdering(test, pat.getNthLastHit(n)) == eSameLayer) {
402 ++n;
403 }
404 return makeResult(n, /*isNewLayer*/ false);
405}
std::pair< std::vector< unsigned int >, bool > res
@ eBranchPattern
Test successfull with multiple pattern hits in the same logical measurement layer,...
@ eAddHit
Test successfull, add the hit to the pattern.
static LayerOrdering checkLayerOrdering(const HitPayload &hit1, const HitPayload &hit2)
Method to check the logical layer ordering of two hits.
LineCompatibilityResult computeResidual(const HitPayload &seed, const HitPayload &test, const PatternState &pat, const uint8_t patHitIdx, const Amg::Vector3D &beamSpot) const
Helper method to compute the residual of a test hit against a reference pattern hit and check whether...
T deltaPhi(T phiA, T phiB)
Return difference phiA - phiB in range [-pi, pi].
Definition phihelper.h:42
AthConfigFlags beamSpot(AthConfigFlags flags, str instanceName, str recoMode)
: Small struct to encapsulate the checkLineCompatibility result

◆ computeResidual()

GlobalPatternFinder::LineCompatibilityResult MuonR4::FastReco::GlobalPatternFinder::computeResidual ( const HitPayload & seed,
const HitPayload & test,
const PatternState & pat,
const uint8_t patHitIdx,
const Amg::Vector3D & beamSpot ) const
private

Helper method to compute the residual of a test hit against a reference pattern hit and check whether it is within the acceptance window.

The residual is defined against the line passing through seed and the reference pattern hit

Parameters
seedseed hit information
testtest hit information
patternpattern to be extended
patHitIdxIndex of the pattern hit to use for residual computation
beamSpotNeeded to draw the pattern line when the provided pattern hit is too close to the seed
Returns
: result of the test, including the computed line residual and acceptance window

The dynamic acceptance window is defined using the error propagation law for the residual. We have two contributions: the line extrapolation distance and the uncertainty on the line slope

Alpha parameter determining the propagation/ line extrapolation magnitude

Loosen the window when using the beamspot as reference, as the line slope is less well defined

Apply LR correction if the pat hit is a straw

Definition at line 407 of file GlobalPatternFinder.cxx.

411 {
412 const HitPayload& patHit {pat.getNthLastHit(patHitIdx)};
413 const int nMeas {pat.nBendingTriggerHits + pat.nPrecisionHits};
414
415 ATH_MSG_VERBOSE(__func__<<"() Start residual check given pat hit: " << *patHit << ", st: " << stName(patHit.station));
416 // Check whether we have to use the beamspot instead of the last pattern hit to draw the line with the seed.
417 const double patDeltaZ {std::abs(patHit.Z - seed.Z)};
418 const bool useBeamspot {patHit == seed || patDeltaZ < Acts::s_epsilon ||
419 (patHit.station == seed.station && (patHit->msSector()->barrel() ? std::abs(patHit.R - seed.R) : patDeltaZ) <= m_cfg.minLayerSeparation)};
420
421 if (useBeamspot && patHitIdx + 1 < nMeas) {
422 ATH_MSG_VERBOSE(__func__<<"() Distance seed to pat hit is very small - try with next pat hit as reference.");
423 return computeResidual(seed, test, pat, patHitIdx + 1u, beamSpot);
424 }
425
426 /* If the pat hit is a straw, find the consecutive (MDT) hits on the same layer and return use middle one
427 for next computations — gives a more central reference for the line direction */
428 const HitPayload& centralPatHit { patHit->isStraw() ? [&]() -> const HitPayload& {
429 uint8_t nSameLayer{1u};
430 while (patHitIdx + nSameLayer < nMeas &&
431 checkLayerOrdering(patHit, pat.getNthLastHit(patHitIdx + nSameLayer)) == eSameLayer) {
432 ++nSameLayer;
433 }
434 return nSameLayer > 2u ? pat.getNthLastHit(patHitIdx + (nSameLayer - 1u) / 2u) : patHit;
435 }() : patHit};
436
437 ATH_MSG_VERBOSE(__func__<<"() Distance seed to pat hit - deltaR_pat= " << (centralPatHit.R - seed.R) << ", deltaZ_pat= " << (centralPatHit.Z - seed.Z) << ". Use beamspot: " << useBeamspot);
439
440 // Determine the coordinates to use as reference for the line computation
441 const double refR {useBeamspot ? beamSpot.perp() : centralPatHit.R};
442 const double refZ {useBeamspot ? beamSpot.z() : centralPatHit.Z};
443
444 // Compute the line slope.
445 const double dZ_slope {refZ - seed.Z};
446 const double dR_slope {refR - seed.R};
447 assert(dZ_slope != 0);
448 const double lineSlope {dR_slope / dZ_slope};
449 ATH_MSG_VERBOSE(__func__<<"() Slope computation - deltaR_slope= " << dR_slope << ", deltaZ_slope= " << dZ_slope << ", slope= " << lineSlope);
450
451 // Compute the residual
452 const double dZ_res {test.Z - seed.Z};
453 res.residual = (test.R - seed.R) - lineSlope * dZ_res;
454 ATH_MSG_VERBOSE(__func__<<"() Residual computation - deltaR_res: " << (test.R - seed.R) << ", deltaZ_res: " << dZ_res << ", signed residual: " << res.residual);
455
458 const double geometricalFactor {1.+ Acts::square(lineSlope)};
459 const double alpha {dZ_res / dZ_slope};
460 const double propagationFactor {1. - alpha + Acts::square(alpha)};
461 res.accWindow = m_cfg.baseRWindow * std::sqrt(2*geometricalFactor * propagationFactor);
462 if (useBeamspot) res.accWindow *= 1.5;
463 ATH_MSG_VERBOSE(__func__<<"() Window computation - Geometrical Factor: " << std::sqrt(geometricalFactor) << " alpha: " << alpha
464 << ", Propagation Factor: " << std::sqrt(propagationFactor) << ", Computed Window: " << res.accWindow);
465
467 if (!useBeamspot && centralPatHit->isStraw()) {
468 const double LRcorrection {dZ_res * geometricalFactor * centralPatHit->driftRadius()/ Acts::fastHypot(dZ_slope, dR_slope)};
469 res.residual = std::min(std::abs(res.residual-LRcorrection), std::abs(res.residual+LRcorrection));
470 ATH_MSG_VERBOSE(__func__<<"() Apply LR correction: " << LRcorrection << ", corrected residual: " << res.residual);
471 } else {
472 res.residual = std::abs(res.residual);
473 }
474 if (pat.visualInfo) {
475 pat.visualInfo->hitLineInfo[test.hit] = std::make_pair(lineSlope, res.accWindow);
476 }
477 return res;
478}
const std::string & stName(StIndex index)
convert StIndex into a string

◆ constructTree()

GlobalPatternFinder::SearchTree_t MuonR4::FastReco::GlobalPatternFinder::constructTree ( const ActsTrk::GeometryContext & gctx,
const SpacePointContainerVec & spacepoints ) const
private

Method to construct the search tree by filling it up with spacepoints from the given containers.

The tree does not contain only-phi hits.

Parameters
gctxGeometry context
spacepointsVector of space point containers
Returns
: Constructed search tree

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

Blow-up the number of sectors by a factor of 2. The even numbers represent the segments expressed @ the sector centre. The odd numbers represent the overlap region between two adjacent sectors. For sector 16, the right overlap region is mapped to 1

Definition at line 782 of file GlobalPatternFinder.cxx.

783 {
784 SearchTree_t::vector_t rawData{};
786 // Before the loops: estimate the total number of hits
787 size_t totalHits = 0;
788 for (const SpacePointContainer* spc : spacepoints) {
789 for (const SpacePointBucket* bucket : *spc) {
790 totalHits += bucket->size();
791 }
792 }
793 // We can have up to 3 entries per hit (when the hit does not measure phi).
794 rawData.reserve(3 * totalHits);
795
796 for (const SpacePointContainer* spc : spacepoints) {
797 ATH_MSG_VERBOSE("Adding to the search tree "<<spc->size()<<" space point buckets");
798 for (const SpacePointBucket* bucket : *spc) {
799 ATH_MSG_VERBOSE("Processing " << bucket->size() << " spacepoint...");
800 const Amg::Transform3D& localToGlobal {bucket->msSector()->localToGlobalTransform(gctx)};
801 const StIndex bucketStation {m_cfg.idHelperSvc->stationIndex(bucket->front()->identify())};
802 const int sector = bucket->msSector()->sector();
803
804 for (const auto& hit : *bucket) {
805 // Ignore only-phi hits and MDT hits if desired
806 if (!hit->measuresEta() || (!m_cfg.useMdtHits && hit->isStraw())) {
807 continue;
808 }
809 const Amg::Vector3D globalPos {localToGlobal * hit->localPosition()};
810 const double globalTheta {globalPos.theta()};
811 const HitPayload newHit {hit.get(), bucket, spc, bucketStation,
812 static_cast<uint8_t>(m_spSorter.sectorLayerNum(*hit)),globalPos.perp(), globalPos.z(), globalPos.phi()};
813
816 for (const auto proj : {SectorProjector::leftOverlap, SectorProjector::center, SectorProjector::rightOverlap}) {
818 const int projSector = MsTrackSeeder::ringSector(sector + Acts::toUnderlying(proj));
819 if (hit->measuresPhi() && proj != SectorProjector::center &&
820 !sectorMap.insideSector(projSector, newHit.phi)) {
821 ATH_MSG_VERBOSE(__func__<<"() Hit @"<< *hit<<"\nwith globPhi "<<inDegrees(newHit.phi)
822 <<" is not in sector "<<projSector<<" ["<<inDegrees(sectorMap.sectorPhi(projSector)-sectorMap.sectorWidth(projSector))
823 <<", "<<inDegrees(sectorMap.sectorPhi(projSector)+sectorMap.sectorWidth(projSector))
824 <<"] which is "<<MsTrackSeeder::to_string(proj) <<" to "<< sector);
825 continue;
826 }
827 std::array<double, 2> coords{};
828 coords[Acts::toUnderlying(SeedCoords::eTheta)] = globalTheta;
829
833 coords[Acts::toUnderlying(SeedCoords::eSector)] = MsTrackSeeder::ringOverlap(2*sector + Acts::toUnderlying(proj));
834 ATH_MSG_VERBOSE(__func__<<"() Add hit @"<< *hit
835 <<"\nwith global position "<<Amg::toString(globalPos) <<" and coordinates "<<coords<<" to the search tree");
836 rawData.emplace_back(std::move(coords), newHit);
837 }
838 }
839 }
840 }
841 ATH_MSG_VERBOSE("Create a new tree with "<<rawData.size()<<" entries. ");
842 return SearchTree_t{std::move(rawData)};
843}
@ eSector
Expanded sector coordinate of the associated spectrometer sector
Acts::KDTree< 2, HitPayload, double, std::array, 5 > SearchTree_t
Definition of the search tree class.
static std::string to_string(const SectorProjector proj)
Print the sector projector.
static int ringSector(const int sector)
Ensure that the parsed sector number is following the MS sector schema 0 is mapped to 16 and 17 is ma...
static int ringOverlap(const int sector)
Maps the sector 33 -> 0 to close the extended MS symmetry ring.
SectorProjector
Enumeration to select the sector projection.
std::string toString(const Translation3D &translation, int precision=4)
GeoPrimitvesToStringConverter.
MsTrackSeeder::SectorProjector SectorProjector
DataVector< SpacePointBucket > SpacePointContainer
Abrivation of the space point container type.

◆ 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

Definition at line 748 of file GlobalPatternFinder.cxx.

748 {
749 GlobalPattern::HitCollection hitPerStation{};
750 for (const auto& [station, hits] : cache.hitsPerStation) {
751 auto& out = hitPerStation[station];
752 out.reserve(hits.size());
753 std::ranges::transform(hits, std::back_inserter(out), [](const HitPayload& h){ return h.hit;});
754 }
755 GlobalPattern pattern{std::move(hitPerStation)};
756 pattern.setTheta(cache.theta);
757 pattern.setPhi(cache.phi);
758 // Set the pattern sector(s) and theta.
759 pattern.setSector(cache.sector1);
760 pattern.setSecondarySector(cache.sector2);
761 // Set pattern quality information.
762 pattern.setNPrecisionHits(cache.nPrecisionHits);
763 pattern.setNEtaNonPrecisionHits(cache.nBendingTriggerHits);
764 pattern.setNPhiHits(cache.nPhiHits);
765 pattern.setMeanNormResidual2(cache.meanNormResidual2);
766 return pattern;
767}
std::unordered_map< StIndex, std::vector< HitType > > HitCollection

◆ convertToPattern() [2/2]

GlobalPatternFinder::PatternVec 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 770 of file GlobalPatternFinder.cxx.

770 {
772 patterns.reserve(cache.size());
773 std::transform(cache.begin(), cache.end(), std::back_inserter(patterns), [this](const PatternState& cacheEntry) {
774 GlobalPattern pattern {convertToPattern(cacheEntry)};
775 ATH_MSG_VERBOSE("convertToPattern() Converted new pattern "<<pattern);
776 return pattern;
777 });
778 return patterns;
779}
std::vector< GlobalPattern > PatternVec
Abrivation for a vector of global patterns.

◆ extendPatterns()

void MuonR4::FastReco::GlobalPatternFinder::extendPatterns ( PatternStateVec & activePatterns,
const HitPayload & test,
const HitPayload & seed,
const HitPayload & prevCandidate,
PatternHitVisualInfoVec * visualInfo = nullptr ) const
private

Function testing 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
activePatternsVector of active patterns to be extended
testHit to be tested
seedSeed hit information
prevCandidatePrevious candidate hit information
visualInfoPointer to visual information for pattern visualization (nullptr if the VisualizationTool is disabled)

Check angular compatibility of the test hit and the pattern

Add the test hit to the pattern

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

Update visual information of the original pattern

Add the new pattern to the list of next patterns

Definition at line 240 of file GlobalPatternFinder.cxx.

244 {
245 // Filter patterns: deduplicate groups sharing the same last hit, but only when the next
246 // test hit is on a different layer — if it's on the same layer we might branch again and need both
247 if (activePatterns.size() > 1) {
248 // Sort pointers to avoid moving pattern states during deduplication
249 std::vector<PatternState*> patPtrs(activePatterns.size());
250 std::ranges::transform(activePatterns, patPtrs.begin(), [](PatternState& p){ return &p; });
251 // Sort patteres by last inserted hit to find groups of patterns sharing the same last hit
252 std::ranges::sort(patPtrs, {}, [](const PatternState* p){ return p->lastInsertedHit; });
253
254 PatternStateVec deduplicated{};
255 deduplicated.reserve(activePatterns.size());
256 for (auto it = patPtrs.begin(); it != patPtrs.end(); ) {
257 // Find the group of patterns sharing the same last hit
258 const SpacePoint* groupLastHit {(*it)->lastInsertedHit};
259 auto groupEnd = std::ranges::find_if(it, patPtrs.end(), [&](const PatternState* p) {
260 return p->lastInsertedHit != groupLastHit;
261 });
262 if (checkLayerOrdering(test, (*it)->getNthLastHit(1u)) != eSameLayer) {
263 // If last hit of the group is on a different layer than the test hit, we deduplicate and pick the best pattern in the group
264 deduplicated.push_back(std::move(**std::ranges::max_element(it, groupEnd,
265 [this](const PatternState* a, const PatternState* b){ return isBetter(*b, *a); })));
266 } else {
267 // If the last hit of the group is on the same layer as the test hit, we keep all patterns in the group, as we might branch again with the next hits.
268 std::ranges::transform(it, groupEnd, std::back_inserter(deduplicated),
269 [](PatternState* p){ return std::move(*p); });
270 }
271 it = groupEnd;
272 }
273 std::swap(activePatterns, deduplicated);
274 }
275 PatternStateVec nextPatterns{};
276 nextPatterns.reserve(activePatterns.size()* 2);
277
278 const unsigned refMissedLayerHits {std::ranges::min_element(activePatterns,
279 [](const auto& a, const auto& b) { return a.nMissedLayerHits < b.nMissedLayerHits; }
280 )->nMissedLayerHits};
281
282 for (PatternState& pattern : activePatterns) {
283
284 if (pattern.nMissedLayerHits >= m_cfg.maxMissedLayerHits && pattern.nMissedLayerHits > refMissedLayerHits) {
285 ATH_MSG_VERBOSE(__func__<<"() Pattern " << pattern << " has missed " << pattern.nMissedLayerHits << " hits in different layers, above the maximum allowed - abort this pattern.");
287 continue;
288 }
289
291 const auto [result, residual, accWindow] {checkLineCompatibility(seed, test, pattern)};
292 switch (result) {
295 pattern.addHit(test, residual, accWindow);
296 ATH_MSG_VERBOSE(__func__<<"() Hit is compatible, add it to the pattern. Updated pattern: " << pattern);
297 break;
298 }
300 /* Check first if the branched pattern already exists*/
301 if (std::ranges::any_of(nextPatterns, [&test, &pattern](const PatternState& p) {
302 return p.lastInsertedHit == test.hit && p.prevLayerHit == pattern.prevLayerHit; })) {
303 ATH_MSG_VERBOSE(__func__<<"() Hit is compatible & on same layer of last added hit, but branched pattern already exists");
304 break;
305 }
307 PatternState newPattern {pattern};
308 const HitPayload& lastPatHit {pattern.getNthLastHit(1u)};
309 newPattern.overWriteHit(lastPatHit, test, residual, accWindow);
310 ATH_MSG_VERBOSE(__func__<<"() Hit is compatible & on same layer of last added hit - new branched pattern: " << newPattern);
311
313 if (newPattern.visualInfo && pattern.visualInfo) {
314 pattern.visualInfo->replacedHits.push_back(test.hit);
315 newPattern.visualInfo->replacedHits.push_back(lastPatHit.hit);
316 }
318 nextPatterns.push_back(std::move(newPattern));
319 break;
320 }
322 ATH_MSG_VERBOSE(__func__<<"() Hit is not compatible with the pattern.");
323 if (pattern.visualInfo) {
324 pattern.visualInfo->discardedHits.push_back(test.hit);
325 }
326 if (checkLayerOrdering(test, prevCandidate) != eSameLayer){
327 pattern.nMissedLayerHits++;
328 }
329 break;
330 }
331 }
332 nextPatterns.push_back(std::move(pattern));
333 }
334 std::swap(activePatterns, nextPatterns);
335};
static Double_t a
void addVisualInfo(const PatternState &candidate, PatternHitVisualInfo::PatternStatus status, PatternHitVisualInfoVec *visualInfo) const
Helper function to add visual information of a given pattern (which is usually going to be destroyed)...
bool isBetter(const PatternState &a, const PatternState &b) const
Operator to compare two patterns.
LineCompatibilityResult checkLineCompatibility(const HitPayload &seed, const HitPayload &test, const PatternState &pat) const
Method to check the line compatibility of a test hit with a given pattern.

◆ findPatterns()

GlobalPatternFinder::PatternVec MuonR4::FastReco::GlobalPatternFinder::findPatterns ( const ActsTrk::GeometryContext & gctx,
const SpacePointContainerVec & spacepoints,
BucketPerContainer & outBuckets ) 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
outBucketsoutput collection of space-point buckets grouped by their corresponding input container to be written into StoreGate.
Returns
: Vector of found patterns

Create the search tree by ordering hits in theta and expanded spectrometer sector and find patterns in eta

Add phi-only hits to the patterns

Fill the output buckets & visual info

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

Plot patterns

Definition at line 65 of file GlobalPatternFinder.cxx.

67 {
69 auto visualInfo {m_cfg.visionTool ? std::make_unique<PatternHitVisualInfoVec>() : nullptr};
70 PatternStateVec patterns{findPatternsInEta(constructTree(gctx, spacepoints), visualInfo.get())};
71
74
76 for (const PatternState& pat : patterns) {
79
80 for (const auto& [spc, bucketVec] : pat.bucketsPerContainer) {
81 if (outBuckets.find(spc) == outBuckets.end()) {
82 throw std::runtime_error("The space point container associated to the pattern is not present in the output bucket map.");
83 }
84 for (const SpacePointBucket* bucket : bucketVec) {
85 auto& outBucketVec = outBuckets[spc];
86 if (std::ranges::find(outBucketVec, bucket) == outBucketVec.end()) {
87 outBucketVec.push_back(bucket);
88 }
89 }
90 }
91 }
93 if (visualInfo) {
94 m_cfg.visionTool->plotPatternBuckets(Gaudi::Hive::currentContext(), "GlobPatFind_", std::move(*visualInfo));
95 }
97}
SearchTree_t constructTree(const ActsTrk::GeometryContext &gctx, const SpacePointContainerVec &spacepoints) const
Method to construct the search tree by filling it up with spacepoints from the given containers.
void addPhiOnlyHits(const ActsTrk::GeometryContext &gctx, PatternStateVec &patterns) const
Method to add phi-only measurements to existing PatternStates.
PatternStateVec findPatternsInEta(const SearchTree_t &orderedSpacepoints, PatternHitVisualInfoVec *visualInfo=nullptr) const
Method steering the building of patterns in eta.

◆ findPatternsInEta()

GlobalPatternFinder::PatternStateVec MuonR4::FastReco::GlobalPatternFinder::findPatternsInEta ( const SearchTree_t & orderedSpacepoints,
PatternHitVisualInfoVec * visualInfo = nullptr ) const
private

Method steering the building of patterns in eta.

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

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

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

check how many existing (and overlapping) 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 one hit is precision and one trigger, put first the trigger one. Relevant for STGCs

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

Initialize a pattern from the seed hit

Helper function to process a new candidate hit. During pattern building, we can have pattern branching when the initial pattern is compatible with multiple hits in the same layer. These patterns will be stored in activePatterns. For each new hit, extendPatterns
will try to extend every active pattern and remove the ones not meeting continuation criteria.

Parameters
testPairThe new candidate hit to process.

Helper function ensuring we have at the end one pattern for seed hit. If multiple survived at the end, we keep the best.

First search for compatible hits from the seed layer onwards

Then try to proceed toward the innermost layer

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

Definition at line 100 of file GlobalPatternFinder.cxx.

101 {
102 constexpr auto thetaIdx {Acts::toUnderlying(SeedCoords::eTheta)};
103 constexpr auto sectorIdx {Acts::toUnderlying(SeedCoords::eSector)};
105 using enum SeedCoords;
106 for (const auto seedingLayer : m_cfg.layerSeedings) {
107 ATH_MSG_VERBOSE(__func__<<"() Start pattern search in eta with seeding layer "<< layerName(seedingLayer));
109 for (const auto& [seedCoords, seed] : orderedSpacepoints) {
111 const LayerIndex seedLayer {toLayerIndex(seed.station)};
112 if (seedLayer != seedingLayer || (seed->isStraw() && !m_cfg.seedFromMdt)) {
113 continue;
114 }
115 ATH_MSG_VERBOSE(__func__<<"() New seed hit "<<*seed<<", coordinates "<<seedCoords);
117 auto nExistingPatterns {std::ranges::count_if(patterns, [&seed, &seedCoords, this](const PatternState& pattern){
118 if (std::abs(pattern.theta - seedCoords[thetaIdx]) > m_cfg.thetaSearchWindow ||
119 pattern.sectorCoord != static_cast<int>(seedCoords[sectorIdx])) {
120 return false;
121 }
122 return pattern.isInPattern(seed);
123 })};
124 if (nExistingPatterns >= m_cfg.maxSeedAttempts) {
125 ATH_MSG_VERBOSE(__func__<<"() Seed has already been used in "<<nExistingPatterns<<" patterns, which is above the limit - skip this seed.");
126 continue;
127 }
129 SearchTree_t::range_t selectRange{};
131 selectRange[sectorIdx].shrink(seedCoords[sectorIdx] - 0.1, seedCoords[sectorIdx] + 0.1);
135 const double thetaWindow {(seedLayer == LayerIndex::Inner || seedLayer == LayerIndex::Outer)
136 ? m_cfg.thetaSearchWindow : 0.5*m_cfg.thetaSearchWindow};
137 selectRange[thetaIdx].shrink(seedCoords[thetaIdx] - thetaWindow, seedCoords[thetaIdx] + thetaWindow);
139 std::vector<TreeNode> candidateHits = orderedSpacepoints.rangeSearchWithKey(selectRange);
140 if (candidateHits.size() < m_cfg.minBendingTriggerHits + m_cfg.minBendingPrecisionHits) {
141 ATH_MSG_VERBOSE(__func__<<"() Found "<<candidateHits.size()<<" candidate hits, below the minimum required - skip this seed.");
142 continue;
143 }
145 if (std::ranges::none_of(candidateHits, [this, seedLayer](const TreeNode& c){
146 return m_cfg.idHelperSvc->layerIndex(c.second->identify()) != seedLayer; }) ) {
147 ATH_MSG_VERBOSE(__func__<<"() All candidates are in the same station layer, and we need at least two - skip this seed.");
148 continue;
149 }
151 std::ranges::sort(candidateHits, [](const TreeNode& c1, const TreeNode& c2){
152 const HitPayload& hit1 {c1.second};
153 const HitPayload& hit2 {c2.second};
155 if (ordering == eSameLayer) {
157 if (hit1.isPrecision != hit2.isPrecision) {
158 return hit2.isPrecision;
159 }
161 return hit1->localPosition().y() < hit2->localPosition().y();
162 }
163 return ordering == eLowerLayer;
164 });
165 if (msgLvl(MSG::VERBOSE)) {
166 ATH_MSG_VERBOSE(__func__<<"() Found "<< candidateHits.size()<<" candidate hits: ");
167 for (const auto& [coords, hit] : candidateHits) {
168 ATH_MSG_VERBOSE(__func__<<"() \t**"<<*hit<<", coords: "<<coords << ", glob Z/R/phi: "<<hit.Z<<" / "<<hit.R<<" / "<<inDegrees(hit.phi)
169 << ", st/layer: " << stName(hit.station) << " / "<< hit.layerNum);
170 }
171 }
172 const auto seedItr {std::ranges::find_if(candidateHits,
173 [&seed](const TreeNode& c){ return c.second == seed; })};
174 assert(seedItr != candidateHits.end());
176 std::vector<PatternState> activePatterns{};
177 activePatterns.emplace_back(seed, static_cast<int>(seedCoords[sectorIdx]), seedCoords[thetaIdx]);
178 if (visualInfo) {
179 activePatterns.back().visualInfo = std::make_unique<PatternHitVisualInfo>(
180 seed.hit, seedCoords[thetaIdx] - thetaWindow, seedCoords[thetaIdx] + thetaWindow);
181 }
182
183 const HitPayload* prevCandidate {&seed};
189 auto processNewHit = [&](const TreeNode& testPair){
190 const HitPayload& test {testPair.second};
191 ATH_MSG_VERBOSE("processNewHit() *** Test "<<*test<<" against " << activePatterns.size() << " active patterns.");
192 extendPatterns(activePatterns, test, seed, *prevCandidate, visualInfo);
193 prevCandidate = &test;
194 };
197 auto ensureOnePattern = [&activePatterns, &visualInfo, this]() {
198 if (activePatterns.size() > 1) {
199 ATH_MSG_VERBOSE("ensureOnePattern() Found "<<activePatterns.size()<<" patterns in current search stage - keep the best one.");
200 activePatterns = resolveOverlaps(std::move(activePatterns), visualInfo);
201 resetVisualToOverlap(visualInfo);
202 assert(activePatterns.size() == 1);
203 }
204 };
205
207 ATH_MSG_VERBOSE(__func__<<"() Search between " << candidateHits.size() <<" candidates for compatible hits...");
208 for (auto it = std::next(seedItr); it != candidateHits.end(); ++it) {
209 processNewHit(*it);
210 }
211 ensureOnePattern();
213 for (auto it = std::reverse_iterator(seedItr); it != candidateHits.rend(); ++it) {
214 processNewHit(*it);
215 }
216 ensureOnePattern();
217 PatternState& newPattern {activePatterns.back()};
219 if (newPattern.nBendingTriggerHits < m_cfg.minBendingTriggerHits ||
220 newPattern.nPrecisionHits < m_cfg.minBendingPrecisionHits ||
221 std::ranges::count_if(newPattern.hitsPerStation, [](const auto& st) { return st.second.size()>= 3; }) < 2) {
222 ATH_MSG_VERBOSE(__func__<<"() Pattern " << newPattern << "\ndoes not meet minimum hit requirements - reject.");
224 continue;
225 }
226 newPattern.meanNormResidual2 /= (newPattern.nPrecisionHits + newPattern.nBendingTriggerHits);
227 if (newPattern.meanNormResidual2 > m_cfg.meanNormRes2Cut) {
228 ATH_MSG_VERBOSE(__func__<<"() Pattern " << newPattern << "\ndoes not meet the mean norm residual2 cut - reject.");
230 continue;
231 }
232 newPattern.isFinalized = true;
233 ATH_MSG_VERBOSE(__func__<<"() Add new pattern "<<newPattern);
234 patterns.push_back(std::move(newPattern));
235 }
236 }
237 ATH_MSG_VERBOSE("Found in total "<<patterns.size()<<" patterns in eta before overlap removal");
238 return resolveOverlaps(std::move(patterns), visualInfo);
239}
bool msgLvl(const MSG::Level lvl) const
Test the output level.
std::pair< SearchTree_t::coordinate_t, HitPayload > TreeNode
Type alias for a tree node, formed by a hit payload and its indexing coordinates.
PatternStateVec resolveOverlaps(PatternStateVec &&toResolve, PatternHitVisualInfoVec *visualInfo=nullptr) const
Method to remove overlapping patterns.
void extendPatterns(PatternStateVec &activePatterns, const HitPayload &test, const HitPayload &seed, const HitPayload &prevCandidate, PatternHitVisualInfoVec *visualInfo=nullptr) const
Function testing pattern compatibility of a set of active patterns (patterns produced from the same s...
LayerOrdering
Enum to express the logical measurement layer ordering given two hits.
SeedCoords
Abrivation of the seed coordinates.
const std::string & layerName(LayerIndex index)
convert LayerIndex into a string
double meanNormResidual2
Mean over eta hits of the square of their residual divided by acceptance window.

◆ initMessaging()

void AthMessaging::initMessaging ( ) const
privateinherited

Initialize our message level and MessageSvc.

This method should only be called once.

Definition at line 39 of file AthMessaging.cxx.

40{
42 // If user did not set an explicit level, set a default
43 if (m_lvl == MSG::NIL) {
44 m_lvl = m_imsg ?
45 static_cast<MSG::Level>( m_imsg.load()->outputLevel(m_nm) ) :
46 MSG::INFO;
47 }
48}
std::string m_nm
Message source name.
std::atomic< IMessageSvc * > m_imsg
MessageSvc pointer.
std::atomic< MSG::Level > m_lvl
Current logging level.
IMessageSvc * getMessageSvc(bool quiet=false)

◆ isBetter()

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

Operator to compare two patterns.

Parameters
aFirst pattern to compare
bSecond pattern to compare
Returns
: true if pattern a is better than pattern b, false otherwise

Function computing the score for a pattern

Definition at line 479 of file GlobalPatternFinder.cxx.

480 {
482 auto score = [this](const PatternState& p) {
483 const long nStations {std::ranges::count_if(p.hitsPerStation, [](const auto& pair){ return pair.second.size() >= 4u; })};
484 const double etaHitCount {1.*p.nBendingTriggerHits + m_cfg.precisionWeight * p.nPrecisionHits};
485 const double etaHitScore {std::tanh(etaHitCount / (std::max(nStations, 1L) * m_cfg.hitScoreSaturation))};
486 const double stationScore {std::tanh(1.*nStations / 2.)};
487 double residualRatio {p.meanNormResidual2 / (1.5*m_cfg.meanNormRes2Cut)};
488 if (!p.isFinalized) residualRatio /= (p.nBendingTriggerHits + p.nPrecisionHits);
489 const double resPenalty {m_cfg.residualPenalty * residualRatio / (1. + residualRatio)};
490 const double etaScore {stationScore * etaHitScore * (1. - resPenalty)};
491 const double phiScore {std::tanh(1.*p.nPhiHits / m_cfg.phiBonusSaturation)};
492 ATH_MSG_VERBOSE("score() stationScore: "<<stationScore<<", etaHitScore: "<<etaHitScore<<", resPenalty: "<<resPenalty<<", etaScore: "<<etaScore<<", phiScore: "<<phiScore);
493 return 0.9 * etaScore + 0.1 * phiScore;
494 };
495 const double scoreA {score(a)};
496 const double scoreB {score(b)};
497 ATH_MSG_VERBOSE(__func__<<"() Pattern " << a << " with score " << scoreA << " is " <<
498 (scoreA > scoreB ? "BETTER" : "WORSE") << " than " << b << " with score " << scoreB);
499 return scoreA > scoreB;
500}

◆ isPhiCompatible()

bool MuonR4::FastReco::GlobalPatternFinder::isPhiCompatible ( const double testPhi,
const PatternState & pattern ) const
private

Method to check the phi compatibility of a test hit with a given pattern.

Parameters
testPhitest global phi
patternpattern to be extended
Returns
: true if the test hit is phi compatible with the pattern, false otherwise

We check that the test hit is compatible with the pattern phi, if available, which is given by the first phi measurement in the pattern. If the pattern doesn't have a phi yet, we check that the test hit is in the same pattern sector(s)

Definition at line 728 of file GlobalPatternFinder.cxx.

729 {
733 if (pattern.nPhiHits) {
734 if (std::abs(CxxUtils::deltaPhi(pattern.phi, testPhi)) > m_cfg.phiTolerance) {
735 ATH_MSG_VERBOSE(__func__<<"() The pattern with phi = "<<inDegrees(pattern.phi)<<" is not compatible with the test hit with phi "<<inDegrees(testPhi));
736 return false;
737 }
738 } else {
739 const bool isCompatible {pattern.sector1 == pattern.sector2 ? sectorMap.insideSector(pattern.sector1, testPhi) :
740 sectorMap.insideSector(pattern.sector1, testPhi) && sectorMap.insideSector(pattern.sector2, testPhi)};
741 if (!isCompatible) {
742 ATH_MSG_VERBOSE(__func__<<"() The test hit with phi = "<<inDegrees(testPhi)<<" is not inside the pattern sectors: "<<pattern.sector1<<" and "<<pattern.sector2);
743 return false;
744 }
745 }
746 return true;
747}

◆ msg() [1/2]

MsgStream & AthMessaging::msg ( ) const
inlineinherited

The standard message stream.

Returns a reference to the default message stream May not be invoked before sysInitialize() has been invoked.

Definition at line 167 of file AthMessaging.h.

168{
169 MsgStream* ms = m_msg_tls.get();
170 if (!ms) {
171 if (!m_initialized.test_and_set()) initMessaging();
172 ms = new MsgStream(m_imsg,m_nm);
173 m_msg_tls.reset( ms );
174 }
175
176 ms->setLevel (m_lvl);
177 return *ms;
178}
boost::thread_specific_ptr< MsgStream > m_msg_tls
MsgStream instance (a std::cout like with print-out levels).
void initMessaging() const
Initialize our message level and MessageSvc.

◆ msg() [2/2]

MsgStream & AthMessaging::msg ( const MSG::Level lvl) const
inlineinherited

The standard message stream.

Returns a reference to the default message stream May not be invoked before sysInitialize() has been invoked.

Definition at line 182 of file AthMessaging.h.

183{ return msg() << lvl; }
MsgStream & msg() const
The standard message stream.

◆ msgLvl()

bool AthMessaging::msgLvl ( const MSG::Level lvl) const
inlineinherited

Test the output level.

Parameters
lvlThe message level to test against
Returns
boolean Indicating if messages at given level will be printed
Return values
trueMessages at level "lvl" will be printed

Definition at line 151 of file AthMessaging.h.

152{
153 // If user did not set explicit message level we have to initialize
154 // the messaging and retrieve the default via the MessageSvc.
155 if (m_lvl==MSG::NIL && !m_initialized.test_and_set()) initMessaging();
156
157 if (m_lvl <= lvl) {
158 msg() << lvl;
159 return true;
160 } else {
161 return false;
162 }
163}

◆ resolveOverlaps()

GlobalPatternFinder::PatternStateVec MuonR4::FastReco::GlobalPatternFinder::resolveOverlaps ( PatternStateVec && toResolve,
PatternHitVisualInfoVec * 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

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

Overlap if more than half the hits of the smaller pattern are shared

Definition at line 502 of file GlobalPatternFinder.cxx.

503 {
504 PatternStateVec outputPatterns{};
505 outputPatterns.reserve(toResolve.size());
507 auto areOverlapping = [this](const PatternState& a, const PatternState& b) {
509 if(std::abs(a.sectorCoord - b.sectorCoord) > 1) {
510 return false;
511 }
512 if (a.nPhiHits > 0 && b.nPhiHits > 0) {
513 if (std::abs(CxxUtils::deltaPhi(a.phi, b.phi)) > 2.*m_cfg.phiTolerance) return false;
514 } else if (a.nPhiHits > 0) {
515 if (!sectorMap.insideSector(b.sector1, a.phi) || !sectorMap.insideSector(b.sector2, a.phi)) {
516 return false;
517 }
518 } else if (b.nPhiHits > 0) {
519 if (!sectorMap.insideSector(a.sector1, b.phi) || !sectorMap.insideSector(a.sector2, b.phi)) {
520 return false;
521 }
522 }
523 if (std::abs(a.theta - b.theta) > 2.*m_cfg.thetaSearchWindow) {
524 return false;
525 }
527 int nSharedHits{0};
528 for (const auto& [stA, hitsA] : a.hitsPerStation) {
529 auto itB = b.hitsPerStation.find(stA);
530 if (itB == b.hitsPerStation.end()) {
531 continue;
532 }
533 nSharedHits += std::ranges::count_if(hitsA, [&](const HitPayload& hitA){
534 return std::ranges::find(itB->second, hitA) != itB->second.end();
535 });
536 }
538 const int minHits {std::min(a.nBendingTriggerHits + a.nPrecisionHits, b.nBendingTriggerHits + b.nPrecisionHits)};
539 return nSharedHits > minHits / 2;
540 };
541 for (auto it = toResolve.begin(); it != toResolve.end(); ++it) {
542 if (it->isOverlap) {
543 // If already marked as overlap, add to visual info, and discard the pattern
545 continue;
546 }
547 for (auto jt = std::next(it); jt != toResolve.end(); ++jt) {
548 if (jt->isOverlap || !areOverlapping(*it, *jt)) {
549 continue;
550 }
551 if (isBetter(*it, *jt)) {
552 jt->isOverlap = true;
553 } else {
554 it->isOverlap = true;
555 break;
556 }
557 }
558 if (!it->isOverlap) {
559 it->finalizePatternEta();
560 outputPatterns.push_back( std::move(*it));
561 } else {
562 // If overlap, add to visual info, as the pattern will be discarded
564 }
565 }
566 ATH_MSG_VERBOSE(__func__<<"() Patterns surviving overlap removal: "<< outputPatterns.size());
567 return outputPatterns;
568}

◆ setLevel()

void AthMessaging::setLevel ( MSG::Level lvl)
inherited

Change the current logging level.

Use this rather than msg().setLevel() for proper operation with MT.

Definition at line 28 of file AthMessaging.cxx.

29{
30 m_lvl = lvl;
31}

Member Data Documentation

◆ ATLAS_THREAD_SAFE

std::atomic_flag m_initialized AthMessaging::ATLAS_THREAD_SAFE = ATOMIC_FLAG_INIT
mutableprivateinherited

Messaging initialized (initMessaging).

Definition at line 141 of file AthMessaging.h.

◆ m_cfg

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

Global Pattern Recognition configuration.

Definition at line 394 of file GlobalPatternFinder.h.

◆ m_imsg

std::atomic<IMessageSvc*> AthMessaging::m_imsg { nullptr }
mutableprivateinherited

MessageSvc pointer.

Definition at line 135 of file AthMessaging.h.

135{ nullptr };

◆ m_lvl

std::atomic<MSG::Level> AthMessaging::m_lvl { MSG::NIL }
mutableprivateinherited

Current logging level.

Definition at line 138 of file AthMessaging.h.

138{ MSG::NIL };

◆ m_msg_tls

boost::thread_specific_ptr<MsgStream> AthMessaging::m_msg_tls
mutableprivateinherited

MsgStream instance (a std::cout like with print-out levels).

Definition at line 132 of file AthMessaging.h.

◆ m_nm

std::string AthMessaging::m_nm
privateinherited

Message source name.

Definition at line 129 of file AthMessaging.h.

◆ m_spSorter

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

Spacepoint sorter per logical measurement layer.

Definition at line 392 of file GlobalPatternFinder.h.

392{};

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