ATLAS Offline Software
Loading...
Searching...
No Matches
GlobalPatternFinderDefs.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
6
10#include "Acts/Utilities/Logger.hpp"
11
12
13
14namespace {
15 const Muon::MuonSectorMapping sectorMap{};
16
20 Amg::Vector3D phiGradient(const Amg::Vector3D& pos) {
21 return Amg::Vector3D{-pos.y(), pos.x(), 0.} / pos.perp2();
22 }
24 constexpr double inDeg(double angle) {
25 return angle / Gaudi::Units::deg;
26 }
27}
28
29namespace MuonR4::FastReco {
30 using namespace Acts::UnitLiterals;
31
32 GlobalPatternFinder::HitPayload::HitPayload(const Acts::GeometryContext& gctx,
33 const SpacePoint* sp,
35 const Amg::Transform3D& localToGlobal)
36 : position{localToGlobal * sp->localPosition()},
38
39 using CovIdx = SpacePoint::CovIdx;
40
41 if (!sp->measuresEta()) {
42 // Phi-only measurements
43 const Amg::Vector3D phiMeasDir {localToGlobal.rotation() * sp->toNextSensor()};
44
45 phiCov = sp->covariance()[Acts::toUnderlying(CovIdx::phiCov)] *
46 Acts::square(phiMeasDir.dot(phiGradient(position)));
47 return;
48 }
49 const auto& surfLinearTrf = xAOD::muonSurface(sp->primaryMeasurement()).localToGlobalTransform(gctx).linear();
50
51 if (sp->isStraw()) {
52 // Remember that for straw hits, the x component of secondaryMeasDir is repurposed
53 // to store the transverse covariance of the drift radius
54 double& discCov = stripAngle;
55 discCov = Acts::square(sp->driftRadius()) +
56 sp->covariance()[Acts::toUnderlying(CovIdx::etaCov)];
57
58 if (sp->measuresPhi()) {
59 phiCov = discCov / Acts::square(position.perp()) +
60 Acts::square(sensorDir(gctx).dot(phiGradient(position))) *
61 (sp->covariance()[Acts::toUnderlying(CovIdx::phiCov)] - discCov);
62 }
63 } else if (sp->measuresPhi()) {
64 const Amg::Vector3D phiMeasDir = surfLinearTrf.col(Amg::y);
65 const Amg::Vector3D gradPhi {phiGradient(position)};
67 auto oneDimContribution = [&](CovIdx idx, const Amg::Vector3D& measDir) -> double {
68 return sp->covariance()[Acts::toUnderlying(idx)] *
69 Acts::square(measDir.dot(gradPhi));
70 };
71
72 // Handle the case of TGC separately
74 const Amg::Vector3D etaMeasDir = localToGlobal.rotation() * sp->toNextSensor();
75 const Amg::Vector3D phiSensorDir = surfLinearTrf.col(Amg::x);
76
77 const double c {etaMeasDir.dot(phiMeasDir)};
78 if (std::abs(c) > Acts::s_epsilon) {
79 stripAngle = std::atan2(etaMeasDir.dot(phiSensorDir), c);
80 nonOrthogonalStrips = true;
81 }
82 phiCov = oneDimContribution(CovIdx::etaCov, etaMeasDir) +
83 oneDimContribution(CovIdx::phiCov, phiMeasDir);
84 } else {
85 const Amg::Vector3D etaMeasDir = surfLinearTrf.col(Amg::x);
86 phiCov = oneDimContribution(CovIdx::etaCov, etaMeasDir) +
87 oneDimContribution(CovIdx::phiCov, phiMeasDir);
88 }
89 }
90 }
91 Amg::Vector3D GlobalPatternFinder::HitPayload::sensorDir(const Acts::GeometryContext& gctx) const {
92 const auto& surfLinearTrf =
93 xAOD::muonSurface(spacePoint->primaryMeasurement()).localToGlobalTransform(gctx).linear();
94
95 if (spacePoint->isStraw()) {
96 return surfLinearTrf.col(Amg::z);
97 } else {
99 return - std::sin(stripAngle) * surfLinearTrf.col(Amg::y)
100 + std::cos(stripAngle) * surfLinearTrf.col(Amg::x);
101 }
102 return surfLinearTrf.col(Amg::y);
103 }
104 }
105 double
106 GlobalPatternFinder::HitPayload::residualVariance(const Acts::GeometryContext& gctx,
107 const Amg::Vector3D& contractionVector,
108 const bool isProjected) const {
109 using CovIdx = SpacePoint::CovIdx;
110
112 assert(isProjected || std::abs(contractionVector.mag() - 1.0) < Acts::s_epsilon);
113
114 if (spacePoint->isStraw()) {
115 const double discCov {stripAngle};
116 if (!isProjected) {
117 assert(sp->measuresPhi());
118 const double vDotRsq {Acts::square(sensorDir(gctx).dot(contractionVector))};
119 return discCov * (1 - vDotRsq) +
120 vDotRsq * spacePoint->covariance()[Acts::toUnderlying(CovIdx::phiCov)];
121 }
125 return discCov * contractionVector.mag2();
126
127 } else if (spacePoint->measuresEta()) {
128 const auto& surfLinearTrf = xAOD::muonSurface(spacePoint->primaryMeasurement()).localToGlobalTransform(gctx).linear();
130 auto oneDimContribution = [&](CovIdx idx, const Amg::Vector3D& measDir) -> double {
131 return spacePoint->covariance()[Acts::toUnderlying(idx)] *
132 Acts::square(measDir.dot(contractionVector));
133 };
134 if (spacePoint->measuresPhi()) {
135 const Amg::Vector3D etaMeasDir {nonOrthogonalStrips
136 ? sensorDir(gctx).cross(surfLinearTrf.col(Amg::z))
137 : surfLinearTrf.col(Amg::x)};
138 const Amg::Vector3D phiMeasDir {surfLinearTrf.col(Amg::y)};
139 return oneDimContribution(CovIdx::etaCov, etaMeasDir) +
140 oneDimContribution(CovIdx::phiCov, phiMeasDir);
141 }
142 const Amg::Vector3D etaMeasDir {surfLinearTrf.col(Amg::x)};
143 return oneDimContribution(CovIdx::etaCov, etaMeasDir);
144 } else {
145 throw std::runtime_error("Phi only hits are not meant to be used for residual computation.");
146 }
147 }
149 return spacePoint == other.spacePoint;
150 }
151
153 const std::int8_t expSector,
154 const Config* cfg,
155 const Acts::Logger* logger)
156 : cfg{cfg},
158 lastInsertedHit{seed},
159 prevLayerHit{seed},
160 lineAnchorHit{seed},
161 patTheta{seed->position.theta()},
162 expSect{ExpandedSector{expSector}} {
163
165 hitsPerStation[Acts::toUnderlying(seed->station)].push_back(seed);
166
168 if (seed->isPrecision) nPrecisionLayers++;
169 else nTriggerLayers++;
170
171 if (seed.sp()->measuresPhi()) nPhiLayers++;
172
174 needLineUpdate = true;
175 }
177 const CandidateHit& testHit,
178 const Amg::Vector3D& beamSpot) {
179
180 if (testHit.sp()->measuresPhi() && !isPhiCompatible(*testHit)) {
181 ACTS_VERBOSE(__func__<<"() Test hit phi "<<testHit->position.phi()
182 <<" not compatible with "<<brief(*this));
183 return LineTestRes{};
184 }
185
189 auto makeResult = [&](const LineTestDecision decision) -> LineTestRes {
190 LineTestRes res{computeLineResidual(gctx, testHit)};
191 double accWindow {cfg->nResidualSigma * res.sigma};
194 if (useBeamspot || testHit->station != lastInsertedHit->station ||
195 (testHit->station != prevLayerHit->station && testHit.globLayer == lastInsertedHit.globLayer)) {
196 accWindow *= 2.;
197 }
198 if (res.residual < accWindow) {
199 res.result = decision;
200 }
201 if (visualInfo) {
202 visualInfo->hitLineInfo[testHit.sp()] =
203 std::make_pair(std::tan(lineDir.theta()), accWindow);
204 }
205 return res;
206 };
207
208 if(testHit.globLayer != lastInsertedHit.globLayer) {
209 updateLineParameters(gctx, beamSpot);
210 return makeResult(LineTestDecision::eAddHit);
211 }
212 if (testHit == lastInsertedHit) {
213 ACTS_VERBOSE(__func__<<"() Test hit is the same as last inserted hit - reject.");
214 return LineTestRes{};
215 }
216 if (lineAnchorHit.globLayer == lastInsertedHit.globLayer) {
217 ACTS_VERBOSE(__func__<<"() Test hit on same layer as seed with no prior hits - reject.");
218 return LineTestRes{};
219 }
220 return makeResult(LineTestDecision::eBranchPattern);
221 }
223 // Treat first the special case where we have only one station
224 if (nStations(/*onlyGoodStations=*/ false) < 2) {
225 // If we call this method with only one station, it means that we inverted the hit search direction without
226 // finding any hit in other stations beside the initial one. So the anchor is the last added hit.
228 return;
229 }
230 // Find first the closest station to the reference station among the pattern stations
231 const auto& closestStIt = std::ranges::min_element(hitsPerStation, std::ranges::less{},
232 [&refHit](const auto& hits){
233 if (hits.empty() || hits.front()->station == refHit->station) {
234 return std::numeric_limits<int>::max();
235 }
236 return std::abs(hits.front().globLayer - refHit.globLayer);
237 });
238
239 // Then find the closest hit in that station to the reference hit
240 const auto& hits {*closestStIt};
241 lineAnchorHit = *std::ranges::min_element(hits, std::ranges::less{},
242 [&refHit](const CandidateHit& hit){
243 return std::abs(hit.globLayer - refHit.globLayer); });
244 }
245 void GlobalPatternFinder::PatternState::updateLineParameters(const Acts::GeometryContext& gctx,
246 const Amg::Vector3D& beamSpot) {
247 if (!needLineUpdate) {
248 return;
249 }
252 Amg::Vector3D d {pos2 - pos1};
253 leverArm = d.mag();
254
256 useBeamspot = (lastInsertedHit->station == lineAnchorHit->station) &&
257 leverArm < cfg->minHitDistance4Line;
258 if (useBeamspot) {
259 linePos = beamSpot;
260 d = pos2 - beamSpot;
261 leverArm = d.mag();
262 } else {
263 linePos = pos1;
264 }
265 lineDir = d / leverArm;
266 needLineUpdate = false;
267
268 ACTS_VERBOSE(__func__<<"() Updated --> linePos R/z/theta: "<<linePos.perp()<<" / "<<linePos.z()
269 <<" / "<<inDeg(linePos.theta())<<", lineDir theta: "<<inDeg(lineDir.theta())
270 <<", LeverArm: "<<leverArm<<", Use beamspot: "<<useBeamspot);
271 }
273 const CandidateHit& testHit) const {
275
279 const bool projectTestHit {!testHit.sp()->measuresPhi() || nPhiLayers == 0u};
280 const Amg::Vector3D testPos {projectTestHit ? projToPhiPlane(gctx,*testHit)
281 : testHit->position};
282 const Amg::Vector3D K {testPos - linePos};
283 const double KdotD {K.dot(lineDir)};
284
285 const Amg::Vector3D R {K - KdotD * lineDir}; // Residual vector
286 res.residual = R.mag();
287 if (res.residual < Acts::s_epsilon) {
289 res.residual = std::numeric_limits<double>::max();
290 res.sigma = 0.;
291 return res;
292 }
293 const Amg::Vector3D resDir {R / res.residual}; // Residual direction
295 const double alpha {KdotD / leverArm};
296
298 double phiPlaneDerivativeAcc {0.};
300 double residualCovAcc {0.};
301
317 auto covarianceTerm = [&](const HitPayload& hit,
318 const Amg::Vector3D& pos,
319 const double preFactor,
320 bool isProjected) -> void {
321
322 if (!isProjected) {
323 residualCovAcc += Acts::square(preFactor) *
324 hit.residualVariance(gctx, resDir, /*isProjected=*/false);
325 return;
326 }
327 const Amg::Vector3D sensorDir {hit.sensorDir(gctx)};
328 const double projFactor {sensorDir.dot(resDir) /
329 sensorDir.dot(bendPlaneNorm)};
330 const Amg::Vector3D trfDir {resDir - projFactor * bendPlaneNorm};
331
332 residualCovAcc += Acts::square(preFactor)
333 * hit.residualVariance(gctx, trfDir, /*isProjected=*/true);
334 phiPlaneDerivativeAcc += preFactor * pos.perp() * projFactor;
335 };
336
338 if (useBeamspot) {
339 const double covS1 = cfg->beamSpotLength * Acts::square(resDir.z()) +
340 cfg->beamSpotRadius * (1 - Acts::square(resDir.z()));
341 residualCovAcc += Acts::square(alpha - 1) * covS1;
342 } else {
343 covarianceTerm(*lineAnchorHit, linePos, alpha - 1, /*isProjected=*/true);
344 }
345
347 const Amg::Vector3D pos2 {linePos + leverArm * lineDir};
348 covarianceTerm(*lastInsertedHit, pos2, -alpha, /*isProjected=*/true);
349
351 covarianceTerm(*testHit, testPos, 1., projectTestHit);
352
353 res.sigma = std::sqrt(residualCovAcc + Acts::square(phiPlaneDerivativeAcc) * patPhiCov);
354
355 ACTS_VERBOSE(__func__<<"() "<< brief(*this)<<"\nUse beamspot: "<<useBeamspot
356 <<", alpha: "<<alpha<<", Residual: "<<res.residual<<" +- "<<res.sigma
357 <<", linePos R/theta: "<<linePos.perp()<<" / "<<inDeg(linePos.theta())
358 <<", lineDir theta: "<<inDeg(lineDir.theta())
359 <<", testPos R/theta/phi: "<<testPos.perp()<<" / "<<inDeg(testPos.theta())<<" / "<<inDeg(testPos.phi())
360 <<", resDir theta/phi: "<<inDeg(resDir.theta())<<" / "<<inDeg(resDir.phi())
361 <<", hit pos sigma: "<<std::sqrt(residualCovAcc)
362 <<", phi plane sigma: "<<std::abs(phiPlaneDerivativeAcc)*std::sqrt(patPhiCov));
363 return res;
364 }
366 const HitPayload& hit) const {
367 return Acts::PlanarHelper::intersectPlane(hit.position, hit.sensorDir(gctx),
368 bendPlaneNorm, Amg::Vector3D::Zero()).position();
369 }
371 if (!nPhiLayers) {
374 patPhi = sectorMap.sectorOverlapPhi(expSect.msSector(),
375 expSect.adjacentMsSector());
376 patPhiCov = Acts::square(expSect.sectorSize()) / 3.;
377 bendPlaneNorm = Acts::makeDirectionFromPhiTheta(patPhi + 90._degree, 90._degree);
378 ACTS_VERBOSE(__func__<<"() No phi hits in the pattern, set pattern phi to "
379 <<inDeg(patPhi)<<" +- "<<inDeg(std::sqrt(patPhiCov)));
380 return;
381 }
382 double sumSin{0.}, sumCos{0.}, sumWeight{0.};
383
384 auto processPhiHit = [&sumSin, &sumCos, &sumWeight](const HitPayload& hit){
385 if (!hit->measuresPhi()) {
386 return;
387 }
388 if (hit.phiCov < Acts::s_epsilon) {
389 std::stringstream ss {};
390 ss << "Unexpected to have a phi hit with zero variance in phi direction: " << *hit.spacePoint << "\n";
391 throw std::runtime_error(ss.str());
392 }
393 const double w = 1./hit.phiCov;
394
395 const double phi {hit.position.phi()};
396 sumSin += w * std::sin(phi);
397 sumCos += w * std::cos(phi);
398 sumWeight += w;
399 };
400 for (const std::vector<CandidateHit>& hits : hitsPerStation) {
401 for (const auto& hit : hits) {
402 processPhiHit(*hit);
403 }
404 }
405 for (const HitPayload& hit : phiOnlyHits) {
406 processPhiHit(hit);
407 }
408
409 patPhi = std::atan2(sumSin, sumCos);
410 patPhiCov = 1./sumWeight;
411 bendPlaneNorm = Acts::makeDirectionFromPhiTheta(patPhi + 90._degree, 90._degree);
412 ACTS_VERBOSE(__func__<<"() Updated pattern phi to "
413 <<inDeg(patPhi)<<" +- "<<inDeg(std::sqrt(patPhiCov)));
414 }
419 const double testPhi {hit.position.phi()};
420 if (nPhiLayers) {
421 const double deltaPhiSigma {std::sqrt(patPhiCov + hit.phiCov)};
422 const double deltaPhi {P4Helpers::deltaPhi(patPhi, testPhi)};
423 if (std::abs(deltaPhi) > cfg->nPhiSigma * deltaPhiSigma) {
424 ACTS_VERBOSE(__func__<<"() The pattern with phi = "<<inDeg(patPhi)<<" +- "<<inDeg(std::sqrt(patPhiCov))
425 <<" is not compatible with the test hit with phi "<<inDeg(testPhi) <<" +- "<<inDeg(std::sqrt(hit.phiCov)));
426 return false;
427 }
428 } else {
429 const unsigned sector1 {expSect.msSector()};
430 const unsigned sector2 {expSect.adjacentMsSector()};
431 const bool isCompatible {sector1 == sector2
432 ? sectorMap.insideSector(sector1, testPhi)
433 : sectorMap.insideSector(sector1, testPhi) && sectorMap.insideSector(sector2, testPhi)};
434 if (!isCompatible) {
435 ACTS_VERBOSE(__func__<<"() The test hit with phi = "<<inDeg(testPhi)
436 <<" is not inside the pattern sectors: "<<sector1<<" and "<<sector2);
437 return false;
438 }
439 }
440 return true;
441 }
443 const double residual,
444 const double resSigma) {
446 hitsPerStation[Acts::toUnderlying(hit->station)].push_back(hit);
447
449 if (hit->isPrecision) nPrecisionLayers++;
450 else nTriggerLayers++;
451
452 if (hit.sp()->measuresPhi()) {
453 nPhiLayers++;
455 }
456
458 const bool isNewStation {hit->station != lastInsertedHit->station};
461
462 meanNormResidual2 += Acts::square(residual / resSigma);
463 lastResSigma = resSigma;
464 lastResidual = residual;
465
467 if (isNewStation) {
469 }
470 needLineUpdate = true;
471 }
473 const double newResidual,
474 const double newResSigma) {
475 const StIndex st {newHit->station};
476 if (st != lastInsertedHit->station || lastInsertedHit.globLayer != newHit.globLayer) {
477 throw std::runtime_error(std::format(
478 "Trying to overwrite a hit in station/layer {}/{} with another one from station/layer {}/{}",
479 stName(lastInsertedHit->station), lastInsertedHit.globLayer, stName(st), newHit.globLayer));
480 }
481 /* We expect to overwrite hits of the same type (precision/trigger), since we only branch when we have
482 * compatible hits in the same layer, except for sTGC hits, where we have pad and strips in the same layer */
483 if (lastInsertedHit->isPrecision != newHit->isPrecision) {
484 if (newHit.sp()->type() != xAOD::UncalibMeasType::sTgcStripType) {
485 std::stringstream ss {};
486 ss << "Trying to overwrite a hit with incompatible type\n";
487 ss << "Old hit: " << **lastInsertedHit << ", isPrecision: " << lastInsertedHit->isPrecision << ", measuresEta: " << lastInsertedHit.sp()->measuresEta() << "\n";
488 ss << "New hit: " << **newHit << ", isPrecision: " << newHit->isPrecision << ", measuresEta: " << newHit.sp()->measuresEta();
489 throw std::runtime_error(ss.str());
490 }
491 if (newHit->isPrecision) {
494 } else {
497 }
498 }
500 bool updatePhi {false};
501 if (lastInsertedHit.sp()->measuresPhi()) {
502 nPhiLayers--;
503 updatePhi = true;
504 }
505 if (newHit.sp()->measuresPhi()) {
506 nPhiLayers++;
507 updatePhi = true;
508 }
510 meanNormResidual2 += Acts::square(newResidual / newResSigma) -
511 Acts::square(lastResidual / lastResSigma);
512 lastResSigma = newResSigma;
513 lastResidual = newResidual;
514
516 if (visualInfo) {
517 visualInfo->replacedHits.push_back(lastInsertedHit.sp());
518 }
519 auto& stHits {hitsPerStation[Acts::toUnderlying(st)]};
520 if (stHits.back() != lastInsertedHit) {
521 std::stringstream ss {};
522 ss << "Trying to overwrite a hit that is not the last inserted hit in station/layer "
523 << stName(st) << "/" << lastInsertedHit.globLayer << "\n";
524 ss << "Last inserted hit: " << **lastInsertedHit << "\n";
525 ss << "Last hit in station: " << **stHits.back();
526 throw std::runtime_error(ss.str());
527 }
528 stHits.pop_back();
529
531 stHits.push_back(newHit);
532 lastInsertedHit = newHit;
533
534 if (updatePhi) {
536 }
537 needLineUpdate = true;
538 }
540 const auto& hits {hitsPerStation[Acts::toUnderlying(hit.station)]};
541 return std::ranges::find_if(hits,
542 [&hit](const CandidateHit& c){ return *c == hit; }) != hits.end();
543 }
544 uint8_t GlobalPatternFinder::PatternState::nStations(const bool onlyGoodStations) const {
545 uint8_t nStations {0u};
546 for (uint8_t st{0u}; st < s_nStations; ++st) {
547 const auto& hits {hitsPerStation[st]};
548 if (!hits.empty() &&
549 (!onlyGoodStations || hits.size() >= cfg->minStationLayers)) {
550 nStations++;
551 }
552 }
553 return nStations;
554 }
564 std::vector<const SpacePointBucket*>
566 std::vector<const SpacePointBucket*> buckets{};
567 for (const std::vector<CandidateHit>& hits : hitsPerStation) {
568 for (const auto& hit : hits) {
569 if (std::ranges::find(buckets, hit->bucket) == buckets.end()) {
570 buckets.push_back(hit->bucket);
571 }
572 }
573 }
574 return buckets;
575 }
576 void GlobalPatternFinder::PatternState::print(std::ostream& ostr, bool detailed) const {
577 ostr<<"PatternState Exp Sector: "<<static_cast<int>(expSect.sector())
578 <<", Theta: "<<inDeg(patTheta) << ", Phi: "<<inDeg(patPhi)<<" +- "<<inDeg(std::sqrt(patPhiCov));
579 ostr<<", nPrec: "<<static_cast<int>(nPrecisionLayers)<<", nEtaNonPrec: "
580 <<static_cast<int>(nTriggerLayers)<<", nPhi: "<<static_cast<int>(nPhiLayers);
581 ostr<<", mean norma res sq: "<<getMeanResidual2()<<", dirTheta: "<<inDeg(lineDir.theta());
582 ostr<<", Hit per station: \n";
583 for (uint8_t st{0u}; st < s_nStations; ++st) {
584 const auto& hits {hitsPerStation[st]};
585 if (hits.empty()) continue;
586
587 ostr<<" Station "<<static_cast<StIndex>(st)<<" has "<<hits.size()<<" hits ";
588 if (detailed) {
589 ostr<<"\n";
590 for (const auto& hit : hits) {
591 ostr<<" "<<hit<<"\n";
592 }
593 }
594 }
595 if (!detailed) {
596 ostr <<"\n Last hit: "<<lastInsertedHit<<"\n prevLayerHit: "
597 <<prevLayerHit << "\n lineAnchorHit: "<<lineAnchorHit;
598 }
599 }
600
601 void GlobalPatternFinder::CandidateHit::print(std::ostream& ostr) const {
602 ostr<<*sp()<<", glob Z/R/phi: "<<hit->position.z()<<" / "<<hit->position.perp()<<" / "
603 <<inDeg(hit->position.phi())<< ", st: " << hit->station <<", loc/glob lay: "
604 <<static_cast<int>(hit->locLayer)<<"/"<<static_cast<int>(globLayer);
605 }
606
609 return {p, /*detailed=*/false};
610 }
613 return {p, /*detailed=*/true};
614 }
615}
Scalar deltaPhi(const MatrixBase< Derived > &vec) const
Scalar phi() const
phi method
Scalar theta() const
theta method
std::pair< std::vector< unsigned int >, bool > res
bool hit(const Container &ids, int pdgId)
static Double_t sp
static Double_t ss
if(pathvar)
double angle(const GeoTrf::Vector2D &a, const GeoTrf::Vector2D &b)
constexpr float inDeg(const float rad)
static PatternPrintView brief(const PatternState &p)
Print the pattern state with brief information.
Muon::MuonStationIndex::StIndex StIndex
Type alias for the station index.
static const int s_nStations
Number of stations.
static PatternPrintView detailed(const PatternState &p)
Print the pattern state with detailed information.
: The muon space point bucket represents a collection of points that will bre processed together in t...
The muon space point is the combination of two uncalibrated measurements one of them measures the eta...
bool measuresPhi() const
: Does the space point contain a phi measurement
bool measuresEta() const
: Does the space point contain an eta measurement
double sectorOverlapPhi(int sector1, int sector2) const
returns the phi position of the overlap between the two sectors (which have to be neighboring) in rad...
bool insideSector(int sector, double phi) const
checks whether the phi position is consistent with sector
Eigen::Affine3d Transform3D
Eigen::Matrix< double, 3, 1 > Vector3D
LineTestDecision
: Enum for possible outcomes of pattern line compatibility test
@ eBranchPattern
Test successfull with multiple pattern hits on same layer, branch the pattern.
@ eAddHit
Test successfull, add hit to pattern.
constexpr float inDeg(const float rad)
const std::string & stName(StIndex index)
convert StIndex into a string
double deltaPhi(double phiA, double phiB)
delta Phi in range [-pi,pi[
Definition P4Helpers.h:34
Definition dot.py:1
const Acts::Surface & muonSurface(const UncalibratedMeasurement *meas)
Returns the associated Acts surface to the measurement.
Small wrapper for candidate hits used to build patterns.
uint8_t globLayer
Global measurement layer number.
const HitPayload * hit
Pointer to the underlying hit.
Configuration object for the patter finder.
Base class for hit struct containing hit information.
double phiCov
Cached angular covariance [rad^2] of the hit in the phi angle.
HitPayload(const Acts::GeometryContext &gctx, const SpacePoint *sp, const SpacePointBucket *bucket, const Amg::Transform3D &localToGlobal)
Constructor with parameters.
const SpacePoint * spacePoint
Pointer to the underlying hit.
const SpacePointBucket * bucket
Pointer to the parent bucket.
double residualVariance(const Acts::GeometryContext &gctx, const Amg::Vector3D &contractionVector, const bool isProjected) const
Hit contribution contribution to the residual variance due to its intrinsic position uncertainty.
Amg::Vector3D sensorDir(const Acts::GeometryContext &gctx) const
Sensor direction.
bool operator==(const HitPayload &other) const
Equal operator: it compares the underlying hit.
double stripAngle
Strip angle when the strips are non-orthogonal.
A view of the pattern state for printing purposes.
Pattern state object storing pattern information during construction.
PatternState(const CandidateHit &seed, const std::int8_t expSector, const Config *cfg, const Acts::Logger *logger)
Constructor taking the seed information.
void moveLineAnchorHit(const CandidateHit &refHit)
Move the line anchor hit given a reference hit.
double lastResidual
Residual & residual uncertainty of the last inserted hit (needed when replacing a hit).
double meanNormResidual2
Mean over eta hits of the square of their residual divided by residual uncertainty.
Amg::Vector3D projToPhiPlane(const Acts::GeometryContext &gctx, const HitPayload &hit) const
Project a certain hit position onto the bending plane where the pattern is defined.
bool isInPattern(const HitPayload &hit) const
Check wheter a hit is present in the pattern.
void updateLineParameters(const Acts::GeometryContext &gctx, const Amg::Vector3D &beamSpot)
Update the line parameters based on the current hits.
uint8_t nStations(const bool onlyGoodStations) const
Method returning the number of stations.
Amg::Vector3D linePos
Position and direction of the pattern line.
bool useBeamspot
Whether we used the beamspot to compute the line parameters.
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.
std::vector< HitPayload > phiOnlyHits
Array holding phi-only hits.
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.
uint8_t nBendingLayers() const
Return the number of layers in bending coordinate.
Acts::CloneablePtr< PatHitVisual > visualInfo
Pointer to Visual Information for pattern visualization.
CandidateHit prevLayerHit
Last hit in the second-to-last layer.
void addHit(const CandidateHit &hit, const double residual, const double resSigma)
Add a hit to the pattern and update the internal state.
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.
LineTestRes checkLineComp(const Acts::GeometryContext &gctx, const CandidateHit &testHit, const Amg::Vector3D &beamSpot)
Method checking line compatibility of a test hit against the pattern.
bool isPhiCompatible(const HitPayload &hit) const
Method to check the phi compatibility of a test hit with a given pattern.
void updatePatternPhi()
Helper method to update the pattern phi and bending plane normal.
void overWriteHit(const CandidateHit &newHit, const double newResidual, const double newResSigma)
Overwrite the hits on the last layer with the new one.
double patPhi
Pattern phi, which is the phi of the bending plane where the pattern lies.
const Acts::Logger & logger() const
Return the logger.
uint8_t nPrecisionLayers
Counts of precision / non-precision / phi layers.
LineTestRes computeLineResidual(const Acts::GeometryContext &gctx, const CandidateHit &testHit) const
Method to compute the residual of a test hit against the pattern line.
double getMeanResidual2() const
Return the mean normalized residual squared.
double patTheta
Pattern theta, which is the value of the seed hit.
: Small struct to encapsulate the result of the line compatibility test