13#define PRINT_VERBOSE( xmsg ) \
15 if( logger->msgLvl( MSG::VERBOSE ) ) { \
16 logger->msg( MSG::VERBOSE ) << xmsg << endmsg; \
23 return angle / Gaudi::Units::deg;
30 return isBarrel(station) ? std::abs(pos2.perp() - pos1.perp())
31 : std::abs(pos2.z() - pos1.z());
36using namespace Acts::UnitLiterals;
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>);
57 auto visualInfo {
m_cfg.visionTool ? std::make_unique<PatternHitVisualInfoVec>() :
nullptr};
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.");
73 auto& outBucketVec = outBuckets[hit->container];
74 if (std::ranges::find(outBucketVec, hit->bucket) == outBucketVec.end()) {
75 outBucketVec.push_back(hit->bucket);
82 m_cfg.visionTool->plotPatternBuckets(Gaudi::Hive::currentContext(),
"GlobPatFind_", std::move(*visualInfo));
94 startPatternBuff.reserve(20);
96 endPatternBuff.reserve(20);
102 outPatterns.reserve(40);
109 const SearchTree_t::coordinate_t& coords) -> uint8_t {
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])})) {
116 return pattern.isInPattern(hit);
120 for (
const auto seedingLayer :
m_cfg.layerSeedings) {
122 for (
const auto& [seedCoords, seed] : orderedSpacepoints) {
125 if (seedLayer != seedingLayer || (seed.isStraw && !
m_cfg.seedFromMdt)) {
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) {
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.");
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};
151 std::vector<CandidateHit> candidateHits{};
152 orderedSpacepoints.rangeSearchMapDiscard(selectRange, [&candidateHits](
const SearchTree_t::coordinate_t& ,
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.");
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.");
171 return c1.sp()->localPosition().y() < c2.sp()->localPosition().y();
176 for (std::size_t i {1}; i < candidateHits.size(); ++i) {
177 candidateHits[i].globLayer = candidateHits[i - 1].globLayer +
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.");
185 if (
msgLvl(MSG::VERBOSE)) {
186 ATH_MSG_VERBOSE(__func__<<
"() Found "<< candidateHits.size()<<
" candidate hits: ");
187 for (
const auto& c : candidateHits) {
193 const auto seedItr {std::ranges::find_if(candidateHits,
195 assert(seedItr != candidateHits.end());
198 PatternState patternSeed{seedCand,
static_cast<std::int8_t
>(seedCoords[sectorIdx]), &
m_cfg,
this};
200 patternSeed.
visualInfo = std::make_unique<PatternHitVisualInfo>(
201 seed.hit, seedCoords[
thetaIdx] - thetaHalfWindow, seedCoords[
thetaIdx] + thetaHalfWindow);
212 auto processHitRange = [&](
const auto begin,
215 startPatternBuff.clear();
216 startPatternBuff.push_back(std::move(toExtend));
218 for (
auto testItr = begin; testItr != end; ++testItr) {
223 extendPatterns(startPatternBuff, endPatternBuff, testHit, beamSpot, visualInfo);
225 std::swap(startPatternBuff, endPatternBuff);
227 return startPatternBuff.size() > 1
233 PatternStateVec forwardExtended {processHitRange(std::next(seedItr), candidateHits.end(), std::move(patternSeed))};
236 ATH_MSG_VERBOSE(__func__<<
"() Finished forward search, found "<<forwardExtended.size()<<
" forward patterns, start backward search.");
238 backwardExtended.reserve(2*forwardExtended.size());
241 pat.moveLineAnchorHit(seedCand);
242 pat.lastInsertedHit = seedCand;
246 processHitRange(std::reverse_iterator(seedItr), candidateHits.rend(), std::move(pat)),
247 std::back_inserter(backwardExtended)
251 if (backwardExtended.size() > 1) {
256 pat.meanNormResidual2 /= pat.nBendingLayers();
262 pat.isFinalized =
true;
263 outPatterns.push_back(std::move(pat));
267 ATH_MSG_VERBOSE(__func__<<
"() Found in total "<<outPatterns.size()<<
" patterns in eta before overlap removal");
276 ATH_MSG_VERBOSE(__func__<<
"() *** Test "<<testHit<<
" against " << startPatterns.size() <<
" active patterns.");
280 std::vector<unsigned> missedLayersVec{};
281 missedLayersVec.reserve(startPatterns.size());
282 std::ranges::transform(startPatterns, std::back_inserter(missedLayersVec),
284 return std::abs(pat.lastInsertedHit.globLayer - testHit.
globLayer);
286 const unsigned minMissedLayers {std::ranges::min(missedLayersVec)};
288 const bool shouldPrune {startPatterns.size() > 1 &&
289 std::ranges::any_of(startPatterns, [](
const PatternState& p){
290 return p.nBendingLayers() > 2; })};
292 for (
auto [i, pat] : Acts::enumerate(startPatterns)) {
298 if (pat.lastInsertedHit.station == testHit.
station &&
299 missedLayersVec[i] > std::max(
m_cfg.maxMissLayersInStation, minMissedLayers)) {
301 <<
" layer hits, above the max allowed - abort pattern.");
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;
312 if (isBetter(pat, p)) {
313 ATH_MSG_VERBOSE(
"extendPatterns() Pruning: "<<detailed(pat)<<
"\nis BETTER than "<<detailed(p));
318 return true; }) != startPatterns.end()) {
323 const auto [result, residual, accWindow] {pat.checkLineComp(testHit, beamSpot)};
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.");
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);
342 pat.visualInfo->discardedHits.push_back(testHit.
sp());
348 pat.addHit(testHit, residual, accWindow);
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.");
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);
365 pat.visualInfo->discardedHits.push_back(testHit.
sp());
371 ATH_MSG_VERBOSE(__func__<<
"() Hit is not compatible with the pattern - reject hit.");
373 pat.visualInfo->discardedHits.push_back(testHit.
sp());
378 ATH_MSG_VERBOSE(__func__<<
"() Compatible MDT hits on same layer - accept.");
380 pat.addHit(testHit, -1., -1.);
384 ATH_MSG_VERBOSE(__func__<<
"() Hit compatible & on same layer of last added hit - overwrite last hit.");
385 pat.overWriteHit(testHit, residual, accWindow);
389 endPatterns.push_back(std::move(pat));
391 startPatterns.clear();
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.");
403 if (pat.meanNormResidual2 >
m_cfg.meanNormRes2Cut) {
404 ATH_MSG_VERBOSE(__func__<<
"() Pattern " <<
detailed(pat) <<
"\ndoes not meet the mean norm residual2 cut - reject.");
413 outputPatterns.reserve(toResolve.size());
417 if(!
a.expSect.isNeighbour(b.expSect)) {
420 const double deltaThetaSeed {
a.seedHit->position.theta() - b.seedHit->position.theta()};
421 if (std::abs(deltaThetaSeed) > 2.*
m_cfg.thetaSearchWindow) {
425 if (
a.nPhiLayers > 0 && b.nPhiLayers > 0) {
429 }
else if (
a.nPhiLayers > 0) {
430 if (!sectorMap.insideSector(b.expSect.msSector(),
a.patPhi) ||
431 !sectorMap.insideSector(b.expSect.adjacentMsSector(),
a.patPhi)) {
434 }
else if (b.nPhiLayers > 0) {
435 if (!sectorMap.insideSector(
a.expSect.msSector(), b.patPhi) ||
436 !sectorMap.insideSector(
a.expSect.adjacentMsSector(), b.patPhi)) {
443 const auto& hitsA {
a.hitsPerStation[st]};
444 const auto& hitsB {b.hitsPerStation[st]};
445 if (hitsA.empty() || hitsB.empty()) {
448 nSharedHits += std::ranges::count_if(hitsA, [&](
const CandidateHit& hitA){
449 return std::ranges::any_of(hitsB, [&hitA](
const CandidateHit& hitB) {
455 const int minHits {std::min(
a.nBendingHits(), b.nBendingHits())};
456 return nSharedHits >= 0.5 *minHits;
460 const int nGoodStationDiff {
a.nStations(
true) - b.nStations(
true)};
461 if (nGoodStationDiff != 0) {
462 return nGoodStationDiff > 0;
467 for (
auto it = toResolve.begin(); it != toResolve.end(); ++it) {
473 for (
auto jt = std::next(it); jt != toResolve.end(); ++jt) {
474 if (jt->isOverlap || !areOverlapping(*it, *jt)) {
477 if (isBetterOverlap(*it, *jt)) {
479 jt->isOverlap =
true;
481 it->isOverlap =
true;
486 if (!it->isOverlap) {
487 outputPatterns.push_back( std::move(*it));
493 ATH_MSG_VERBOSE(__func__<<
"() Patterns surviving overlap removal: "<< outputPatterns.size());
494 return outputPatterns;
504 struct PhiStripProjectionModel {
511 return Acts::detail::LineHelper::lineIntersect<3>(
512 patPosition, patDirection, stripPos, stripDir).pathLength();
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;
524 if (pat.nMeasurementLayers[Acts::toUnderlying(station)] > 1) {
526 const auto [minIt, maxIt] {std::ranges::minmax_element(stationHits, {},
532 layerDistance(station, pat.projToPhiPlane(*sp1), pat.projToPhiPlane(*sp2)) <
m_cfg.minHitDistance4Line) {
535 pat.moveLineAnchorHit(stationHits.front());
536 sp1 = stationHits.front().hit;
537 sp2 = pat.lineAnchorHit.hit;
539 if (!sp1 || !sp2 )
return result;
544 result.patPosition = pos1;
545 result.patDirection = (pos2 - pos1).
unit();
546 result.isValid =
true;
551 survivingPatterns.reserve(
patterns.size());
557 std::optional<PhiStripProjectionModel> patProjOnStrip{};
558 bool stopSearch {
false};
560 if (stopSearch)
break;
562 const Amg::Transform3D& localToGlobal {bucket->msSector()->localToGlobalTransform(gctx)};
563 const StIndex station {
m_cfg.idHelperSvc->stationIndex(bucket->front()->identify())};
565 const Amg::Vector3D locY {localToGlobal.linear() * Amg::Vector3D::UnitY()};
567 for (
const auto& hit : *bucket) {
568 if (pat.nPhiLayers >=
m_cfg.minPhiLayers) {
573 if (hit->measuresEta()){
579 const Amg::Vector3D globPosTest {localToGlobal * locPosTest};
580 const double globPhi {globPosTest.phi()};
581 if (!pat.isPhiCompatible(globPhi)) {
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.");
598 if (!patProjOnStrip.has_value() || patProjOnStrip->station != station) {
599 patProjOnStrip = makeProjectionModel(pat, station);
601 if (!patProjOnStrip->isValid) {
602 ATH_MSG_VERBOSE(__func__<<
"() Invalid projection model for station "<<station<<
" - skip hit.");
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);
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.");
615 ATH_MSG_VERBOSE(__func__<<
"() Phi-only hit compatible - add it to the pattern.");
616 pat.phiOnlyHits.emplace_back(hit.get(),
nullptr,
nullptr, globPosTest, Amg::Vector3D::Zero(),
617 station, layNum, 0u,
false,
false);
619 if (pat.nPhiLayers == 0) pat.updatePatternPhi(globPhi);
623 if (pat.nPhiLayers <
m_cfg.minPhiLayers) {
625 <<
" phi layers, below the minimum required - reject this pattern.");
628 pat.finalizePatternPhi();
629 survivingPatterns.push_back(std::move(pat));
638 const auto& hits {cache.hitsPerStation[st]};
639 if (hits.empty())
continue;
641 auto& outHits {hitPerStation[
static_cast<StIndex>(st)]};
642 outHits.reserve(hits.size());
643 auto& outBuckets {bucketPerStation[
static_cast<StIndex>(st)]};
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);
653 for (
const HitPayload& hit : cache.phiOnlyHits) {
656 GlobalPattern pattern{std::move(hitPerStation), std::move(bucketPerStation)};
657 pattern.setTheta(cache.seedHit->position.theta());
658 pattern.setPhi(cache.patPhi);
660 pattern.setSector(cache.expSect.sector());
662 pattern.setNPrecisionLayers(cache.nPrecisionLayers);
663 pattern.setNTriggerLayers(cache.nTriggerLayers);
664 pattern.setNPhiLayers(cache.nPhiLayers);
665 pattern.setMeanNormResidual2(cache.getMeanResidual2());
673 std::transform(cache.begin(), cache.end(), std::back_inserter(
patterns),
675 return convertToPattern(cacheEntry);
683 SearchTree_t::vector_t rawData{};
685 using enum SectorProjector;
687 size_t totalHits = 0;
690 totalHits += bucket->size();
694 rawData.reserve(3 * totalHits);
697 ATH_MSG_VERBOSE(__func__<<
"() Processing "<<spc->size()<<
" space point buckets...");
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();
705 for (
const auto& hit : *bucket) {
707 const bool isStraw {hit->isStraw()};
708 if (!hit->measuresEta() || (!
m_cfg.useMdtHits && isStraw)) {
712 const Amg::Vector3D globalPos {localToGlobal * hit->localPosition()};
713 const Amg::Vector3D globWireDir {rotation * hit->sensorDirection()};
718 for (
const SectorProjector proj : {leftOverlap, center, rightOverlap}) {
721 if (proj != SectorProjector::center && hit->measuresPhi() && expSect != hitExpSector) {
722 ATH_MSG_VERBOSE(__func__<<
"() Hit with "<<hitExpSector<<
" is not compatible with "<<expSect);
729 const double projR {hit->measuresPhi() ? globalPos.perp() : (globalPos - globalPos.dot(planeNormal) * planeNormal).perp()};
731 std::array<double, 2> coords{};
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,
744 ATH_MSG_VERBOSE(__func__<<
"() Create a new tree with "<<rawData.size()<<
" entries. ");
748 const std::int8_t expSector,
775 const double phiHit {testHit->
position.phi()};
778 cfg->phiTolerance : sectorMap.sectorWidth(testHit.
sector)};
781 <<
" is not compatible with the test hit with phi "<<
inDeg(phiHit) <<
" - reject.");
791 if (
res.residual <
res.accWindow) {
792 res.result = decision;
796 std::make_pair(std::tan(
lineDir.theta()),
res.accWindow);
809 PRINT_VERBOSE(__func__<<
"() Test hit is the same as last inserted hit - reject.");
818 PRINT_VERBOSE(__func__<<
"() Test hit on same layer as seed with no prior hits, but not consecutive MDT hits - reject.");
839 PRINT_VERBOSE(__func__<<
"() Test hit is trigger hit and last inserted hit is precision, on the same layer - keep precision hit.");
843 PRINT_VERBOSE(__func__<<
"() Test hit is a precision hit and last inserted hit is trigger on the same layer - check residual...");
857 const auto& closestStIt = std::ranges::min_element(
hitsPerStation, std::ranges::less{},
858 [&refHit](
const auto& hits){
859 if (hits.empty() || hits.front().station == refHit.
station) {
860 return std::numeric_limits<int>::max();
862 return std::abs(hits.front().globLayer - refHit.
globLayer);
866 const auto& hits {*closestStIt};
867 auto it {std::ranges::min_element(hits, std::ranges::less{},
869 return std::abs(hit.globLayer - refHit.
globLayer); })};
872 uint8_t nSameLayer {1u};
873 for (
auto jt = std::next(it); jt != hits.end() && jt->globLayer == it->globLayer; ++jt) {
888 (pos1 - pos2).mag() <
cfg->minHitDistance4Line;
897 std::vector<CandidateHit>& hitsLastSt {
hitsPerStation[Acts::toUnderlying(lastSt)]};
898 uint8_t nSameLayer {1u};
907 if (nSameLayer > 1u) {
908 pos2 = pos2 / nSameLayer;
935 const double varianceScale {2. * (1. - alpha + Acts::square(alpha))};
936 res.accWindow =
cfg->baseResidualSigma * std::sqrt(varianceScale);
944 <<std::tan(
lineDir.theta())<<
", Residual: "<<
res.residual<<
", Window: "<<
res.accWindow
945 <<
", alpha: "<<alpha<<
", Scale Factor: "<<std::sqrt(varianceScale));
951 const double R {toProject.perp()};
954 return Acts::PlanarHelper::intersectPlane(toProject, hit.
sensorDir,
964 <<
" is not compatible with the test hit with phi "<<
inDeg(testPhi));
968 const unsigned sector1 {
expSect.msSector()};
969 const unsigned sector2 {
expSect.adjacentMsSector()};
970 const bool isCompatible {sector1 == sector2
971 ? sectorMap.insideSector(sector1, testPhi)
972 : sectorMap.insideSector(sector1, testPhi) && sectorMap.insideSector(sector2, testPhi)};
975 <<
" is not inside the pattern sectors: "<<sector1<<
" and "<<sector2);
982 const double residual,
983 const double acceptWindow) {
996 updatePatternPhi(hit->
position.phi());
1006 if (acceptWindow > 0.) {
1018 const double newResidual,
1019 const double newAcceptWindow) {
1022 throw std::runtime_error(std::format(
1023 "Trying to overwrite a hit in station/layer {}/{} with another one from station/layer {}/{}",
1030 std::stringstream
ss {};
1031 ss <<
"Trying to overwrite a hit with incompatible type\n";
1033 ss <<
"New hit: " << **newHit <<
", isPrecision: " << newHit->
isPrecision <<
", measuresEta: " << newHit.
sp()->
measuresEta();
1034 throw std::runtime_error(
ss.str());
1054 while (!stHits.empty()) {
1059 visualInfo->replacedHits.push_back(stHits.back().sp());
1070 return std::ranges::find_if(hits,
1071 [&hit](
const CandidateHit& c){
return *c == hit; }) != hits.end();
1079 double deltaPhiAcc {0.};
1080 std::optional<double> centralPhi {};
1081 auto processPhiHit = [&deltaPhiAcc, ¢ralPhi](
const HitPayload& hit){
1082 if (!hit->measuresPhi()) {
1085 const double hitPhi {hit.position.phi()};
1087 centralPhi = hitPhi;
1092 for (
const auto& hit : hits) {
1093 processPhiHit(*hit);
1112 [](uint8_t acc,
const auto& hits){
1113 return acc + hits.size(); });
1125 std::vector<const SpacePointBucket*> buckets{};
1127 for (
const auto& hit : hits) {
1128 if (std::ranges::find(buckets, hit->bucket) == buckets.end()) {
1129 buckets.push_back(hit->bucket);
1141 for (
auto it = stationHits.rbegin(); it != stationHits.rend(); ++it) {
1155 bendPlaneNorm = Acts::makeDirectionFromPhiTheta(newPhi + 90._degree, 90._degree);
1158 const int nLayerDiff {
a.nBendingLayers() - b.nBendingLayers()};
1159 if (std::abs(nLayerDiff) >= 3) {
1160 return nLayerDiff > 0;
1162 return a.getMeanResidual2() < b.getMeanResidual2();
1167 auto getLayerOrdering = [](
const bool isLayer1Lower) {
1185 return getLayerOrdering(
isBarrel(st1)
1191 if (layer1 == layer2) {
1193 if (layer1 == LayerIndex::Middle) {
1195 return getLayerOrdering(st1 == StIndex::BM);
1197 if (layer1 == LayerIndex::Inner) {
1201 throw std::runtime_error(
"Unexpected to have two pattern-compatible hits one in BO and the other in EO.");
1203 if (layer1 == LayerIndex::Inner || layer2 == LayerIndex::Inner) {
1205 return getLayerOrdering(layer1 == LayerIndex::Inner);
1207 if (layer1 == LayerIndex::Outer || layer2 == LayerIndex::Outer) {
1209 return getLayerOrdering(layer2 == LayerIndex::Outer);
1211 if (layer1 == LayerIndex::BarrelExtended || layer2 == LayerIndex::BarrelExtended) {
1213 return getLayerOrdering(layer1 == LayerIndex::BarrelExtended);
1216 if (layer1 == LayerIndex::Extended) {
1217 return getLayerOrdering(st2 == StIndex::EM);
1219 return getLayerOrdering(st1 == StIndex::BM);
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) {
1234 return std::abs(tubeNum1-tubeNum2) < 2;
1243 std::vector<const SpacePointBucket*> buckets{cache.getParentBuckets()};
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;
1252 visualInfo->push_back(*cache.visualInfo);
1254 std::ranges::copy(buckets, std::back_inserter(visualInfo->back().parentBuckets));
1256 visualInfo->back().patternCopy = std::make_unique<GlobalPattern>(std::move(pattern));
1257 visualInfo->back().status = status;
1260 return hit == other.hit;
1263 ostr<<**
hit<<
", glob Z/R/phi: "<<
hit->position.z()<<
" / "<<
hit->position.perp()<<
" / "
1264 <<
inDeg(
hit->position.phi())<<
", st: " <<
station <<
", loc/glob lay: "
1265 <<
static_cast<int>(
hit->locLayer)<<
"/"<<
static_cast<int>(
globLayer);
1268 ostr<<
"PatternState Exp Sector: "<<
static_cast<int>(
expSect.sector())
1272 ostr<<
", Hit per station: \n";
1275 if (hits.empty())
continue;
1277 ostr<<
" Station "<<
static_cast<StIndex>(st)<<
" has "<<hits.size()<<
" hits ";
1280 for (
const auto& hit : hits) {
1281 ostr<<
" "<<hit<<
"\n";
1299 v.pat.print(os, v.detailed);
const PlainObject unit() const
This is a plugin that makes Eigen look like CLHEP & defines some convenience methods.
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
#define PRINT_VERBOSE(MSG)
Helper macro for printing verbose messages for debugging.
double angle(const GeoTrf::Vector2D &a, const GeoTrf::Vector2D &b)
constexpr float inDeg(const float rad)
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.
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
std::unordered_map< StIndex, std::vector< const SpacePointBucket * > > BucketCollection
: 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
std::string toString(const Translation3D &translation, int precision=4)
GeoPrimitvesToStringConverter.
Eigen::Affine3d Transform3D
Eigen::Matrix< double, 3, 1 > Vector3D
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)
StIndex
enum to classify the different station layers in the muon spectrometer
bool isBarrel(const ChIndex index)
Returns true if the chamber index points to a barrel chamber.
const std::string & stName(StIndex index)
convert StIndex into a string
LayerIndex toLayerIndex(ChIndex index)
convert ChIndex into LayerIndex
double deltaPhi(double phiA, double phiB)
delta Phi in range [-pi,pi[
void swap(ElementLinkVector< DOBJ > &lhs, ElementLinkVector< DOBJ > &rhs)
MdtDriftCircle_v1 MdtDriftCircle
Small wrapper for candidate hits used to build patterns.
void print(std::ostream &ostr) const
bool isStraw
Is straw hit.
uint8_t globLayer
Global measurement layer number.
const SpacePoint * sp() const
const HitPayload * hit
Pointer to the underlying hit.
StIndex station
Station index.
Hit information stored during pattern building.
Amg::Vector3D position
Global position.
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.
Amg::Vector3D sensorDir
Sensor direction in global frame.
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.
void updatePatternPhi(const double newPhi)
Helper method to update the pattern phi and bending plane normal.
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.
Amg::Vector3D projToPhiPlane(const HitPayload &hit) const
Project a certain hit position onto the bending plane where the pattern is defined.
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.
Amg::Vector3D linePos
Position and direction of the pattern line.
bool useBeamspot
Whether we used the beamspot to compute the line parameters.
CandidateHit seedHit
Seed hit.
double leverArm
Distance between the two points defining the pattern line.
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.
std::vector< const SpacePointBucket * > getParentBuckets() const
Get the buckets associated with the pattern.
bool isFinalized
Flag to indicate if the pattern has been finalized.
Amg::Vector3D bendPlaneNorm
Normal vector to the bending plane where the pattern lies.
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
Last hit in the second-to-last layer.
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.
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
Line anchor hit.
const Config * cfg
Pointer to cfg option.
double patPhi
Pattern phi, which is the phi of the bending plane where the pattern lies.
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
Last inserted hit.
double getMeanResidual2() const
Return the mean normalized residual squared.