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 340 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 137 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 398 of file GlobalPatternFinder.h.

398 : std::int8_t{
399 eSameLayer,
400 eLowerLayer,
401 eHigherLayer
402 };

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

175 : std::int8_t{
177 eAddHit,
179 eBranchPattern,
181 eRejectHit,
183 eConsecutiveMdt,
185 eOverwriteLastHit
186 };

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

139 : std::uint8_t{
141 eSector,
143 eTheta
144 };

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

38 :
40 m_cfg{config} {
41 static_assert(std::is_move_assignable_v<PatternState>);
42 static_assert(std::is_move_constructible_v<PatternState>);
43 static_assert(std::is_copy_assignable_v<PatternState>);
44 static_assert(std::is_copy_constructible_v<PatternState>);
45 static_assert(std::is_nothrow_move_constructible_v<PatternState>);
46 static_assert(std::is_nothrow_move_assignable_v<PatternState>);
47};
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
stationStation index of the projection model
patPositionPattern line position in its phi plane
patDirectionPattern line direction in its phi plane
isValidWhether the projection is valid

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

Definition at line 496 of file GlobalPatternFinder.cxx.

497 {
498 constexpr auto covIdxEta {Acts::toUnderlying(SpacePoint::CovIdx::etaCov)};
504 struct PhiStripProjectionModel {
505 StIndex station{};
506 Amg::Vector3D patPosition{Amg::Vector3D::Zero()};
507 Amg::Vector3D patDirection{Amg::Vector3D::Zero()};
508 bool isValid{false};
509
510 double residual(const Amg::Vector3D& stripPos, const Amg::Vector3D& stripDir) const {
511 return Acts::detail::LineHelper::lineIntersect<3>(
512 patPosition, patDirection, stripPos, stripDir).pathLength();
513 }
514 };
515 auto makeProjectionModel = [this](PatternState& pat, const StIndex station) {
516 PhiStripProjectionModel result{};
517 result.station = station;
518 const std::vector<CandidateHit>& stationHits {
519 pat.hitsPerStation[Acts::toUnderlying(station)]};
520 if (stationHits.empty()) return result;
521
522 const HitPayload* sp1 {nullptr};
523 const HitPayload* sp2 {nullptr};
524 if (pat.nMeasurementLayers[Acts::toUnderlying(station)] > 1) {
525 // if we have >= 2 eta hits in the station, we use the furthestmost to define the pattern line
526 const auto [minIt, maxIt] {std::ranges::minmax_element(stationHits, {},
527 [](const CandidateHit& c){ return c.globLayer; })};
528 sp1 = minIt->hit;
529 sp2 = maxIt->hit;
530 }
531 if (!sp1 || !sp2 ||
532 layerDistance(station, pat.projToPhiPlane(*sp1), pat.projToPhiPlane(*sp2)) < m_cfg.minHitDistance4Line) {
533 // if we have only one eta hit or the layer separation is too small, to find the second hit
534 // we use the functionality of anchor hit
535 pat.moveLineAnchorHit(stationHits.front());
536 sp1 = stationHits.front().hit;
537 sp2 = pat.lineAnchorHit.hit;
538 }
539 if (!sp1 || !sp2 ) return result;
540
541 Amg::Vector3D pos1 {pat.projToPhiPlane(*sp1)};
542 Amg::Vector3D pos2 {pat.projToPhiPlane(*sp2)};
543
544 result.patPosition = pos1;
545 result.patDirection = (pos2 - pos1).unit();
546 result.isValid = true;
547 return result;
548 };
549
550 PatternStateVec survivingPatterns{};
551 survivingPatterns.reserve(patterns.size());
552 for (PatternState& pat : patterns) {
554 ATH_MSG_VERBOSE(__func__<<"() Search for phi-only hits for pattern: " << brief(pat));
555
556 // Projection model of pattern line onto a given phi strip
557 std::optional<PhiStripProjectionModel> patProjOnStrip{};
558 bool stopSearch {false};
559 for (const SpacePointBucket* bucket : pat.getParentBuckets()) {
560 if (stopSearch) break;
561
562 const Amg::Transform3D& localToGlobal {bucket->msSector()->localToGlobalTransform(gctx)};
563 const StIndex station {m_cfg.idHelperSvc->stationIndex(bucket->front()->identify())};
564 // If the projection model is not valid, we will use the pattern theta. We cache the local Y in glob frame
565 const Amg::Vector3D locY {localToGlobal.linear() * Amg::Vector3D::UnitY()};
566
567 for (const auto& hit : *bucket) {
568 if (pat.nPhiLayers >= m_cfg.minPhiLayers) {
569 stopSearch = true;
570 break;
571 }
572 // We are looking for phi-only hits
573 if (hit->measuresEta()){
574 continue;
575 }
576 ATH_MSG_VERBOSE(__func__<<"() *** Test phi-only hit "<<*hit);
577 // Check phi compatibility
578 const Amg::Vector3D locPosTest {hit->localPosition()};
579 const Amg::Vector3D globPosTest {localToGlobal * locPosTest};
580 const double globPhi {globPosTest.phi()};
581 if (!pat.isPhiCompatible(globPhi)) {
582 ATH_MSG_VERBOSE(__func__<<"() Phi-only hit not compatible");
583 continue;
584 }
585 // Check there are not other phi hits in the same layer
586 const uint8_t layNum = m_spSorter.sectorLayerNum(*hit);
587 const std::vector<CandidateHit>& stationHits {
588 pat.hitsPerStation[Acts::toUnderlying(station)]};
589 assert(!stationHits.empty());
590 if (std::ranges::any_of(stationHits, [&](const CandidateHit& h){
591 return h.sp()->measuresPhi() && hit->msSector() == h.sp()->msSector() && layNum == h->locLayer; }) ||
592 std::ranges::any_of(pat.phiOnlyHits, [&](const HitPayload& h){
593 return station == h.station && hit->msSector() == h->msSector() && layNum == h.locLayer; })) {
594 ATH_MSG_VERBOSE(__func__<<"() The pattern already has a phi hit in the same layer - skip hit.");
595 continue;
596 }
597 // Check eta compatibility.
598 if (!patProjOnStrip.has_value() || patProjOnStrip->station != station) {
599 patProjOnStrip = makeProjectionModel(pat, station);
600 }
601 if (!patProjOnStrip->isValid) {
602 ATH_MSG_VERBOSE(__func__<<"() Invalid projection model for station "<<station<<" - skip hit.");
603 continue;
604 }
605 const Amg::Vector3D stripDir {localToGlobal.linear() * hit->sensorDirection()};
606 const double stripHalfLength {std::sqrt(hit->covariance()[covIdxEta])};
607 ATH_MSG_VERBOSE(__func__<<"() Distance pattern line from strip center: "
608 <<patProjOnStrip->residual(globPosTest, stripDir)<<", strip half-length: "<<stripHalfLength);
609
610 if (patProjOnStrip->residual(globPosTest, stripDir) > 1.1*stripHalfLength) {
611 ATH_MSG_VERBOSE(__func__<<"() The pattern falls outside the test hit strip in eta - skip hit.");
612 continue;
613 }
614 // Create the hit payload and add the hit to the pattern. Save only relevant quantities for phi-only hits.
615 ATH_MSG_VERBOSE(__func__<<"() Phi-only hit compatible - add it to the pattern.");
616 pat.phiOnlyHits.emplace_back(hit.get(), /*bucket*/nullptr, /*container*/nullptr, globPosTest, Amg::Vector3D::Zero(),
617 station, layNum, /*sector*/0u, /*isPrecision*/false, /*isStraw*/false);
618
619 if (pat.nPhiLayers == 0) pat.updatePatternPhi(globPhi);
620 pat.nPhiLayers++;
621 }
622 }
623 if (pat.nPhiLayers < m_cfg.minPhiLayers) {
624 ATH_MSG_VERBOSE(__func__<<"() Pattern "<< detailed(pat)<<" has only "<<pat.nPhiLayers
625 <<" phi layers, below the minimum required - reject this pattern.");
626 continue;
627 }
628 pat.finalizePatternPhi();
629 survivingPatterns.push_back(std::move(pat));
630 }
631 std::swap(patterns, survivingPatterns);
632}
const PlainObject unit() const
This is a plugin that makes Eigen look like CLHEP & defines some convenience methods.
#define ATH_MSG_VERBOSE(x)
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
@ 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 1236 of file GlobalPatternFinder.cxx.

1238 {
1239 if (!visualInfo) {
1240 return;
1241 }
1242 // First save the buckets
1243 std::vector<const SpacePointBucket*> buckets{cache.getParentBuckets()};
1244
1245 GlobalPattern pattern {convertToPattern(cache)};
1246 // Check whether the visual info about this pattern is already in the container
1247 if (auto it =std::ranges::find_if(*visualInfo, [&pattern](const auto& v){
1248 return v.patternCopy && *v.patternCopy == pattern; }); it != visualInfo->end()) {
1249 it->status = status; // Update the status if the pattern is already in the container
1250 return;
1251 }
1252 visualInfo->push_back(*cache.visualInfo);
1253
1254 std::ranges::copy(buckets, std::back_inserter(visualInfo->back().parentBuckets));
1255
1256 visualInfo->back().patternCopy = std::make_unique<GlobalPattern>(std::move(pattern));
1257 visualInfo->back().status = status;
1258}
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 1221 of file GlobalPatternFinder.cxx.

1222 {
1223 if (!hit1.isStraw || !hit2.isStraw) {
1224 return false;
1225 }
1226 const uint16_t tubeNum1 {static_cast<const xAOD::MdtDriftCircle*>(hit1.sp()->primaryMeasurement())->driftTube()};
1227 const uint16_t tubeNum2 {static_cast<const xAOD::MdtDriftCircle*>(hit2.sp()->primaryMeasurement())->driftTube()};
1231 if(tubeNum1 == tubeNum2) {
1232 return false;
1233 };
1234 return std::abs(tubeNum1-tubeNum2) < 2;
1235 }
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 1291 of file GlobalPatternFinder.cxx.

1291 {
1292 return {p, /*detailed=*/false};
1293}

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

1166 {
1167 auto getLayerOrdering = [](const bool isLayer1Lower) {
1168 return isLayer1Lower ? eLowerLayer : eHigherLayer;
1169 };
1170 if (hit1 == hit2) {
1171 return eSameLayer;
1172 }
1174 if (hit1->msSector() == hit2->msSector()) {
1175 if (hit1.locLayer == hit2.locLayer) {
1176 return eSameLayer;
1177 } else {
1178 return getLayerOrdering(hit1.locLayer < hit2.locLayer);
1179 }
1180 }
1181 StIndex st1 {hit1.station};
1182 StIndex st2 {hit2.station};
1184 if (st1 == st2) {
1185 return getLayerOrdering(isBarrel(st1)
1186 ? hit1.position.perp() < hit2.position.perp()
1187 : std::abs(hit1.position.z()) < std::abs(hit2.position.z()));
1188 }
1189 LayerIndex layer1 {toLayerIndex(st1)};
1190 LayerIndex layer2 {toLayerIndex(st2)};
1191 if (layer1 == layer2) {
1193 if (layer1 == LayerIndex::Middle) {
1195 return getLayerOrdering(st1 == StIndex::BM);
1196 }
1197 if (layer1 == LayerIndex::Inner) {
1199 return getLayerOrdering(hit1.position.perp() < hit2.position.perp());
1200 }
1201 throw std::runtime_error("Unexpected to have two pattern-compatible hits one in BO and the other in EO.");
1202 }
1203 if (layer1 == LayerIndex::Inner || layer2 == LayerIndex::Inner) {
1205 return getLayerOrdering(layer1 == LayerIndex::Inner);
1206 }
1207 if (layer1 == LayerIndex::Outer || layer2 == LayerIndex::Outer) {
1209 return getLayerOrdering(layer2 == LayerIndex::Outer);
1210 }
1211 if (layer1 == LayerIndex::BarrelExtended || layer2 == LayerIndex::BarrelExtended) {
1213 return getLayerOrdering(layer1 == LayerIndex::BarrelExtended);
1214 }
1216 if (layer1 == LayerIndex::Extended) {
1217 return getLayerOrdering(st2 == StIndex::EM);
1218 }
1219 return getLayerOrdering(st1 == StIndex::BM);
1220}
Muon::MuonStationIndex::LayerIndex LayerIndex
Type alias for the station layer index.
bool isBarrel(const ChIndex index)
Returns true if the chamber index points to a barrel chamber.
LayerIndex toLayerIndex(ChIndex index)
convert ChIndex into LayerIndex

◆ constructTree()

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

682 {
683 SearchTree_t::vector_t rawData{};
684 using SectorProjector = ExpandedSector::SectorProjector;
685 using enum SectorProjector;
686 // Before the loops: estimate the total number of hits
687 size_t totalHits = 0;
688 for (const SpacePointContainer* spc : spacepoints) {
689 for (const SpacePointBucket* bucket : *spc) {
690 totalHits += bucket->size();
691 }
692 }
693 // We can have up to 3 entries per hit (when the hit does not measure phi).
694 rawData.reserve(3 * totalHits);
695
696 for (const SpacePointContainer* spc : spacepoints) {
697 ATH_MSG_VERBOSE(__func__<<"() Processing "<<spc->size()<<" space point buckets...");
698 for (const SpacePointBucket* bucket : *spc) {
699 ATH_MSG_VERBOSE(__func__<<"() Processing " << bucket->size() << " spacepoints...");
700 const Amg::Transform3D& localToGlobal {bucket->msSector()->localToGlobalTransform(gctx)};
701 const Acts::SquareMatrix<3> rotation {localToGlobal.linear()};
702 const StIndex bucketStation {m_cfg.idHelperSvc->stationIndex(bucket->front()->identify())};
703 const uint8_t sector = bucket->msSector()->sector();
704
705 for (const auto& hit : *bucket) {
706 // Ignore only-phi hits and MDT hits if desired
707 const bool isStraw {hit->isStraw()};
708 if (!hit->measuresEta() || (!m_cfg.useMdtHits && isStraw)) {
709 continue;
710 }
711 ATH_MSG_VERBOSE(__func__<<"() Spacepoint: " << *hit);
712 const Amg::Vector3D globalPos {localToGlobal * hit->localPosition()};
713 const Amg::Vector3D globWireDir {rotation * hit->sensorDirection()};
714 const ExpandedSector hitExpSector {globalPos.phi()};
715
718 for (const SectorProjector proj : {leftOverlap, center, rightOverlap}) {
720 const ExpandedSector expSect {sector, proj};
721 if (proj != SectorProjector::center && hit->measuresPhi() && expSect != hitExpSector) {
722 ATH_MSG_VERBOSE(__func__<<"() Hit with "<<hitExpSector<<" is not compatible with "<<expSect);
723 continue;
724 }
725
726 /* Project the hit onto the plane along the sector radial direction.
727 * This allows to remove the bias of hit displacement in phi direction */
728 const Amg::Vector3D planeNormal {expSect.normalDir()};
729 const double projR {hit->measuresPhi() ? globalPos.perp() : (globalPos - globalPos.dot(planeNormal) * planeNormal).perp()};
730
731 std::array<double, 2> coords{};
732 coords[Acts::toUnderlying(SeedCoords::eTheta)] = atan2(projR, globalPos.z());
733 coords[Acts::toUnderlying(SeedCoords::eSector)] = expSect.sector();
734
735 ATH_MSG_VERBOSE(__func__<<"() Add hit: Z: " << globalPos.z() << ", R: " << globalPos.perp()
736 <<", ProjR: " << globalPos.perp()<< ", Phi: "<< inDeg(globalPos.phi())
737 <<", SectorPhi: "<< inDeg(expSect.phi())<<" and coordinates "<<coords<<" to search tree");
738 rawData.emplace_back(std::move(coords), HitPayload{hit.get(), bucket, spc, globalPos, globWireDir, bucketStation,
739 static_cast<uint8_t>(m_spSorter.sectorLayerNum(*hit)), sector, isPrecisionHit(*hit), isStraw});
740 }
741 }
742 }
743 }
744 ATH_MSG_VERBOSE(__func__<<"() Create a new tree with "<<rawData.size()<<" entries. ");
745 return SearchTree_t{std::move(rawData)};
746}
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.
constexpr float inDeg(const float rad)

◆ convertToPattern() [1/2]

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

Method to convert a PatternState into a GlobalPattern object.

Parameters
candidatePatternState to be converted
Returns
: Converted GlobalPattern

Add eta hits

Add phi-only hits

Definition at line 633 of file GlobalPatternFinder.cxx.

633 {
634 GlobalPattern::HitCollection hitPerStation{};
635 GlobalPattern::BucketCollection bucketPerStation{};
637 for (uint8_t st{0u}; st < s_nStations; ++st) {
638 const auto& hits {cache.hitsPerStation[st]};
639 if (hits.empty()) continue;
640
641 auto& outHits {hitPerStation[static_cast<StIndex>(st)]};
642 outHits.reserve(hits.size());
643 auto& outBuckets {bucketPerStation[static_cast<StIndex>(st)]};
644
645 std::ranges::for_each(hits, [&outHits, &outBuckets](const CandidateHit& h){
646 outHits.push_back(h.sp());
647 if (std::ranges::find(outBuckets, h->bucket) == outBuckets.end()) {
648 outBuckets.push_back(h->bucket);
649 }
650 });
651 }
653 for (const HitPayload& hit : cache.phiOnlyHits) {
654 hitPerStation[static_cast<StIndex>(hit.station)].push_back(hit.sp());
655 }
656 GlobalPattern pattern{std::move(hitPerStation), std::move(bucketPerStation)};
657 pattern.setTheta(cache.seedHit->position.theta());
658 pattern.setPhi(cache.patPhi);
659 // Set the pattern sector(s) and theta.
660 pattern.setSector(cache.expSect.sector());
661 // Set pattern quality information.
662 pattern.setNPrecisionLayers(cache.nPrecisionLayers);
663 pattern.setNTriggerLayers(cache.nTriggerLayers);
664 pattern.setNPhiLayers(cache.nPhiLayers);
665 pattern.setMeanNormResidual2(cache.getMeanResidual2());
666 return pattern;
667}
std::unordered_map< StIndex, std::vector< HitType > > HitCollection
std::unordered_map< StIndex, std::vector< const SpacePointBucket * > > BucketCollection
m_data push_back(elt)
@ 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 670 of file GlobalPatternFinder.cxx.

670 {
672 patterns.reserve(cache.size());
673 std::transform(cache.begin(), cache.end(), std::back_inserter(patterns),
674 [this](const PatternState& cacheEntry) {
675 return convertToPattern(cacheEntry);
676 });
677 return patterns;
678}
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 1295 of file GlobalPatternFinder.cxx.

1295 {
1296 return {p, /*detailed=*/true};
1297}

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

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

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

Check angular compatibility of the test hit and the pattern

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

Add the new pattern to the list of next patterns

Update visual information of the original pattern

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

Update visual information

Definition at line 270 of file GlobalPatternFinder.cxx.

274 {
275 endPatterns.clear();
276 ATH_MSG_VERBOSE(__func__<<"() *** Test "<<testHit<<" against " << startPatterns.size() << " active patterns.");
277
278 // Compute the minimum number of missed layer hits among the active patterns,
279 // to use as reference for pruning patterns with too many missed layers.
280 std::vector<unsigned> missedLayersVec{};
281 missedLayersVec.reserve(startPatterns.size());
282 std::ranges::transform(startPatterns, std::back_inserter(missedLayersVec),
283 [&testHit](const PatternState& pat){
284 return std::abs(pat.lastInsertedHit.globLayer - testHit.globLayer);
285 });
286 const unsigned minMissedLayers {std::ranges::min(missedLayersVec)};
287
288 const bool shouldPrune {startPatterns.size() > 1 &&
289 std::ranges::any_of(startPatterns, [](const PatternState& p){
290 return p.nBendingLayers() > 2; })};
291
292 for (auto [i, pat] : Acts::enumerate(startPatterns)) {
293 if (pat.isOverlap) {
295 continue;
296 }
298 if (pat.lastInsertedHit.station == testHit.station &&
299 missedLayersVec[i] > std::max(m_cfg.maxMissLayersInStation, minMissedLayers)) {
300 ATH_MSG_VERBOSE(__func__<<"() Pattern " << detailed(pat) << "\nhas missed " << (int)missedLayersVec[i]
301 << " layer hits, above the max allowed - abort pattern.");
303 continue;
304 }
308 if (shouldPrune && pat.lastInsertedHit.globLayer != testHit.globLayer &&
309 std::ranges::find_if(std::next(startPatterns.begin(), i + 1), startPatterns.end(), [&](PatternState& p){
310 if (p.lastInsertedHit != pat.lastInsertedHit || p.isOverlap) return false;
311
312 if (isBetter(pat, p)) {
313 ATH_MSG_VERBOSE("extendPatterns() Pruning: "<<detailed(pat)<<"\nis BETTER than "<<detailed(p));
314 p.isOverlap = true;
315 return false;
316 }
317 ATH_MSG_VERBOSE("extendPatterns() Pruning: "<<detailed(p)<<"\nis BETTER than "<<detailed(pat));
318 return true; }) != startPatterns.end()) {
320 continue;
321 }
323 const auto [result, residual, accWindow] {pat.checkLineComp(testHit, beamSpot)};
324 switch (result) {
326 if (accWindow > 4.*m_cfg.baseResidualSigma && residual > m_cfg.baseResidualSigma) {
330 if (std::ranges::any_of(endPatterns, [&testHit, &pat](const PatternState& p) {
331 return p.isInLastLayer(testHit) &&
332 (p.prevLayerHit == pat.lastInsertedHit || p.nBendingLayers() > (pat.nBendingLayers() + 1u)); })) {
333 ATH_MSG_VERBOSE(__func__<<"() Low-confidence hit: forking leads to existing pattern - reject.");
334 break;
335 }
336 ATH_MSG_VERBOSE(__func__<<"() Low-confidence hit: forking leads to new pattern - fork.");
338 endPatterns.push_back(pat);
339 endPatterns.back().addHit(testHit, residual, accWindow);
341 if (visualInfo) {
342 pat.visualInfo->discardedHits.push_back(testHit.sp());
343 }
344 ATH_MSG_VERBOSE("New pattern: " << brief(endPatterns.back()));
345 break;
346 }
347 ATH_MSG_VERBOSE(__func__<<"() Hit compatible - add to pattern.");
348 pat.addHit(testHit, residual, accWindow);
349 break;
350 }
352 /* Check first if the branched pattern already exists*/
353 if (std::ranges::any_of(endPatterns, [&testHit, &pat](const PatternState& p) {
354 return p.isInLastLayer(testHit) && p.prevLayerHit == pat.prevLayerHit; })) {
355 ATH_MSG_VERBOSE(__func__<<"() Hit compatible & on same layer of last added hit - branched pattern already exists.");
356 break;
357 }
359 ATH_MSG_VERBOSE(__func__<<"() Hit compatible & on same layer of last added hit - branch pattern.");
360 endPatterns.push_back(pat);
361 endPatterns.back().overWriteHit(testHit, residual, accWindow);
362
364 if (visualInfo) {
365 pat.visualInfo->discardedHits.push_back(testHit.sp());
366 }
367 ATH_MSG_VERBOSE("New pattern: " << brief(endPatterns.back()));
368 break;
369 }
371 ATH_MSG_VERBOSE(__func__<<"() Hit is not compatible with the pattern - reject hit.");
372 if (visualInfo) {
373 pat.visualInfo->discardedHits.push_back(testHit.sp());
374 }
375 break;
376 }
378 ATH_MSG_VERBOSE(__func__<<"() Compatible MDT hits on same layer - accept.");
379 /* For consecutive MDT hits we don't add their residuals to not penalize patterns with many such hits */
380 pat.addHit(testHit, -1., -1.);
381 break;
382 }
384 ATH_MSG_VERBOSE(__func__<<"() Hit compatible & on same layer of last added hit - overwrite last hit.");
385 pat.overWriteHit(testHit, residual, accWindow);
386 break;
387 }
388 }
389 endPatterns.push_back(std::move(pat));
390 }
391 startPatterns.clear();
392};
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.

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

53 {
56 const SearchTree_t orderedSpacepoints {constructTree(gctx, spacepoints)};
57 auto visualInfo {m_cfg.visionTool ? std::make_unique<PatternHitVisualInfoVec>() : nullptr};
58 PatternStateVec patterns{findPatternsInEta(orderedSpacepoints, visualInfo.get())};
59
62
63 for (const PatternState& pat : patterns) {
66
68 for (const std::vector<CandidateHit>& hits : pat.hitsPerStation) {
69 for (const auto& hit : hits) {
70 if (outBuckets.find(hit->container) == outBuckets.end()) {
71 throw std::runtime_error("The space point container associated to the pattern is not present in the output bucket map.");
72 }
73 auto& outBucketVec = outBuckets[hit->container];
74 if (std::ranges::find(outBucketVec, hit->bucket) == outBucketVec.end()) {
75 outBucketVec.push_back(hit->bucket);
76 }
77 }
78 }
79 }
81 if (visualInfo) {
82 m_cfg.visionTool->plotPatternBuckets(Gaudi::Hive::currentContext(), "GlobPatFind_", std::move(*visualInfo));
83 }
85}
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 87 of file GlobalPatternFinder.cxx.

88 {
89 constexpr auto thetaIdx {Acts::toUnderlying(SeedCoords::eTheta)};
90 constexpr auto sectorIdx {Acts::toUnderlying(SeedCoords::eSector)};
91
93 PatternStateVec startPatternBuff{};
94 startPatternBuff.reserve(20);
95 PatternStateVec endPatternBuff{};
96 endPatternBuff.reserve(20);
97
99 const Amg::Vector3D beamSpot{Amg::Vector3D::Zero()};
100
101 PatternStateVec outPatterns{};
102 outPatterns.reserve(40);
107 auto countPatterns = [this](const PatternStateVec& patterns,
108 const HitPayload& hit,
109 const SearchTree_t::coordinate_t& coords) -> uint8_t {
110 return std::ranges::count_if(patterns, [&](const PatternState& pattern){
111 const double patSeedTheta {pattern.seedHit->position.theta()};
112 if (std::abs(patSeedTheta - coords[thetaIdx]) > 2.*m_cfg.thetaSearchWindow ||
113 !pattern.expSect.isNeighbour(ExpandedSector{static_cast<std::int8_t>(coords[sectorIdx])})) {
114 return false;
115 }
116 return pattern.isInPattern(hit);
117 });
118 };
119 using enum SeedCoords;
120 for (const auto seedingLayer : m_cfg.layerSeedings) {
122 for (const auto& [seedCoords, seed] : orderedSpacepoints) {
124 const LayerIndex seedLayer {toLayerIndex(seed.station)};
125 if (seedLayer != seedingLayer || (seed.isStraw && !m_cfg.seedFromMdt)) {
126 continue;
127 }
128 ATH_MSG_VERBOSE(__func__<<"() New seed hit "<<*seed<<", coordinates "<<seedCoords);
130 uint8_t nExistingPatterns {countPatterns(outPatterns, seed, seedCoords)};
131 if (nExistingPatterns >= m_cfg.maxSeedAttempts) {
132 // Try first to resolve overlaps and re-count the number of patterns containing the seed
133 outPatterns = resolveOverlaps(outPatterns, visualInfo);
134 nExistingPatterns = countPatterns(outPatterns,seed, seedCoords);
135 if (nExistingPatterns >= m_cfg.maxSeedAttempts) {
136 ATH_MSG_VERBOSE(__func__<<"() Seed has already been used in "<<nExistingPatterns<<" patterns, which is above the limit - skip this seed.");
137 continue;
138 }
139 }
141 SearchTree_t::range_t selectRange{};
143 selectRange[sectorIdx].shrink(seedCoords[sectorIdx] - 0.1, seedCoords[sectorIdx] + 0.1);
147 const double thetaHalfWindow {(seedLayer == LayerIndex::Inner || seedLayer == LayerIndex::Outer)
148 ? m_cfg.thetaSearchWindow : 0.5*m_cfg.thetaSearchWindow};
149 selectRange[thetaIdx].shrink(seedCoords[thetaIdx] - thetaHalfWindow, seedCoords[thetaIdx] + thetaHalfWindow);
151 std::vector<CandidateHit> candidateHits{};
152 orderedSpacepoints.rangeSearchMapDiscard(selectRange, [&candidateHits](const SearchTree_t::coordinate_t& /*coords*/,
153 const HitPayload& hit){
154 candidateHits.emplace_back(&hit, hit.station, 0u, hit.sector, hit.isStraw);
155 });
156 if (candidateHits.size() < m_cfg.minTriggerLayers + m_cfg.minPrecisionLayers) {
157 ATH_MSG_VERBOSE(__func__<<"() Found "<<candidateHits.size()<<" candidate hits, below minimum required - skip seed.");
158 continue;
159 }
161 if (std::ranges::none_of(candidateHits, [this, seedLayer](const CandidateHit& c){
162 return m_cfg.idHelperSvc->layerIndex(c.sp()->identify()) != seedLayer; }) ) {
163 ATH_MSG_VERBOSE(__func__<<"() All candidates in same station layer, and we need at least two - skip seed.");
164 continue;
165 }
167 std::ranges::sort(candidateHits, [](const CandidateHit& c1, const CandidateHit& c2){
169 if (ordering == eSameLayer) {
171 return c1.sp()->localPosition().y() < c2.sp()->localPosition().y();
172 }
173 return ordering == eLowerLayer;
174 });
176 for (std::size_t i {1}; i < candidateHits.size(); ++i) {
177 candidateHits[i].globLayer = candidateHits[i - 1].globLayer +
178 (checkLayerOrdering(*candidateHits[i - 1], *candidateHits[i]) != eSameLayer);
179 }
180 if (candidateHits.back().globLayer + 1u < (m_cfg.minTriggerLayers + m_cfg.minPrecisionLayers)) {
181 ATH_MSG_VERBOSE(__func__<<"() Found "<<candidateHits.size()<<" candidate hits on "<<candidateHits.back().globLayer + 1u
182 <<" layers, below the minimum required - skip this seed.");
183 continue;
184 }
185 if (msgLvl(MSG::VERBOSE)) {
186 ATH_MSG_VERBOSE(__func__<<"() Found "<< candidateHits.size()<<" candidate hits: ");
187 for (const auto& c : candidateHits) {
188 ATH_MSG_VERBOSE(__func__<<"() \t**"<<c);
189 }
190 }
191
193 const auto seedItr {std::ranges::find_if(candidateHits,
194 [&seed](const CandidateHit& c){ return *c == seed; })};
195 assert(seedItr != candidateHits.end());
196 const CandidateHit& seedCand {*seedItr};
197
198 PatternState patternSeed{seedCand, static_cast<std::int8_t>(seedCoords[sectorIdx]), &m_cfg, this};
199 if (visualInfo) {
200 patternSeed.visualInfo = std::make_unique<PatternHitVisualInfo>(
201 seed.hit, seedCoords[thetaIdx] - thetaHalfWindow, seedCoords[thetaIdx] + thetaHalfWindow);
202 }
203
212 auto processHitRange = [&](const auto begin,
213 const auto end,
214 PatternState&& toExtend) -> PatternStateVec {
215 startPatternBuff.clear();
216 startPatternBuff.push_back(std::move(toExtend));
217
218 for (auto testItr = begin; testItr != end; ++testItr) {
219 const CandidateHit& testHit {*testItr};
220 if (testHit.globLayer == seedCand.globLayer && !seedCand.isStraw) {
221 continue; // skip hits on the same layer as the seed, if straw
222 }
223 extendPatterns(startPatternBuff, endPatternBuff, testHit, beamSpot, visualInfo);
224 // Swap the buffers for the next iteration
225 std::swap(startPatternBuff, endPatternBuff);
226 }
227 return startPatternBuff.size() > 1
228 ? resolveOverlaps(startPatternBuff, visualInfo)
229 : PatternStateVec{std::move(startPatternBuff.back())};
230 };
231
233 PatternStateVec forwardExtended {processHitRange(std::next(seedItr), candidateHits.end(), std::move(patternSeed))};
234
236 ATH_MSG_VERBOSE(__func__<<"() Finished forward search, found "<<forwardExtended.size()<<" forward patterns, start backward search.");
237 PatternStateVec backwardExtended{};
238 backwardExtended.reserve(2*forwardExtended.size());
239
240 for (PatternState& pat : forwardExtended) {
241 pat.moveLineAnchorHit(seedCand);
242 pat.lastInsertedHit = seedCand;
243
245 std::ranges::move(
246 processHitRange(std::reverse_iterator(seedItr), candidateHits.rend(), std::move(pat)),
247 std::back_inserter(backwardExtended)
248 );
249 }
251 if (backwardExtended.size() > 1) {
252 backwardExtended = resolveOverlaps(backwardExtended, visualInfo);
253 }
254
255 for (PatternState& pat : backwardExtended) {
256 pat.meanNormResidual2 /= pat.nBendingLayers();
257 if (!passPatternCuts(pat)) {
259 continue;
260 }
261 ATH_MSG_VERBOSE(__func__<<"() Add new pattern "<<detailed(pat));
262 pat.isFinalized = true;
263 outPatterns.push_back(std::move(pat));
264 }
265 }
266 }
267 ATH_MSG_VERBOSE(__func__<<"() Found in total "<<outPatterns.size()<<" patterns in eta before overlap removal");
268 return resolveOverlaps(outPatterns, visualInfo);
269}
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
constexpr auto thetaIdx
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 1157 of file GlobalPatternFinder.cxx.

1157 {
1158 const int nLayerDiff {a.nBendingLayers() - b.nBendingLayers()};
1159 if (std::abs(nLayerDiff) >= 3) {
1160 return nLayerDiff > 0;
1161 }
1162 return a.getMeanResidual2() < b.getMeanResidual2();
1163}
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 393 of file GlobalPatternFinder.cxx.

393 {
395 if (pat.nTriggerLayers < m_cfg.minTriggerLayers ||
396 pat.nPrecisionLayers < m_cfg.minPrecisionLayers ||
397 std::ranges::count_if(pat.nMeasurementLayers,
398 [this](const uint8_t nLayers) { return nLayers >= m_cfg.minStationLayers; }) < 2) {
399 ATH_MSG_VERBOSE(__func__<<"() Pattern " << detailed(pat) << "\ndoes not meet minimum layer requirements - reject.");
400 return false;
401 }
403 if (pat.meanNormResidual2 > m_cfg.meanNormRes2Cut) {
404 ATH_MSG_VERBOSE(__func__<<"() Pattern " << detailed(pat) << "\ndoes not meet the mean norm residual2 cut - reject.");
405 return false;
406 }
407 return true;
408}

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

411 {
412 PatternStateVec outputPatterns{};
413 outputPatterns.reserve(toResolve.size());
415 auto areOverlapping = [this](const PatternState& a, const PatternState& b) {
417 if(!a.expSect.isNeighbour(b.expSect)) {
418 return false;
419 }
420 const double deltaThetaSeed {a.seedHit->position.theta() - b.seedHit->position.theta()};
421 if (std::abs(deltaThetaSeed) > 2.*m_cfg.thetaSearchWindow) {
422 return false;
423 }
424
425 if (a.nPhiLayers > 0 && b.nPhiLayers > 0) {
426 if (std::abs(P4Helpers::deltaPhi(a.patPhi, b.patPhi)) > 2.*m_cfg.phiTolerance) {
427 return false;
428 }
429 } else if (a.nPhiLayers > 0) {
430 if (!sectorMap.insideSector(b.expSect.msSector(), a.patPhi) ||
431 !sectorMap.insideSector(b.expSect.adjacentMsSector(), a.patPhi)) {
432 return false;
433 }
434 } else if (b.nPhiLayers > 0) {
435 if (!sectorMap.insideSector(a.expSect.msSector(), b.patPhi) ||
436 !sectorMap.insideSector(a.expSect.adjacentMsSector(), b.patPhi)) {
437 return false;
438 }
439 }
441 int nSharedHits{0};
442 for (std::size_t st{0u}; st < s_nStations; ++st) {
443 const auto& hitsA {a.hitsPerStation[st]};
444 const auto& hitsB {b.hitsPerStation[st]};
445 if (hitsA.empty() || hitsB.empty()) {
446 continue;
447 }
448 nSharedHits += std::ranges::count_if(hitsA, [&](const CandidateHit& hitA){
449 return std::ranges::any_of(hitsB, [&hitA](const CandidateHit& hitB) {
450 return hitA.sp()->primaryMeasurement() == hitB.sp()->primaryMeasurement();
451 });
452 });
453 }
455 const int minHits {std::min(a.nBendingHits(), b.nBendingHits())};
456 return nSharedHits >= 0.5 *minHits;
457 };
459 auto isBetterOverlap = [](const PatternState& a, const PatternState& b) {
460 const int nGoodStationDiff {a.nStations(/*onlyGoodStations=*/ true) - b.nStations(/*onlyGoodStations=*/ true)};
461 if (nGoodStationDiff != 0) {
462 return nGoodStationDiff > 0;
463 }
464 return isBetter(a,b);
465 };
466
467 for (auto it = toResolve.begin(); it != toResolve.end(); ++it) {
468 if (it->isOverlap) {
469 // If already marked as overlap, add to visual info, and discard the pattern
471 continue;
472 }
473 for (auto jt = std::next(it); jt != toResolve.end(); ++jt) {
474 if (jt->isOverlap || !areOverlapping(*it, *jt)) {
475 continue;
476 }
477 if (isBetterOverlap(*it, *jt)) {
478 ATH_MSG_VERBOSE(__func__<<"() Pattern "<<detailed(*it)<<"\nis BETTER than "<<detailed(*jt));
479 jt->isOverlap = true;
480 } else {
481 it->isOverlap = true;
482 ATH_MSG_VERBOSE(__func__<<"() Pattern "<<detailed(*jt)<<"\nis BETTER than "<<detailed(*it));
483 break;
484 }
485 }
486 if (!it->isOverlap) {
487 outputPatterns.push_back( std::move(*it));
488 } else {
489 // If overlap, add to visual info, as the pattern will be discarded
491 }
492 }
493 ATH_MSG_VERBOSE(__func__<<"() Patterns surviving overlap removal: "<< outputPatterns.size());
494 return outputPatterns;
495}
static bool isBetter(const PatternState &a, const PatternState &b)
Method to compare two patterns and define which one is better.
double deltaPhi(double phiA, double phiB)
delta Phi in range [-pi,pi[
Definition P4Helpers.h:34

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

1298 {
1299 v.pat.print(os, v.detailed);
1300 return os;
1301}

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 424 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 422 of file GlobalPatternFinder.h.

422{};

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