13#define PRINT_VERBOSE( xmsg ) \
15 if( logger->msgLvl( MSG::VERBOSE ) ) { \
16 logger->msg( MSG::VERBOSE ) << xmsg << endmsg; \
22 double inDegrees(
double angle) {
23 return angle / Gaudi::Units::deg;
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>);
47 auto visualInfo {
m_cfg.visionTool ? std::make_unique<PatternHitVisualInfoVec>() :
nullptr};
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.");
64 auto& outBucketVec = outBuckets[hit->container];
65 if (std::ranges::find(outBucketVec, hit->bucket) == outBucketVec.end()) {
66 outBucketVec.push_back(hit->bucket);
73 m_cfg.visionTool->plotPatternBuckets(Gaudi::Hive::currentContext(),
"GlobPatFind_", std::move(*visualInfo));
85 startPatternBuff.reserve(20);
87 endPatternBuff.reserve(20);
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])})) {
105 return pattern.isInPattern(hit);
109 for (
const auto seedingLayer :
m_cfg.layerSeedings) {
111 for (
const auto& [seedCoords, seed] : orderedSpacepoints) {
114 if (seedLayer != seedingLayer || (seed.isStraw && !
m_cfg.seedFromMdt)) {
117 ATH_MSG_VERBOSE(__func__<<
"() New seed hit "<<*seed<<
", coordinates "<<seedCoords);
119 uint8_t nExistingPatterns {countPatterns(seed, seedCoords)};
120 if (nExistingPatterns >=
m_cfg.maxSeedAttempts) {
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.");
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& ,
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.");
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.");
160 return c1.sp()->localPosition().y() < c2.sp()->localPosition().y();
165 for (std::size_t i {1}; i < candidateHits.size(); ++i) {
166 candidateHits[i].globLayer = candidateHits[i - 1].globLayer +
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.");
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);
183 const auto seedItr {std::ranges::find_if(candidateHits,
185 assert(seedItr != candidateHits.end());
188 PatternState patternSeed{seedCand,
static_cast<std::int8_t
>(seedCoords[sectorIdx]),
189 seedCoords[thetaIdx], &
m_cfg,
this};
191 patternSeed.
visualInfo = std::make_unique<PatternHitVisualInfo>(
192 seed.hit, seedCoords[thetaIdx] - thetaHalfWindow, seedCoords[thetaIdx] + thetaHalfWindow);
203 auto processHitRange = [&](
const auto begin,
206 startPatternBuff.clear();
207 startPatternBuff.push_back(std::move(toExtend));
209 for (
auto testItr = begin; testItr != end; ++testItr) {
214 extendPatterns(startPatternBuff, endPatternBuff, testHit, beamSpot, visualInfo);
216 std::swap(startPatternBuff, endPatternBuff);
218 return startPatternBuff.size() > 1
224 PatternStateVec forwardExtended {processHitRange(std::next(seedItr), candidateHits.end(), std::move(patternSeed))};
227 ATH_MSG_VERBOSE(__func__<<
"() Finished forward search, found "<<forwardExtended.size()<<
" forward patterns, start backward search.");
229 backwardExtended.reserve(2*forwardExtended.size());
232 pat.moveLineAnchorHit(seedCand);
233 pat.lastInsertedHit = seedCand;
237 processHitRange(std::reverse_iterator(seedItr), candidateHits.rend(), std::move(pat)),
238 std::back_inserter(backwardExtended)
242 if (backwardExtended.size() > 1) {
247 pat.meanNormResidual2 /= pat.nBendingLayers();
253 pat.isFinalized =
true;
254 outPatterns.push_back(std::move(pat));
258 ATH_MSG_VERBOSE(__func__<<
"() Found in total "<<outPatterns.size()<<
" patterns in eta before overlap removal");
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.");
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);
277 const unsigned minMissedLayers {std::ranges::min(missedLayersVec)};
279 const bool shouldPrune {startPatterns.size() > 1 &&
280 std::ranges::any_of(startPatterns, [](
const PatternState& p){
281 return p.nBendingLayers() > 2; })};
283 for (
auto [i, pat] : Acts::enumerate(startPatterns)) {
288 if (pat.lastInsertedHit.station == testHit.
station &&
289 missedLayersVec[i] > std::max(
m_cfg.maxMissLayersInStation, minMissedLayers)) {
291 <<
" layer hits, above the max allowed - abort pattern.");
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;
302 if (isBetter(pat, p)) {
303 ATH_MSG_VERBOSE(
"extendPatterns() Pruning: REJECT " << detailed(p));
308 return true; }) != startPatterns.end()) {
312 const auto [result, residual, accWindow] {pat.checkLineComp(testHit, beamSpot)};
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.");
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);
331 pat.visualInfo->discardedHits.push_back(testHit.
sp());
336 pat.addHit(testHit, residual, accWindow);
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.");
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);
353 pat.visualInfo->replacedHits.push_back(testHit.
sp());
354 endPatterns.back().visualInfo->replacedHits.push_back(pat.lastInsertedHit.sp());
360 ATH_MSG_VERBOSE(__func__<<
"() Hit is not compatible with the pattern - reject hit.");
362 pat.visualInfo->discardedHits.push_back(testHit.
sp());
368 if (
const uint8_t nMdtLastLayer{pat.nMDTLastLayer()}; nMdtLastLayer >= 3) {
370 LineTestRes newClusterRes {pat.computeLineResidual(pat.lastInsertedHit)};
373 <<
" consecutive MDT hit: new cluster is not compatible - reject.");
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);
385 pat.visualInfo->discardedHits.push_back(testHit.
sp());
386 endPatterns.back().visualInfo->replacedHits.push_back(pat.lastInsertedHit.sp());
392 pat.addHit(testHit, residual, accWindow);
396 ATH_MSG_VERBOSE(__func__<<
"() Hit compatible & on same layer of last added hit - overwrite last hit.");
397 pat.overWriteHit(testHit, residual, accWindow);
400 pat.visualInfo->replacedHits.push_back(pat.lastInsertedHit.sp());
405 endPatterns.push_back(std::move(pat));
407 startPatterns.clear();
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.");
419 if (pat.meanNormResidual2 >
m_cfg.meanNormRes2Cut) {
420 ATH_MSG_VERBOSE(__func__<<
"() Pattern " <<
detailed(pat) <<
"\ndoes not meet the mean norm residual2 cut - reject.");
429 outputPatterns.reserve(toResolve.size());
433 if(!
a.expSect.isNeighbour(b.expSect)) {
436 if (std::abs(
a.theta - b.theta) > 2.*
m_cfg.thetaSearchWindow) {
439 if (
a.nPhiLayers > 0 && b.nPhiLayers > 0) {
443 }
else if (
a.nPhiLayers > 0) {
444 if (!sectorMap.insideSector(b.expSect.msSector(),
a.phi) ||
445 !sectorMap.insideSector(b.expSect.adjacentMsSector(),
a.phi)) {
448 }
else if (b.nPhiLayers > 0) {
449 if (!sectorMap.insideSector(
a.expSect.msSector(), b.phi) ||
450 !sectorMap.insideSector(
a.expSect.adjacentMsSector(), b.phi)) {
457 const auto& hitsA {
a.hitsPerStation[st]};
458 const auto& hitsB {b.hitsPerStation[st]};
459 if (hitsA.empty() || hitsB.empty()) {
462 nSharedHits += std::ranges::count_if(hitsA, [&](
const CandidateHit& hitA){
463 return std::ranges::any_of(hitsB, [&hitA](
const CandidateHit& hitB) {
469 const int minHits {std::min(
a.nBendingHits(), b.nBendingHits())};
470 return nSharedHits >= 0.5 *minHits;
474 const int nGoodStationDiff {
a.nStations(
true) - b.nStations(
true)};
475 if (nGoodStationDiff != 0) {
476 return nGoodStationDiff > 0;
481 for (
auto it = toResolve.begin(); it != toResolve.end(); ++it) {
487 for (
auto jt = std::next(it); jt != toResolve.end(); ++jt) {
488 if (jt->isOverlap || !areOverlapping(*it, *jt)) {
491 if (isBetterOverlap(*it, *jt)) {
493 jt->isOverlap =
true;
495 it->isOverlap =
true;
500 if (!it->isOverlap) {
501 outputPatterns.push_back( std::move(*it));
507 ATH_MSG_VERBOSE(__func__<<
"() Patterns surviving overlap removal: "<< outputPatterns.size());
508 return outputPatterns;
517 struct PhiStripProjectionModel {
525 const double layCoord =
isBarrel(station) ? pos.perp() : pos.z();
526 return refStrip + (layCoord - refLay) * invSlope;
529 const double stripCoord =
isBarrel(station) ? pos.z() : pos.perp();
530 return std::abs(
project(pos) - stripCoord);
534 PhiStripProjectionModel result{};
535 result.station = station;
536 const auto& hits {pat.hitsPerStation[Acts::toUnderlying(station)]};
537 if (hits.empty())
return result;
540 const auto layCoord = [&result](
const HitPayload& hit) {
541 return isBarrel(result.station) ? hit.R : hit.Z;
544 const auto stripCoord = [&result](
const HitPayload& hit) {
545 return isBarrel(result.station) ? hit.Z : hit.R;
550 if (pat.nMeasurementLayers[Acts::toUnderlying(station)] > 1) {
552 const auto [minIt, maxIt] = std::ranges::minmax_element(hits, {},
557 if (!sp1 || !sp2 || std::abs(layCoord(*sp2) - layCoord(*sp1)) <
m_cfg.minLayerSeparation) {
559 pat.moveLineAnchorHit(hits.front());
561 sp1 = hits.front().hit;
562 sp2 = pat.lineAnchorHit.hit;
564 if (!sp1 || !sp2 )
return result;
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;
574 survivingPatterns.reserve(
patterns.size());
576 pat.finalizePatternEta();
580 std::optional<PhiStripProjectionModel> patProjOnStrip{};
582 const Amg::Transform3D& localToGlobal {bucket->msSector()->localToGlobalTransform(gctx)};
583 const StIndex station {
m_cfg.idHelperSvc->stationIndex(bucket->front()->identify())};
585 const Amg::Vector3D locY {localToGlobal.linear() * Amg::Vector3D::UnitY()};
587 for (
const auto& hit : *bucket) {
589 if (hit->measuresEta()){
595 const Amg::Vector3D globPosTest {localToGlobal * locPosTest};
596 const double globPhi {globPosTest.phi()};
597 if (!pat.isPhiCompatible(globPhi)) {
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.");
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);
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);
624 double thetaMin {(globPosTest - sigmaEta * locY).
theta()};
625 double thetaMax {(globPosTest + sigmaEta * locY).
theta()};
626 if (thetaMax < thetaMin) {
629 isEtaCompatible = std::max(pat.theta - thetaMax, thetaMin - pat.theta) < 0.5 *
m_cfg.thetaSearchWindow;
631 <<
", strip theta window: ["<<inDegrees(thetaMin)<<
", "<<inDegrees(thetaMax)<<
"]");
634 if (!isEtaCompatible) {
635 ATH_MSG_VERBOSE(__func__<<
"() The pattern falls outside the test hit strip in eta - skip this test hit.");
639 ATH_MSG_VERBOSE(__func__<<
"() Phi-only hit compatible - add it to the pattern.");
640 pat.phiOnlyHits.emplace_back(hit.get(),
nullptr,
nullptr, 0.f, 0.f,
641 globPhi, station, layNum, 0u,
false,
false);
642 if (pat.nPhiLayers == 0) pat.phi = globPhi;
646 if (pat.nPhiLayers <
m_cfg.minPhiLayers) {
648 <<
" phi layers, below the minimum required - reject this pattern.");
651 pat.finalizePatternPhi();
652 survivingPatterns.push_back(std::move(pat));
660 const auto& hits {cache.hitsPerStation[st]};
661 if (hits.empty())
continue;
663 auto& out {hitPerStation[
static_cast<StIndex>(st)]};
664 out.reserve(hits.size());
665 std::ranges::transform(hits, std::back_inserter(out),
669 for (
const HitPayload& hit : cache.phiOnlyHits) {
671 out.push_back(hit.
sp());
674 pattern.setTheta(cache.theta);
675 pattern.setPhi(cache.phi);
677 pattern.setSector(cache.expSect.sector());
679 pattern.setNPrecisionLayers(cache.nPrecisionLayers);
680 pattern.setNTriggerLayers(cache.nTriggerLayers);
681 pattern.setNPhiLayers(cache.nPhiLayers);
682 pattern.setMeanNormResidual2(cache.getMeanResidual2());
690 std::transform(cache.begin(), cache.end(), std::back_inserter(
patterns),
692 return convertToPattern(cacheEntry);
700 SearchTree_t::vector_t rawData{};
703 size_t totalHits = 0;
706 totalHits += bucket->size();
710 rawData.reserve(3 * totalHits);
713 ATH_MSG_VERBOSE(__func__<<
"() Processing "<<spc->size()<<
" space point buckets...");
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();
720 for (
const auto& hit : *bucket) {
722 const bool isStraw {hit->isStraw()};
723 if (!hit->measuresEta() || (!
m_cfg.useMdtHits && isStraw)) {
727 const Amg::Vector3D globalPos {localToGlobal * hit->localPosition()};
728 const double globalPhi {globalPos.phi()};
730 const uint8_t localLayer =
m_spSorter.sectorLayerNum(*hit);
738 for (
const auto proj : {SectorProjector::leftOverlap, SectorProjector::center, SectorProjector::rightOverlap}) {
741 if (proj != SectorProjector::center && hit->measuresPhi() && expSect != hitExpSector) {
742 ATH_MSG_VERBOSE(__func__<<
"() Hit with "<<hitExpSector<<
" is not compatible with "<<expSect);
748 const double projR {(globalPos - globalPos.dot(projDir) * projDir).perp()};
750 std::array<double, 2> coords{};
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});
763 ATH_MSG_VERBOSE(__func__<<
"() Create a new tree with "<<rawData.size()<<
" entries. ");
767 const std::int8_t expSector,
768 const double seedTheta,
784 if (seed->sp()->measuresPhi()) {
798 cfg->phiTolerance : sectorMap.sectorWidth(testHit.
sector)};
801 <<
" is not compatible with the test hit with phi "<<inDegrees(testHit->
phi) <<
" - reject.");
811 if (
res.residual <
res.accWindow) {
812 res.result = decision;
828 PRINT_VERBOSE(__func__<<
"() Test hit is the same as last inserted hit - reject.");
837 PRINT_VERBOSE(__func__<<
"() Test hit on same layer as seed with no prior hits, but not consecutive MDT hits - reject.");
858 PRINT_VERBOSE(__func__<<
"() Test hit is a trigger hit and last inserted hit is precision on the same layer - keep the precision hit.");
862 PRINT_VERBOSE(__func__<<
"() Test hit is a precision hit and last inserted hit is trigger on the same layer - check residual...");
876 const auto& closestStIt = std::ranges::min_element(
hitsPerStation, std::ranges::less{},
877 [&refHit](
const auto& hits){
878 if (hits.empty() || hits.front().station == refHit.
station) {
879 return std::numeric_limits<int>::max();
881 return std::abs(hits.front().globLayer - refHit.
globLayer);
885 const auto& hits {*closestStIt};
886 auto it {std::ranges::min_element(hits, std::ranges::less{},
888 return std::abs(hit.globLayer - refHit.
globLayer); })};
891 uint8_t nSameLayer {1u};
892 for (
auto jt = std::next(it); jt != hits.end() && jt->globLayer == it->globLayer; ++jt) {
904 const bool new_useBeamspot {lastSt ==
lineAnchorHit.station &&
908 const double new_anchorR {new_useBeamspot ? beamSpot.perp() :
lineAnchorHit.R};
909 const double new_anchorZ {new_useBeamspot ? beamSpot.z() :
lineAnchorHit.Z};
914 double refR {0.}, refZ {0.};
915 uint8_t nSameLayer {0u};
916 for (
const auto& hit : hitsLastSt) {
925 const double new_dZ_slope {refZ - new_anchorZ};
926 if (std::abs(new_dZ_slope) < 1.) {
927 PRINT_VERBOSE(
"updateLineParameters() Couldn't update the line parameters.");
930 const double dR_slope {refR - new_anchorR};
932 <<
" --> " << new_dZ_slope <<
", " << dR_slope / new_dZ_slope);
949 const double dZ_res {testHit.
Z -
anchorZ};
955 const double geoFactor {1.+ Acts::square(
lineSlope)};
956 const double alpha {dZ_res /
dZ_slope};
957 const double propFactor {1. - alpha + Acts::square(alpha)};
958 res.accWindow =
cfg->baseRWindow * std::sqrt(2*geoFactor * propFactor);
968 const double LRcorrection {dZ_res * geoFactor *
LR_factor};
969 PRINT_VERBOSE(__func__<<
"() signed residual: " <<
res.residual <<
" -> Apply LR correction: " << LRcorrection);
970 res.residual = std::min(std::abs(
res.residual-LRcorrection), std::abs(
res.residual+LRcorrection));
972 res.residual = std::abs(
res.residual);
976 <<
", Residual: " <<
res.residual <<
", Window: " <<
res.accWindow <<
", Geometrical Factor: "
977 << std::sqrt(geoFactor) <<
", Propagation Factor: " << std::sqrt(propFactor));
987 <<
" is not compatible with the test hit with phi "<<inDegrees(testPhi));
991 const unsigned sector1 {
expSect.msSector()};
992 const unsigned sector2 {
expSect.adjacentMsSector()};
993 const bool isCompatible {sector1 == sector2
994 ? sectorMap.insideSector(sector1, testPhi)
995 : sectorMap.insideSector(sector1, testPhi) && sectorMap.insideSector(sector2, testPhi)};
997 PRINT_VERBOSE(__func__<<
"() The test hit with phi = "<<inDegrees(testPhi)
998 <<
" is not inside the pattern sectors: "<<sector1<<
" and "<<sector2);
1005 const double residual,
1006 const double acceptWindow) {
1027 if (acceptWindow > 0.) {
1039 const double newResidual,
1040 const double newAcceptWindow) {
1043 throw std::runtime_error(std::format(
1044 "Trying to overwrite a hit in station/layer {}/{} with another one from station/layer {}/{}",
1051 std::stringstream
ss {};
1052 ss <<
"Trying to overwrite a hit with incompatible type\n";
1054 ss <<
"New hit: " << **newHit <<
", isPrecision: " << newHit->
isPrecision <<
", measuresEta: " << newHit.
sp()->
measuresEta();
1055 throw std::runtime_error(
ss.str());
1073 while (!stHits.empty()) {
1086 return std::ranges::find_if(hits,
1087 [&hit](
const CandidateHit& c){
return *c == hit; }) != hits.end();
1093 for (
const auto& hit : hits) {
1094 theta += atan2(hit.R, hit.Z);
1102 phi = sectorMap.sectorOverlapPhi(
expSect.msSector(),
expSect.adjacentMsSector());
1105 double deltaPhiAcc {0.};
1106 std::optional<double> centralPhi {};
1107 auto processPhiHit = [&deltaPhiAcc, ¢ralPhi](
const HitPayload& hit){
1108 if (!hit->measuresPhi()) {
1112 centralPhi = hit.phi;
1117 for (
const auto& hit : hits) {
1118 processPhiHit(*hit);
1137 [](uint8_t acc,
const auto& hits){
1138 return acc + hits.size(); });
1151 uint8_t nMdtLastayer {0u};
1152 for (
auto it = stationHits.rbegin(); it != stationHits.rend(); ++it) {
1158 return nMdtLastayer;
1161 std::vector<const SpacePointBucket*> buckets{};
1163 for (
const auto& hit : hits) {
1164 if (std::ranges::find(buckets, hit->bucket) == buckets.end()) {
1165 buckets.push_back(hit->bucket);
1177 for (
auto it = stationHits.rbegin(); it != stationHits.rend(); ++it) {
1190 const int nLayerDiff {
a.nBendingLayers() - b.nBendingLayers()};
1191 if (std::abs(nLayerDiff) >= 3) {
1192 return nLayerDiff > 0;
1194 return a.getMeanResidual2() < b.getMeanResidual2();
1199 auto getLayerOrdering = [](
const bool isLayer1Lower) {
1217 return getLayerOrdering(hit1->
msSector()->
barrel() ? hit1.
R < hit2.
R : std::abs(hit1.
Z) < std::abs(hit2.
Z));
1221 if (layer1 == layer2) {
1223 if (layer1 == LayerIndex::Middle) {
1225 return getLayerOrdering(st1 == StIndex::BM);
1227 if (layer1 == LayerIndex::Inner) {
1229 return getLayerOrdering(hit1.
R < hit2.
R);
1231 throw std::runtime_error(
"Unexpected to have two pattern-compatible hits one in BO and the other in EO.");
1233 if (layer1 == LayerIndex::Inner || layer2 == LayerIndex::Inner) {
1235 return getLayerOrdering(layer1 == LayerIndex::Inner);
1237 if (layer1 == LayerIndex::Outer || layer2 == LayerIndex::Outer) {
1239 return getLayerOrdering(layer2 == LayerIndex::Outer);
1241 if (layer1 == LayerIndex::BarrelExtended || layer2 == LayerIndex::BarrelExtended) {
1243 return getLayerOrdering(layer1 == LayerIndex::BarrelExtended);
1246 if (layer1 == LayerIndex::Extended) {
1247 return getLayerOrdering(st2 == StIndex::EM);
1249 return getLayerOrdering(st1 == StIndex::BM);
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) {
1264 return std::abs(tubeNum1-tubeNum2) < 2;
1273 std::vector<const SpacePointBucket*> buckets{cache.getParentBuckets()};
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;
1282 visualInfo->push_back(*cache.visualInfo);
1284 std::ranges::copy(buckets, std::back_inserter(visualInfo->back().parentBuckets));
1286 visualInfo->back().patternCopy = std::make_unique<GlobalPattern>(std::move(pattern));
1287 visualInfo->back().status = status;
1290 return hit == other.hit;
1293 ostr<<
"Pattern state, Exp Sector: "<<(int)
expSect.sector()<<
", Theta: "<<inDegrees(
theta) <<
", Phi: "<<inDegrees(
phi);
1296 ostr<<
", Hit per station: \n";
1299 if (hits.empty())
continue;
1304 for (
const auto& hit : hits) {
1305 ostr<<
" "<<**hit<<
", R: "<<hit.R<<
", Z: "<<hit.Z<<
", Phi: "<<inDegrees(hit->phi)<<
", loc/glob lay: "<<(int)hit->locLayer<<
"/"<<(
int)hit.globLayer<<
"\n";
1322 v.pat.print(os, v.detailed);
Scalar theta() const
theta method
bool isValid() const
Test to see if the link can be dereferenced.
#define ATH_MSG_VERBOSE(x)
std::pair< std::vector< unsigned int >, bool > res
T_ResultType project(ParameterMapping::type< N > parameter_map, const T_Matrix &matrix)
#define PRINT_VERBOSE(MSG)
Helper macro for printing verbose messages for debugging.
double angle(const GeoTrf::Vector2D &a, const GeoTrf::Vector2D &b)
static const Attributes_t empty
Header file for AthHistogramAlgorithm.
bool msgLvl(const MSG::Level lvl) const
Test the output level.
AthMessaging(IMessageSvc *msgSvc, const std::string &name)
Constructor.
bool barrel() const
Returns whether the sector is placed in the barrel.
double phi() const
Returns the phi angle of the expanded sector.
Amg::Vector3D normalDir() const
Returns the vector that is normal to the plane spanned by the expanded sector.
SectorProjector
Enumeration to select the sector projection of the regular MS sector.
std::int8_t sector() const
Returns the expanded sector number.
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.
std::vector< const SpacePointContainer * > SpacePointContainerVec
Abrivation for a vector of space-point containers.
PatternStateVec resolveOverlaps(PatternStateVec &toResolve, PatternHitVisualInfoVec *visualInfo=nullptr) const
Method to remove overlapping patterns.
static bool areConsecutiveMdt(const CandidateHit &hit1, const CandidateHit &hit2)
Helper function to check whether two hits are consecutive MDT measurements.
friend std::ostream & operator<<(std::ostream &os, const PatternPrintView &v)
std::vector< PatternHitVisualInfo > PatternHitVisualInfoVec
Abrivation for a vector of visual information objects.
static LayerOrdering checkLayerOrdering(const HitPayload &hit1, const HitPayload &hit2)
Method to check the logical layer ordering of two hits.
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 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)...
Config m_cfg
Global Pattern Recognition configuration.
static PatternPrintView brief(const PatternState &p)
Print the pattern candidate and stream operator.
std::vector< GlobalPattern > PatternVec
Abrivation for a vector of global patterns.
static bool isBetter(const PatternState &a, const PatternState &b)
Method to compare two patterns and define which one is better.
LineTestDecision
: Enum for possible outcomes of pattern line compatibility test
@ eRejectHit
Test failed, discard the hit.
@ eBranchPattern
Test successfull with multiple pattern hits on same layer, branch the pattern.
@ eOverwriteLastHit
Test successful, overwrite the hit.
@ eAddHit
Test successfull, add hit to pattern.
@ eConsecutiveMdt
Test hit is a consecutive MDT hit.
bool passPatternCuts(const PatternState &pat) const
Method to check if a pattern passes the quality cuts.
Muon::MuonStationIndex::StIndex StIndex
Type alias for the station index.
LayerOrdering
Enum to express the logical measurement layer ordering given two hits.
void addPhiOnlyHits(const ActsTrk::GeometryContext &gctx, PatternStateVec &patterns) const
Method to add phi-only measurements to existing PatternStates.
GlobalPatternFinder(const std::string &name, Config &&config)
Standard constructor.
std::vector< PatternState > PatternStateVec
static const int s_nStations
PatternVec findPatterns(const ActsTrk::GeometryContext &gctx, const SpacePointContainerVec &spacepoints, BucketPerContainer &outBuckets) const
Main methods steering the pattern finding.
SeedCoords
Abrivation of the seed coordinates.
@ eSector
Expanded sector coordinate of the associated spectrometer sector
SpacePointPerLayerSorter m_spSorter
Spacepoint sorter per logical measurement layer.
GlobalPattern convertToPattern(const PatternState &candidate) const
Method to convert a PatternState into a GlobalPattern object.
std::unordered_map< const SpacePointContainer *, std::vector< const SpacePointBucket * > > BucketPerContainer
Abrivation for a collection of space-point buckets grouped by their corresponding input container.
PatternStateVec findPatternsInEta(const SearchTree_t &orderedSpacepoints, PatternHitVisualInfoVec *visualInfo=nullptr) const
Method steering the global pattern building in the bending plane.
Acts::KDTree< 2, HitPayload, double, std::array, 5 > SearchTree_t
Definition of the search tree class.
static PatternPrintView detailed(const PatternState &p)
Muon::MuonStationIndex::LayerIndex LayerIndex
Type alias for the station layer index.
Data class to represent an eta maximum in hough space.
std::unordered_map< StIndex, std::vector< HitType > > HitCollection
: The muon space point bucket represents a collection of points that will bre processed together in t...
bool measuresPhi() const
: Does the space point contain a phi measurement
xAOD::UncalibMeasType type() const
const MuonGMR4::SpectrometerSector * msSector() const
const xAOD::MuonMeasurement * primaryMeasurement() const
bool measuresEta() const
: Does the space point contain an eta measurement
std::vector< std::string > patterns
Eigen::Affine3d Transform3D
Eigen::Matrix< double, 3, 1 > Vector3D
T wrapToPi(T phi)
Wrap angle in radians to [-pi, pi].
T deltaPhi(T phiA, T phiB)
Return difference phiA - phiB in range [-pi, pi].
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.
bool isBarrel(const ChIndex index)
Returns true if the chamber index points to a barrel chamber.
const std::string & stName(StIndex index)
convert StIndex into a string
LayerIndex toLayerIndex(ChIndex index)
convert ChIndex into LayerIndex
void swap(ElementLinkVector< DOBJ > &lhs, ElementLinkVector< DOBJ > &rhs)
MdtDriftCircle_v1 MdtDriftCircle
Helper for azimuthal angle calculations.
Small wrapper for candidate hits used to build patterns.
bool isStraw
Is straw hit.
uint8_t globLayer
Global measurement layer number.
const SpacePoint * sp() const
Hit information stored during pattern building.
const SpacePoint * hit
Pointer to the underlying hit.
const SpacePoint * sp() const
Get the pointer to the underlying hit.
uint8_t locLayer
Layer number in the sector frame.
bool isPrecision
Is precision hit.
bool isStraw
Is straw hit.
StIndex station
Station index.
bool operator==(const HitPayload &other) const
Equal operator: it compares the underlying hit.
: Small struct to encapsulate the result of the line compatibility test
Pattern state object storing pattern information during construction.
Acts::CloneablePtr< PatternHitVisualInfo > visualInfo
Pointer to Visual Information for pattern visualization.
void moveLineAnchorHit(const CandidateHit &refHit)
Move the line anchor hit given a reference hit.
double lastResidual
Residual & acceptance window of the last inserted hit (needed when replacing a hit).
std::array< uint8_t, s_nStations > nMeasurementLayers
Counts of measurement layers per station.
double meanNormResidual2
Mean over eta hits of the square of their residual divided by acceptance window.
bool isInPattern(const HitPayload &hit) const
Check wheter a hit is present in the pattern.
uint8_t nStations(const bool onlyGoodStations) const
Method returning the number of stations.
uint8_t nBendingHits() const
Return the number of hits in bending coordinate.
bool useBeamspot
Whether we used the beamspot to compute the line parameters.
double theta
Average theta & average phi of the pattern.
bool needLineUpdate
Whether we need to update the pattern line the next time we find a hit in a new layer.
void overWriteHit(const CandidateHit &newHit, const double newResidual, const double newAcceptWindow)
Overwrite the hits on the last layer with the new one.
std::vector< HitPayload > phiOnlyHits
Array holding phi-only hits.
ExpandedSector expSect
expanded MS sector
LineTestRes checkLineComp(const CandidateHit &testHit, const Amg::Vector3D &beamSpot)
Method checking line compatibility of a test hit against the pattern.
LineTestRes computeLineResidual(const CandidateHit &testHit) const
Method to compute the residual of a test hit against the pattern line.
void finalizePatternEta()
Finalize the pattern building in eta and update its state.
std::vector< const SpacePointBucket * > getParentBuckets() const
Get the buckets associated with the pattern.
double LR_factor
Simple line model: factor for LR correction to be applied.
bool isFinalized
Flag to indicate if the pattern has been finalized.
void updateLineParameters(const Amg::Vector3D &beamSpot)
Update the line parameters based on the current hits.
uint8_t nBendingLayers() const
Return the number of layers in bending coordinate.
void finalizePatternPhi()
Finalize the pattern building in phi and update its state.
CandidateHit prevLayerHit
Pointer to the last hit in the second-to-last layer.
double anchorZ
Simple line model: anchor Z and R.
PatternState()=delete
Delete default destructor - ensure patterns are always constructed from a seed or another pattern.
bool isPhiCompatible(const double testPhi) const
Method to check the phi compatibility of a test hit with a given pattern.
double lineSlope
Simple line model: slope and associated delta Z, i.e.
std::array< std::vector< CandidateHit >, s_nStations > hitsPerStation
Map collection of hits per station.
void print(std::ostream &ostr, bool detailed) const
Print the pattern candidate.
CandidateHit lineAnchorHit
Pointer to the line anchor hit.
const Config * cfg
Pointer to cfg option.
const AthMessaging * logger
Logger.
void addHit(const CandidateHit &hit, const double residual, const double acceptWindow)
Add a hit to the pattern and update the internal state.
bool isInLastLayer(const CandidateHit &hit) const
Check whether a given hit is in the last layer.
uint8_t nPrecisionLayers
Counts of precision / non-precision / phi layers.
CandidateHit lastInsertedHit
Pointer to the last inserted hit.
double getMeanResidual2() const
Return the mean normalized residual squared.
uint8_t nMDTLastLayer() const
Gives the number of MDT hits on the last layer.