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  CandidateHit
 Small wrapper for candidate hits used to build patterns. More...
struct  LineTestRes
 : Small struct to encapsulate the result of the line compatibility test More...
struct  PatternState
 Pattern state object storing pattern information during construction. More...
struct  PatternPrintView

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  LineTestDecision : std::int8_t {
  eAddHit , eBranchPattern , eRejectHit , eConsecutiveMdt ,
  eOverwriteLastHit
}
 : Enum for possible outcomes of pattern line compatibility test 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 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 global pattern building in the bending plane.
void extendPatterns (PatternStateVec &startPatterns, PatternStateVec &endPatterns, const CandidateHit &testHit, const Amg::Vector3D &beamSpot, PatternHitVisualInfoVec *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, 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.
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 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 bool areConsecutiveMdt (const CandidateHit &hit1, const CandidateHit &hit2)
 Helper function to check whether two hits are consecutive MDT measurements.
static PatternPrintView brief (const PatternState &p)
 Print the pattern candidate and stream operator.
static PatternPrintView detailed (const PatternState &p)

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).

Static Private Attributes

static const int s_nStations {Acts::toUnderlying(StIndex::StIndexMax)}

Friends

std::ostream & operator<< (std::ostream &os, const PatternPrintView &v)

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 30 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 41 of file GlobalPatternFinder.h.

◆ LayerIndex

Type alias for the station layer index.

Definition at line 35 of file GlobalPatternFinder.h.

◆ PatternHitVisualInfo

◆ PatternHitVisualInfoVec

Abrivation for a vector of visual information objects.

Definition at line 45 of file GlobalPatternFinder.h.

◆ PatternStateVec

Definition at line 331 of file GlobalPatternFinder.h.

◆ PatternVec

Abrivation for a vector of global patterns.

Definition at line 39 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 139 of file GlobalPatternFinder.h.

◆ SpacePointContainerVec

Abrivation for a vector of space-point containers.

Definition at line 37 of file GlobalPatternFinder.h.

◆ StIndex

Type alias for the station index.

Definition at line 33 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 389 of file GlobalPatternFinder.h.

389 : std::int8_t{
390 eSameLayer,
391 eLowerLayer,
392 eHigherLayer
393 };

◆ LineTestDecision

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

: Enum for possible outcomes of pattern line compatibility test

Enumerator
eAddHit 

Test successfull, add hit to pattern.

eBranchPattern 

Test successfull with multiple pattern hits on same layer, branch the pattern.

eRejectHit 

Test failed, discard the hit.

eConsecutiveMdt 

Test hit is a consecutive MDT hit.

eOverwriteLastHit 

Test successful, overwrite the hit.

Needed e.g. for sTGCs

Definition at line 171 of file GlobalPatternFinder.h.

171 : std::int8_t{
173 eAddHit,
175 eBranchPattern,
177 eRejectHit,
179 eConsecutiveMdt,
181 eOverwriteLastHit
182 };

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

141 : std::uint8_t{
143 eSector,
145 eTheta
146 };

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

28 :
30 m_cfg{config} {
31 static_assert(std::is_move_assignable_v<PatternState>);
32 static_assert(std::is_move_constructible_v<PatternState>);
33 static_assert(std::is_copy_assignable_v<PatternState>);
34 static_assert(std::is_copy_constructible_v<PatternState>);
35 static_assert(std::is_nothrow_move_constructible_v<PatternState>);
36 static_assert(std::is_nothrow_move_assignable_v<PatternState>);
37};
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 510 of file GlobalPatternFinder.cxx.

511 {
512 constexpr auto covIdxEta {Acts::toUnderlying(SpacePoint::CovIdx::etaCov)};
517 struct PhiStripProjectionModel {
518 StIndex station{};
519 double refLay{0.};
520 double refStrip{0.};
521 double invSlope{0.};
522 bool isValid{false};
523
524 double project(const Amg::Vector3D& pos) const {
525 const double layCoord = isBarrel(station) ? pos.perp() : pos.z();
526 return refStrip + (layCoord - refLay) * invSlope;
527 }
528 double residual(const Amg::Vector3D& pos) const {
529 const double stripCoord = isBarrel(station) ? pos.z() : pos.perp();
530 return std::abs(project(pos) - stripCoord);
531 }
532 };
533 auto makeProjectionModel = [this](PatternState& pat, const StIndex station) {
534 PhiStripProjectionModel result{};
535 result.station = station;
536 const auto& hits {pat.hitsPerStation[Acts::toUnderlying(station)]};
537 if (hits.empty()) return result;
538
539 // Define the coordinate across the layers, i.e. orthogonal to the strip
540 const auto layCoord = [&result](const HitPayload& hit) {
541 return isBarrel(result.station) ? hit.R : hit.Z;
542 };
543 // Define the coordinate along the strip, i.e. orthogonal to the measureent layers
544 const auto stripCoord = [&result](const HitPayload& hit) {
545 return isBarrel(result.station) ? hit.Z : hit.R;
546 };
547
548 const HitPayload* sp1 {nullptr};
549 const HitPayload* sp2 {nullptr};
550 if (pat.nMeasurementLayers[Acts::toUnderlying(station)] > 1) {
551 // if we have >= 2 eta hits in the station, we use the furthestmost to define the pattern line
552 const auto [minIt, maxIt] = std::ranges::minmax_element(hits, {},
553 [](const CandidateHit& c){ return c.globLayer; });
554 sp1 = minIt->hit;
555 sp2 = maxIt->hit;
556 }
557 if (!sp1 || !sp2 || std::abs(layCoord(*sp2) - layCoord(*sp1)) < m_cfg.minLayerSeparation) {
558 // if we have only one eta hit, to find the second hit we use the we use the functionality of anchor hit
559 pat.moveLineAnchorHit(hits.front());
560
561 sp1 = hits.front().hit;
562 sp2 = pat.lineAnchorHit.hit;
563 }
564 if (!sp1 || !sp2 ) return result;
565
566 result.refLay = layCoord(*sp1);
567 result.refStrip = stripCoord(*sp1);
568 result.invSlope = (stripCoord(*sp2) - stripCoord(*sp1)) / (layCoord(*sp2) - layCoord(*sp1));
569 result.isValid = true;
570 return result;
571 };
572
573 PatternStateVec survivingPatterns{};
574 survivingPatterns.reserve(patterns.size());
575 for (PatternState& pat : patterns) {
576 pat.finalizePatternEta();
578 ATH_MSG_VERBOSE(__func__<<"() Search for phi-only hits for pattern: " << brief(pat));
579 // Projection model of pattern line onto a given phi strip
580 std::optional<PhiStripProjectionModel> patProjOnStrip{};
581 for (const SpacePointBucket* bucket : pat.getParentBuckets()) {
582 const Amg::Transform3D& localToGlobal {bucket->msSector()->localToGlobalTransform(gctx)};
583 const StIndex station {m_cfg.idHelperSvc->stationIndex(bucket->front()->identify())};
584 // If the projection model is not valid, we will use the pattern theta. We cache the local Y in glob frame
585 const Amg::Vector3D locY {localToGlobal.linear() * Amg::Vector3D::UnitY()};
586
587 for (const auto& hit : *bucket) {
588 // We are looking for phi-only hits
589 if (hit->measuresEta()){
590 continue;
591 }
592 ATH_MSG_VERBOSE(__func__<<"() *** Test phi-only hit "<<*hit);
593 // Check phi compatibility
594 const Amg::Vector3D locPosTest {hit->localPosition()};
595 const Amg::Vector3D globPosTest {localToGlobal * locPosTest};
596 const double globPhi {globPosTest.phi()};
597 if (!pat.isPhiCompatible(globPhi)) {
598 ATH_MSG_VERBOSE(__func__<<"() Phi-only hit not compatible");
599 continue;
600 }
601 // Check there are not other phi hits in the same layer
602 const uint8_t layNum = m_spSorter.sectorLayerNum(*hit);
603 const uint8_t stIdx = Acts::toUnderlying(station);
604 assert(!pat.hitsPerStation[stIdx].empty());
605 if (std::ranges::any_of(pat.hitsPerStation[stIdx], [&](const CandidateHit& h){
606 return h.sp()->measuresPhi() && hit->msSector() == h.sp()->msSector() && layNum == h->locLayer; }) ||
607 std::ranges::any_of(pat.phiOnlyHits, [&](const HitPayload& h){
608 return station == h.station && hit->msSector() == h->msSector() && layNum == h.locLayer; })) {
609 ATH_MSG_VERBOSE(__func__<<"() The pattern already has a phi hit in the same layer - skip this test hit.");
610 continue;
611 }
612 // Check eta compatibility. Try first to use the pattern line projection model
613 bool isEtaCompatible {false};
614 const double sigmaEta {std::sqrt(hit->covariance()[covIdxEta])};
615 if (!patProjOnStrip.has_value() || patProjOnStrip->station != station) {
616 patProjOnStrip = makeProjectionModel(pat, station);
617 }
618 if(patProjOnStrip->isValid) {
619 isEtaCompatible = patProjOnStrip->residual(globPosTest) <= 1.1*sigmaEta;
620 ATH_MSG_VERBOSE(__func__<<"() Distance pattern line from strip center: "<<patProjOnStrip->residual(globPosTest)
621 <<", strip half-length: "<<sigmaEta<<", isCompatible: "<<isEtaCompatible);
622 } else {
623 // Check pattern theta against global theta at the boundaries of the phi strip
624 double thetaMin {(globPosTest - sigmaEta * locY).theta()};
625 double thetaMax {(globPosTest + sigmaEta * locY).theta()};
626 if (thetaMax < thetaMin) {
627 std::swap(thetaMin, thetaMax);
628 }
629 isEtaCompatible = std::max(pat.theta - thetaMax, thetaMin - pat.theta) < 0.5 * m_cfg.thetaSearchWindow;
630 ATH_MSG_VERBOSE(__func__<<"() Pattern theta "<<inDegrees(pat.theta)
631 <<", strip theta window: ["<<inDegrees(thetaMin)<<", "<<inDegrees(thetaMax)<<"]");
632
633 }
634 if (!isEtaCompatible) {
635 ATH_MSG_VERBOSE(__func__<<"() The pattern falls outside the test hit strip in eta - skip this test hit.");
636 continue;
637 }
638 // Create the hit payload and add the hit to the pattern. Save only relevant quantities for phi-only hits.
639 ATH_MSG_VERBOSE(__func__<<"() Phi-only hit compatible - add it to the pattern.");
640 pat.phiOnlyHits.emplace_back(hit.get(), /*bucket*/nullptr, /*container*/nullptr, /*R*/0.f, /*Z*/0.f,
641 globPhi, station, layNum, /*sector*/0u, /*isPrecision*/false, /*isStraw*/false);
642 if (pat.nPhiLayers == 0) pat.phi = globPhi;
643 pat.nPhiLayers++;
644 }
645 }
646 if (pat.nPhiLayers < m_cfg.minPhiLayers) {
647 ATH_MSG_VERBOSE(__func__<<"() Pattern "<< detailed(pat)<<" has only "<<pat.nPhiLayers
648 <<" phi layers, below the minimum required - reject this pattern.");
649 continue;
650 }
651 pat.finalizePatternPhi();
652 survivingPatterns.push_back(std::move(pat));
653 }
654 std::swap(patterns, survivingPatterns);
655}
Scalar theta() const
theta method
#define ATH_MSG_VERBOSE(x)
T_ResultType project(ParameterMapping::type< N > parameter_map, const T_Matrix &matrix)
static PatternPrintView brief(const PatternState &p)
Print the pattern candidate and stream operator.
Muon::MuonStationIndex::StIndex StIndex
Type alias for the station index.
std::vector< PatternState > PatternStateVec
SpacePointPerLayerSorter m_spSorter
Spacepoint sorter per logical measurement layer.
static PatternPrintView detailed(const PatternState &p)
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)
Small wrapper for candidate hits used to build patterns.
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 1266 of file GlobalPatternFinder.cxx.

1268 {
1269 if (!visualInfo) {
1270 return;
1271 }
1272 // First save the buckets
1273 std::vector<const SpacePointBucket*> buckets{cache.getParentBuckets()};
1274
1275 GlobalPattern pattern {convertToPattern(cache)};
1276 // Check whether the visual info about this pattern is already in the container
1277 if (auto it =std::ranges::find_if(*visualInfo, [&pattern](const auto& v){
1278 return v.patternCopy && *v.patternCopy == pattern; }); it != visualInfo->end()) {
1279 it->status = status; // Update the status if the pattern is already in the container
1280 return;
1281 }
1282 visualInfo->push_back(*cache.visualInfo);
1283
1284 std::ranges::copy(buckets, std::back_inserter(visualInfo->back().parentBuckets));
1285
1286 visualInfo->back().patternCopy = std::make_unique<GlobalPattern>(std::move(pattern));
1287 visualInfo->back().status = status;
1288}
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

◆ areConsecutiveMdt()

bool MuonR4::FastReco::GlobalPatternFinder::areConsecutiveMdt ( const CandidateHit & hit1,
const CandidateHit & hit2 )
staticprivate

Helper function to check whether two hits are consecutive MDT measurements.

To understand: in principle we should not have two MDT hits in the same tube... but this can happen now when running run4 digitization. Therefore, for now we don't throw when hit1==hit2 but we just reject one of the two... to understand for future improvements

Definition at line 1251 of file GlobalPatternFinder.cxx.

1252 {
1253 if (!hit1.isStraw || !hit2.isStraw) {
1254 return false;
1255 }
1256 const uint16_t tubeNum1 {static_cast<const xAOD::MdtDriftCircle*>(hit1.sp()->primaryMeasurement())->driftTube()};
1257 const uint16_t tubeNum2 {static_cast<const xAOD::MdtDriftCircle*>(hit2.sp()->primaryMeasurement())->driftTube()};
1261 if(tubeNum1 == tubeNum2) {
1262 return false;
1263 };
1264 return std::abs(tubeNum1-tubeNum2) < 2;
1265 }
std::uint16_t driftTube() const
Returns the tube number of the measurement (1-120).
MdtDriftCircle_v1 MdtDriftCircle
setWord1 uint16_t

◆ brief()

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

Print the pattern candidate and stream operator.

Definition at line 1314 of file GlobalPatternFinder.cxx.

1314 {
1315 return {p, /*detailed=*/false};
1316}

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

1198 {
1199 auto getLayerOrdering = [](const bool isLayer1Lower) {
1200 return isLayer1Lower ? eLowerLayer : eHigherLayer;
1201 };
1202 if (hit1 == hit2) {
1203 return eSameLayer;
1204 }
1206 if (hit1->msSector() == hit2->msSector()) {
1207 if (hit1.locLayer == hit2.locLayer) {
1208 return eSameLayer;
1209 } else {
1210 return getLayerOrdering(hit1.locLayer < hit2.locLayer);
1211 }
1212 }
1213 StIndex st1 {hit1.station};
1214 StIndex st2 {hit2.station};
1216 if (st1 == st2) {
1217 return getLayerOrdering(hit1->msSector()->barrel() ? hit1.R < hit2.R : std::abs(hit1.Z) < std::abs(hit2.Z));
1218 }
1219 LayerIndex layer1 {toLayerIndex(st1)};
1220 LayerIndex layer2 {toLayerIndex(st2)};
1221 if (layer1 == layer2) {
1223 if (layer1 == LayerIndex::Middle) {
1225 return getLayerOrdering(st1 == StIndex::BM);
1226 }
1227 if (layer1 == LayerIndex::Inner) {
1229 return getLayerOrdering(hit1.R < hit2.R);
1230 }
1231 throw std::runtime_error("Unexpected to have two pattern-compatible hits one in BO and the other in EO.");
1232 }
1233 if (layer1 == LayerIndex::Inner || layer2 == LayerIndex::Inner) {
1235 return getLayerOrdering(layer1 == LayerIndex::Inner);
1236 }
1237 if (layer1 == LayerIndex::Outer || layer2 == LayerIndex::Outer) {
1239 return getLayerOrdering(layer2 == LayerIndex::Outer);
1240 }
1241 if (layer1 == LayerIndex::BarrelExtended || layer2 == LayerIndex::BarrelExtended) {
1243 return getLayerOrdering(layer1 == LayerIndex::BarrelExtended);
1244 }
1246 if (layer1 == LayerIndex::Extended) {
1247 return getLayerOrdering(st2 == StIndex::EM);
1248 }
1249 return getLayerOrdering(st1 == StIndex::BM);
1250}
Muon::MuonStationIndex::LayerIndex LayerIndex
Type alias for the station layer index.
LayerIndex toLayerIndex(ChIndex index)
convert ChIndex into LayerIndex

◆ 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

Definition at line 698 of file GlobalPatternFinder.cxx.

699 {
700 SearchTree_t::vector_t rawData{};
701 using SectorProjector = ExpandedSector::SectorProjector;
702 // Before the loops: estimate the total number of hits
703 size_t totalHits = 0;
704 for (const SpacePointContainer* spc : spacepoints) {
705 for (const SpacePointBucket* bucket : *spc) {
706 totalHits += bucket->size();
707 }
708 }
709 // We can have up to 3 entries per hit (when the hit does not measure phi).
710 rawData.reserve(3 * totalHits);
711
712 for (const SpacePointContainer* spc : spacepoints) {
713 ATH_MSG_VERBOSE(__func__<<"() Processing "<<spc->size()<<" space point buckets...");
714 for (const SpacePointBucket* bucket : *spc) {
715 ATH_MSG_VERBOSE(__func__<<"() Processing " << bucket->size() << " spacepoints...");
716 const Amg::Transform3D& localToGlobal {bucket->msSector()->localToGlobalTransform(gctx)};
717 const StIndex bucketStation {m_cfg.idHelperSvc->stationIndex(bucket->front()->identify())};
718 const uint8_t sector = bucket->msSector()->sector();
719
720 for (const auto& hit : *bucket) {
721 // Ignore only-phi hits and MDT hits if desired
722 const bool isStraw {hit->isStraw()};
723 if (!hit->measuresEta() || (!m_cfg.useMdtHits && isStraw)) {
724 continue;
725 }
726 ATH_MSG_VERBOSE(__func__<<"() Spacepoint: " << *hit);
727 const Amg::Vector3D globalPos {localToGlobal * hit->localPosition()};
728 const double globalPhi {globalPos.phi()};
729 const ExpandedSector hitExpSector {globalPhi};
730 const uint8_t localLayer = m_spSorter.sectorLayerNum(*hit);
731 const bool isPrecision {MuonR4::isPrecisionHit(*hit)};
732
733 /* Determine the projection direction, which is the direction normal to the radial direction */
734 const Amg::Vector3D projDir {ExpandedSector{sector, SectorProjector::center}.normalDir()};
735
738 for (const auto proj : {SectorProjector::leftOverlap, SectorProjector::center, SectorProjector::rightOverlap}) {
740 const ExpandedSector expSect {sector, proj};
741 if (proj != SectorProjector::center && hit->measuresPhi() && expSect != hitExpSector) {
742 ATH_MSG_VERBOSE(__func__<<"() Hit with "<<hitExpSector<<" is not compatible with "<<expSect);
743 continue;
744 }
745
746 /* Project the hit onto the plane along the sector radial direction.
747 * This allows to remove the bias of hit displacement in phi direction */
748 const double projR {(globalPos - globalPos.dot(projDir) * projDir).perp()};
749
750 std::array<double, 2> coords{};
751 coords[Acts::toUnderlying(SeedCoords::eTheta)] = atan2(projR, globalPos.z());
752 coords[Acts::toUnderlying(SeedCoords::eSector)] = expSect.sector();
753
754 ATH_MSG_VERBOSE(__func__<<"() Add hit: Z: " << globalPos.z() << ", R: " << globalPos.perp() << ", ProjR: " << projR
755 << ", Phi: "<< inDegrees(globalPhi) << ", SectorPhi: " << inDegrees(expSect.phi()) << " and coordinates "<<coords<<" to the search tree");
756 rawData.emplace_back(std::move(coords), HitPayload{hit.get(), bucket, spc,
757 static_cast<float>(projR), static_cast<float>(globalPos.z()), static_cast<float>(globalPhi),
758 bucketStation, localLayer, sector, isPrecision, isStraw});
759 }
760 }
761 }
762 }
763 ATH_MSG_VERBOSE(__func__<<"() Create a new tree with "<<rawData.size()<<" entries. ");
764 return SearchTree_t{std::move(rawData)};
765}
SectorProjector
Enumeration to select the sector projection of the regular MS sector.
@ 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.
bool isPrecisionHit(const SpacePoint &hit)
Returns whether the uncalibrated spacepoint is a precision hit (Mdt, micromegas, stgc strips).
DataVector< SpacePointBucket > SpacePointContainer
Abrivation of the space point container type.
@ globalPhi
Definition HitInfo.h:38

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

656 {
657 GlobalPattern::HitCollection hitPerStation{};
659 for (uint8_t st{0u}; st < s_nStations; ++st) {
660 const auto& hits {cache.hitsPerStation[st]};
661 if (hits.empty()) continue;
662
663 auto& out {hitPerStation[static_cast<StIndex>(st)]};
664 out.reserve(hits.size());
665 std::ranges::transform(hits, std::back_inserter(out),
666 [](const CandidateHit& h){ return h.sp();});
667 }
669 for (const HitPayload& hit : cache.phiOnlyHits) {
670 auto& out {hitPerStation[static_cast<StIndex>(hit.station)]};
671 out.push_back(hit.sp());
672 }
673 GlobalPattern pattern{std::move(hitPerStation)};
674 pattern.setTheta(cache.theta);
675 pattern.setPhi(cache.phi);
676 // Set the pattern sector(s) and theta.
677 pattern.setSector(cache.expSect.sector());
678 // Set pattern quality information.
679 pattern.setNPrecisionLayers(cache.nPrecisionLayers);
680 pattern.setNTriggerLayers(cache.nTriggerLayers);
681 pattern.setNPhiLayers(cache.nPhiLayers);
682 pattern.setMeanNormResidual2(cache.getMeanResidual2());
683 return pattern;
684}
std::unordered_map< StIndex, std::vector< HitType > > HitCollection
@ u
Enums for curvilinear frames.
Definition ParamDefs.h:77

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

687 {
689 patterns.reserve(cache.size());
690 std::transform(cache.begin(), cache.end(), std::back_inserter(patterns),
691 [this](const PatternState& cacheEntry) {
692 return convertToPattern(cacheEntry);
693 });
694 return patterns;
695}
std::vector< GlobalPattern > PatternVec
Abrivation for a vector of global patterns.

◆ detailed()

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

Definition at line 1318 of file GlobalPatternFinder.cxx.

1318 {
1319 return {p, /*detailed=*/true};
1320}

◆ extendPatterns()

void MuonR4::FastReco::GlobalPatternFinder::extendPatterns ( PatternStateVec & startPatterns,
PatternStateVec & endPatterns,
const CandidateHit & testHit,
const Amg::Vector3D & beamSpot,
PatternHitVisualInfoVec * 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)

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

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 of the original pattern

Check the MDT cluster size on the last layer is not above the maximum, otherwise we break the cluster

We break the residual at the second-to-last hit. So we compute the new cluster residual

We remove all hits on last layer, except the last hit. Then we add the new hit.

Update visual information of the original pattern

Definition at line 261 of file GlobalPatternFinder.cxx.

265 {
266 endPatterns.clear();
267 ATH_MSG_VERBOSE("processHitRange() *** Test "<<**testHit<<" R/Z/globLayer: "<<testHit.R<<", "<<testHit.Z
268 <<", "<<static_cast<int>(testHit.globLayer)<<" against " << startPatterns.size() << " active patterns.");
269
270 // Compute the minimum number of missed layer hits among the active patterns,
271 // to use as reference for pruning patterns with too many missed layers.
272 std::vector<unsigned> missedLayersVec{};
273 missedLayersVec.reserve(startPatterns.size());
274 std::ranges::transform(startPatterns, std::back_inserter(missedLayersVec), [&testHit](const PatternState& pat){
275 return std::abs(pat.lastInsertedHit.globLayer - testHit.globLayer);
276 });
277 const unsigned minMissedLayers {std::ranges::min(missedLayersVec)};
278
279 const bool shouldPrune {startPatterns.size() > 1 &&
280 std::ranges::any_of(startPatterns, [](const PatternState& p){
281 return p.nBendingLayers() > 2; })};
282
283 for (auto [i, pat] : Acts::enumerate(startPatterns)) {
284 if (pat.isOverlap) {
285 continue;
286 }
287 // Check the pattern has not already missed too many layers compared to other patterns.
288 if (pat.lastInsertedHit.station == testHit.station &&
289 missedLayersVec[i] > std::max(m_cfg.maxMissLayersInStation, minMissedLayers)) {
290 ATH_MSG_VERBOSE(__func__<<"() Pattern " << detailed(pat) << "\nhas missed " << (int)missedLayersVec[i]
291 << " layer hits, above the max allowed - abort pattern.");
293 continue;
294 }
298 if (shouldPrune && pat.lastInsertedHit.globLayer != testHit.globLayer &&
299 std::ranges::find_if(std::next(startPatterns.begin(), i + 1), startPatterns.end(), [&](PatternState& p){
300 if (p.lastInsertedHit != pat.lastInsertedHit || p.isOverlap) return false;
301
302 if (isBetter(pat, p)) {
303 ATH_MSG_VERBOSE("extendPatterns() Pruning: REJECT " << detailed(p));
304 p.isOverlap = true;
305 return false;
306 }
307 ATH_MSG_VERBOSE("extendPatterns() Pruning: REJECT " << detailed(pat));
308 return true; }) != startPatterns.end()) {
309 continue;
310 }
312 const auto [result, residual, accWindow] {pat.checkLineComp(testHit, beamSpot)};
313 switch (result) {
315 if (accWindow > 4.*m_cfg.baseRWindow && residual > m_cfg.baseRWindow) {
319 if (std::ranges::any_of(endPatterns, [&testHit, &pat](const PatternState& p) {
320 return p.isInLastLayer(testHit) &&
321 (p.prevLayerHit == pat.lastInsertedHit || p.nBendingLayers() > (pat.nBendingLayers() + 1u)); })) {
322 ATH_MSG_VERBOSE(__func__<<"() Low-confidence hit: forking leads to existing pattern - reject.");
323 break;
324 }
325 ATH_MSG_VERBOSE(__func__<<"() Low-confidence hit: forking leads to new pattern - fork.");
327 endPatterns.push_back(pat);
328 endPatterns.back().addHit(testHit, residual, accWindow);
330 if (visualInfo) {
331 pat.visualInfo->discardedHits.push_back(testHit.sp());
332 }
333 break;
334 }
335 ATH_MSG_VERBOSE(__func__<<"() Hit compatible - add to pattern.");
336 pat.addHit(testHit, residual, accWindow);
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.isInLastLayer(testHit) && p.prevLayerHit == pat.prevLayerHit; })) {
343 ATH_MSG_VERBOSE(__func__<<"() Hit compatible & on same layer of last added hit - branched pattern already exists.");
344 break;
345 }
347 ATH_MSG_VERBOSE(__func__<<"() Hit compatible & on same layer of last added hit - branch pattern.");
348 endPatterns.push_back(pat);
349 endPatterns.back().overWriteHit(testHit, residual, accWindow);
350
352 if (visualInfo) {
353 pat.visualInfo->replacedHits.push_back(testHit.sp());
354 endPatterns.back().visualInfo->replacedHits.push_back(pat.lastInsertedHit.sp());
355 }
356 ATH_MSG_VERBOSE("New pattern: " << brief(endPatterns.back()));
357 break;
358 }
360 ATH_MSG_VERBOSE(__func__<<"() Hit is not compatible with the pattern - reject hit.");
361 if (visualInfo) {
362 pat.visualInfo->discardedHits.push_back(testHit.sp());
363 }
364 break;
365 }
368 if (const uint8_t nMdtLastLayer{pat.nMDTLastLayer()}; nMdtLastLayer >= 3) {
370 LineTestRes newClusterRes {pat.computeLineResidual(pat.lastInsertedHit)};
371 if (newClusterRes.residual > newClusterRes.accWindow) {
372 ATH_MSG_VERBOSE(__func__<<"() Hit is the #"<<nMdtLastLayer+1
373 <<" consecutive MDT hit: new cluster is not compatible - reject.");
374 break;
375 }
376 ATH_MSG_VERBOSE(__func__<<"() Hit is the #"<<nMdtLastLayer + 1
377 <<" consecutive MDT hit: new cluster is compatible - create new pattern with new cluster.");
378 endPatterns.push_back(pat);
380 endPatterns.back().overWriteHit(pat.lastInsertedHit, newClusterRes.residual, newClusterRes.accWindow);
381 endPatterns.back().addHit(testHit, residual, accWindow);
382
384 if (visualInfo) {
385 pat.visualInfo->discardedHits.push_back(testHit.sp());
386 endPatterns.back().visualInfo->replacedHits.push_back(pat.lastInsertedHit.sp());
387 }
388 ATH_MSG_VERBOSE("New pattern: " << brief(endPatterns.back()));
389 break;
390 }
391 ATH_MSG_VERBOSE(__func__<<"() Consecutive MDT hits on same layer - accept. " << brief(pat));
392 pat.addHit(testHit, residual, accWindow);
393 break;
394 }
396 ATH_MSG_VERBOSE(__func__<<"() Hit compatible & on same layer of last added hit - overwrite last hit.");
397 pat.overWriteHit(testHit, residual, accWindow);
398
399 if (visualInfo) {
400 pat.visualInfo->replacedHits.push_back(pat.lastInsertedHit.sp());
401 }
402 break;
403 }
404 }
405 endPatterns.push_back(std::move(pat));
406 }
407 startPatterns.clear();
408};
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)...
@ eBranchPattern
Test successfull with multiple pattern hits on same layer, branch the pattern.
: Small struct to encapsulate the result of the line compatibility test

◆ 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. The tree should outlive the pattern finding process

Add phi-only hits to the patterns

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

Fill the output buckets

Plot patterns

Definition at line 41 of file GlobalPatternFinder.cxx.

43 {
46 const SearchTree_t orderedSpacepoints {constructTree(gctx, spacepoints)};
47 auto visualInfo {m_cfg.visionTool ? std::make_unique<PatternHitVisualInfoVec>() : nullptr};
48 PatternStateVec patterns{findPatternsInEta(orderedSpacepoints, visualInfo.get())};
49
52
53
54 for (const PatternState& pat : patterns) {
57
59 for (const std::vector<CandidateHit>& hits : pat.hitsPerStation) {
60 for (const auto& hit : hits) {
61 if (outBuckets.find(hit->container) == outBuckets.end()) {
62 throw std::runtime_error("The space point container associated to the pattern is not present in the output bucket map.");
63 }
64 auto& outBucketVec = outBuckets[hit->container];
65 if (std::ranges::find(outBucketVec, hit->bucket) == outBucketVec.end()) {
66 outBucketVec.push_back(hit->bucket);
67 }
68 }
69 }
70 }
72 if (visualInfo) {
73 m_cfg.visionTool->plotPatternBuckets(Gaudi::Hive::currentContext(), "GlobPatFind_", std::move(*visualInfo));
74 }
76}
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 global pattern building in the bending plane.

◆ findPatternsInEta()

GlobalPatternFinder::PatternStateVec MuonR4::FastReco::GlobalPatternFinder::findPatternsInEta ( const SearchTree_t & orderedSpacepoints,
PatternHitVisualInfoVec * 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 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

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

79 {
80 constexpr auto thetaIdx {Acts::toUnderlying(SeedCoords::eTheta)};
81 constexpr auto sectorIdx {Acts::toUnderlying(SeedCoords::eSector)};
82
84 PatternStateVec startPatternBuff{};
85 startPatternBuff.reserve(20);
86 PatternStateVec endPatternBuff{};
87 endPatternBuff.reserve(20);
88
90 const Amg::Vector3D beamSpot{Amg::Vector3D::Zero()};
91
92 PatternStateVec outPatterns{};
93 outPatterns.reserve(40);
98 auto countPatterns = [&outPatterns, this](const HitPayload& hit,
99 const SearchTree_t::coordinate_t& coords) -> uint8_t {
100 return std::ranges::count_if(outPatterns, [&](const PatternState& pattern){
101 if (std::abs(pattern.theta - coords[thetaIdx]) > 2.*m_cfg.thetaSearchWindow ||
102 !pattern.expSect.isNeighbour(ExpandedSector{static_cast<std::int8_t>(coords[sectorIdx])})) {
103 return false;
104 }
105 return pattern.isInPattern(hit);
106 });
107 };
108 using enum SeedCoords;
109 for (const auto seedingLayer : m_cfg.layerSeedings) {
111 for (const auto& [seedCoords, seed] : orderedSpacepoints) {
113 const LayerIndex seedLayer {toLayerIndex(seed.station)};
114 if (seedLayer != seedingLayer || (seed.isStraw && !m_cfg.seedFromMdt)) {
115 continue;
116 }
117 ATH_MSG_VERBOSE(__func__<<"() New seed hit "<<*seed<<", coordinates "<<seedCoords);
119 uint8_t nExistingPatterns {countPatterns(seed, seedCoords)};
120 if (nExistingPatterns >= m_cfg.maxSeedAttempts) {
121 // Try first to resolve overlaps and re-count the number of patterns containing the seed
122 outPatterns = resolveOverlaps(outPatterns, visualInfo);
123 nExistingPatterns = countPatterns(seed, seedCoords);
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 }
128 }
130 SearchTree_t::range_t selectRange{};
132 selectRange[sectorIdx].shrink(seedCoords[sectorIdx] - 0.1, seedCoords[sectorIdx] + 0.1);
136 const double thetaHalfWindow {(seedLayer == LayerIndex::Inner || seedLayer == LayerIndex::Outer)
137 ? m_cfg.thetaSearchWindow : 0.5*m_cfg.thetaSearchWindow};
138 selectRange[thetaIdx].shrink(seedCoords[thetaIdx] - thetaHalfWindow, seedCoords[thetaIdx] + thetaHalfWindow);
140 std::vector<CandidateHit> candidateHits{};
141 orderedSpacepoints.rangeSearchMapDiscard(selectRange, [&candidateHits](const SearchTree_t::coordinate_t& /*coords*/,
142 const HitPayload& hit){
143 candidateHits.emplace_back(&hit, hit.R, hit.Z, hit.station, 0u, hit.sector, hit.isStraw);
144 });
145 if (candidateHits.size() < m_cfg.minTriggerLayers + m_cfg.minPrecisionLayers) {
146 ATH_MSG_VERBOSE(__func__<<"() Found "<<candidateHits.size()<<" candidate hits, below the minimum required - skip this seed.");
147 continue;
148 }
150 if (std::ranges::none_of(candidateHits, [this, seedLayer](const CandidateHit& c){
151 return m_cfg.idHelperSvc->layerIndex(c.sp()->identify()) != seedLayer; }) ) {
152 ATH_MSG_VERBOSE(__func__<<"() All candidates are in the same station layer, and we need at least two - skip this seed.");
153 continue;
154 }
156 std::ranges::sort(candidateHits, [](const CandidateHit& c1, const CandidateHit& c2){
158 if (ordering == eSameLayer) {
160 return c1.sp()->localPosition().y() < c2.sp()->localPosition().y();
161 }
162 return ordering == eLowerLayer;
163 });
165 for (std::size_t i {1}; i < candidateHits.size(); ++i) {
166 candidateHits[i].globLayer = candidateHits[i - 1].globLayer +
167 (checkLayerOrdering(*candidateHits[i - 1], *candidateHits[i]) != eSameLayer);
168 }
169 if (candidateHits.back().globLayer + 1u < (m_cfg.minTriggerLayers + m_cfg.minPrecisionLayers)) {
170 ATH_MSG_VERBOSE(__func__<<"() Found "<<candidateHits.size()<<" candidate hits on "<<candidateHits.back().globLayer + 1
171 <<" layers, below the minimum required - skip this seed.");
172 continue;
173 }
174 if (msgLvl(MSG::VERBOSE)) {
175 ATH_MSG_VERBOSE(__func__<<"() Found "<< candidateHits.size()<<" candidate hits: ");
176 for (const auto& c : candidateHits) {
177 ATH_MSG_VERBOSE(__func__<<"() \t**"<<**c<<", glob Z/R/phi: "<<c.Z<<" / "<<c.R<<" / "<<inDegrees(c->phi)
178 << ", st/layer: " << stName(c.station) << " / "<< (int)c.globLayer);
179 }
180 }
181
183 const auto seedItr {std::ranges::find_if(candidateHits,
184 [&seed](const CandidateHit& c){ return *c == seed; })};
185 assert(seedItr != candidateHits.end());
186 const CandidateHit& seedCand {*seedItr};
187
188 PatternState patternSeed{seedCand, static_cast<std::int8_t>(seedCoords[sectorIdx]),
189 seedCoords[thetaIdx], &m_cfg, this};
190 if (visualInfo) {
191 patternSeed.visualInfo = std::make_unique<PatternHitVisualInfo>(
192 seed.hit, seedCoords[thetaIdx] - thetaHalfWindow, seedCoords[thetaIdx] + thetaHalfWindow);
193 }
194
203 auto processHitRange = [&](const auto begin,
204 const auto end,
205 PatternState&& toExtend) -> PatternStateVec {
206 startPatternBuff.clear();
207 startPatternBuff.push_back(std::move(toExtend));
208
209 for (auto testItr = begin; testItr != end; ++testItr) {
210 const CandidateHit& testHit {*testItr};
211 if (testHit.globLayer == seedCand.globLayer && !seedCand.isStraw) {
212 continue; // skip hits on the same layer as the seed, if straw
213 }
214 extendPatterns(startPatternBuff, endPatternBuff, testHit, beamSpot, visualInfo);
215 // Swap the buffers for the next iteration
216 std::swap(startPatternBuff, endPatternBuff);
217 }
218 return startPatternBuff.size() > 1
219 ? resolveOverlaps(startPatternBuff, visualInfo)
220 : PatternStateVec{std::move(startPatternBuff.back())};
221 };
222
224 PatternStateVec forwardExtended {processHitRange(std::next(seedItr), candidateHits.end(), std::move(patternSeed))};
225
227 ATH_MSG_VERBOSE(__func__<<"() Finished forward search, found "<<forwardExtended.size()<<" forward patterns, start backward search.");
228 PatternStateVec backwardExtended{};
229 backwardExtended.reserve(2*forwardExtended.size());
230
231 for (PatternState& pat : forwardExtended) {
232 pat.moveLineAnchorHit(seedCand);
233 pat.lastInsertedHit = seedCand;
234
236 std::ranges::move(
237 processHitRange(std::reverse_iterator(seedItr), candidateHits.rend(), std::move(pat)),
238 std::back_inserter(backwardExtended)
239 );
240 }
242 if (backwardExtended.size() > 1) {
243 backwardExtended = resolveOverlaps(backwardExtended, visualInfo);
244 }
245
246 for (PatternState& pat : backwardExtended) {
247 pat.meanNormResidual2 /= pat.nBendingLayers();
248 if (!passPatternCuts(pat)) {
250 continue;
251 }
252 ATH_MSG_VERBOSE(__func__<<"() Add new pattern "<<detailed(pat));
253 pat.isFinalized = true;
254 outPatterns.push_back(std::move(pat));
255 }
256 }
257 }
258 ATH_MSG_VERBOSE(__func__<<"() Found in total "<<outPatterns.size()<<" patterns in eta before overlap removal");
259 return resolveOverlaps(outPatterns, visualInfo);
260}
bool msgLvl(const MSG::Level lvl) const
Test the output level.
void extendPatterns(PatternStateVec &startPatterns, PatternStateVec &endPatterns, const CandidateHit &testHit, const Amg::Vector3D &beamSpot, PatternHitVisualInfoVec *visualInfo=nullptr) const
Main function controlling the development of patterns, including pattern branching when necessary.
PatternStateVec resolveOverlaps(PatternStateVec &toResolve, PatternHitVisualInfoVec *visualInfo=nullptr) const
Method to remove overlapping patterns.
static LayerOrdering checkLayerOrdering(const HitPayload &hit1, const HitPayload &hit2)
Method to check the logical layer ordering of two hits.
bool passPatternCuts(const PatternState &pat) const
Method to check if a pattern passes the quality cuts.
LayerOrdering
Enum to express the logical measurement layer ordering given two hits.
SeedCoords
Abrivation of the seed coordinates.
return m_collEvts back().back().max_entries
const std::string & stName(StIndex index)
convert StIndex into a string
AthConfigFlags beamSpot(AthConfigFlags flags, str instanceName, str recoMode)

◆ 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 )
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

Definition at line 1189 of file GlobalPatternFinder.cxx.

1189 {
1190 const int nLayerDiff {a.nBendingLayers() - b.nBendingLayers()};
1191 if (std::abs(nLayerDiff) >= 3) {
1192 return nLayerDiff > 0;
1193 }
1194 return a.getMeanResidual2() < b.getMeanResidual2();
1195}
static Double_t a

◆ 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}

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

409 {
411 if (pat.nTriggerLayers < m_cfg.minTriggerLayers ||
412 pat.nPrecisionLayers < m_cfg.minPrecisionLayers ||
413 std::ranges::count_if(pat.nMeasurementLayers,
414 [this](const uint8_t nLayers) { return nLayers >= m_cfg.minStationLayers; }) < 2) {
415 ATH_MSG_VERBOSE(__func__<<"() Pattern " << detailed(pat) << "\ndoes not meet minimum layer requirements - reject.");
416 return false;
417 }
419 if (pat.meanNormResidual2 > m_cfg.meanNormRes2Cut) {
420 ATH_MSG_VERBOSE(__func__<<"() Pattern " << detailed(pat) << "\ndoes not meet the mean norm residual2 cut - reject.");
421 return false;
422 }
423 return true;
424}

◆ 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 50% of the hits of the smaller pattern are shared

Determine best pattern

Definition at line 426 of file GlobalPatternFinder.cxx.

427 {
428 PatternStateVec outputPatterns{};
429 outputPatterns.reserve(toResolve.size());
431 auto areOverlapping = [this](const PatternState& a, const PatternState& b) {
433 if(!a.expSect.isNeighbour(b.expSect)) {
434 return false;
435 }
436 if (std::abs(a.theta - b.theta) > 2.*m_cfg.thetaSearchWindow) {
437 return false;
438 }
439 if (a.nPhiLayers > 0 && b.nPhiLayers > 0) {
440 if (std::abs(CxxUtils::deltaPhi(a.phi, b.phi)) > 2.*m_cfg.phiTolerance) {
441 return false;
442 }
443 } else if (a.nPhiLayers > 0) {
444 if (!sectorMap.insideSector(b.expSect.msSector(), a.phi) ||
445 !sectorMap.insideSector(b.expSect.adjacentMsSector(), a.phi)) {
446 return false;
447 }
448 } else if (b.nPhiLayers > 0) {
449 if (!sectorMap.insideSector(a.expSect.msSector(), b.phi) ||
450 !sectorMap.insideSector(a.expSect.adjacentMsSector(), b.phi)) {
451 return false;
452 }
453 }
455 int nSharedHits{0};
456 for (std::size_t st{0u}; st < s_nStations; ++st) {
457 const auto& hitsA {a.hitsPerStation[st]};
458 const auto& hitsB {b.hitsPerStation[st]};
459 if (hitsA.empty() || hitsB.empty()) {
460 continue;
461 }
462 nSharedHits += std::ranges::count_if(hitsA, [&](const CandidateHit& hitA){
463 return std::ranges::any_of(hitsB, [&hitA](const CandidateHit& hitB) {
464 return hitA.sp()->primaryMeasurement() == hitB.sp()->primaryMeasurement();
465 });
466 });
467 }
469 const int minHits {std::min(a.nBendingHits(), b.nBendingHits())};
470 return nSharedHits >= 0.5 *minHits;
471 };
473 auto isBetterOverlap = [](const PatternState& a, const PatternState& b) {
474 const int nGoodStationDiff {a.nStations(/*onlyGoodStations=*/ true) - b.nStations(/*onlyGoodStations=*/ true)};
475 if (nGoodStationDiff != 0) {
476 return nGoodStationDiff > 0;
477 }
478 return isBetter(a,b);
479 };
480
481 for (auto it = toResolve.begin(); it != toResolve.end(); ++it) {
482 if (it->isOverlap) {
483 // If already marked as overlap, add to visual info, and discard the pattern
485 continue;
486 }
487 for (auto jt = std::next(it); jt != toResolve.end(); ++jt) {
488 if (jt->isOverlap || !areOverlapping(*it, *jt)) {
489 continue;
490 }
491 if (isBetterOverlap(*it, *jt)) {
492 ATH_MSG_VERBOSE(__func__<<"() Pattern "<<detailed(*it)<<"\nis BETTER than\n"<<detailed(*jt));
493 jt->isOverlap = true;
494 } else {
495 it->isOverlap = true;
496 ATH_MSG_VERBOSE(__func__<<"() Pattern "<<detailed(*jt)<<"\nis BETTER than\n"<<detailed(*it));
497 break;
498 }
499 }
500 if (!it->isOverlap) {
501 outputPatterns.push_back( std::move(*it));
502 } else {
503 // If overlap, add to visual info, as the pattern will be discarded
505 }
506 }
507 ATH_MSG_VERBOSE(__func__<<"() Patterns surviving overlap removal: "<< outputPatterns.size());
508 return outputPatterns;
509}
static bool isBetter(const PatternState &a, const PatternState &b)
Method to compare two patterns and define which one is better.
T deltaPhi(T phiA, T phiB)
Return difference phiA - phiB in range [-pi, pi].
Definition phihelper.h:42

◆ 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}

◆ operator<<

std::ostream & operator<< ( std::ostream & os,
const PatternPrintView & v )
friend

Definition at line 1321 of file GlobalPatternFinder.cxx.

1321 {
1322 v.pat.print(os, v.detailed);
1323 return os;
1324}

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 415 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 413 of file GlobalPatternFinder.h.

413{};

◆ s_nStations

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

Definition at line 104 of file GlobalPatternFinder.h.

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

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