ATLAS Offline Software
Loading...
Searching...
No Matches
PhiHoughTransformAlg.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
6
8
9
14#include "Acts/Utilities/Helpers.hpp"
15
16namespace {
17 constexpr double resetVal = 1.e10;
18}
19
20
21namespace MuonR4{
22using namespace SegmentFit;
23
25 ATH_CHECK(m_geoCtxKey.initialize());
26 ATH_CHECK(m_maxima.initialize());
27 ATH_CHECK(m_segmentSeeds.initialize());
28 ATH_CHECK(m_visionTool.retrieve(EnableTool{!m_visionTool.empty()}));
29 ATH_CHECK(m_idHelperSvc.retrieve());
30 return StatusCode::SUCCESS;
31}
33 HoughPlaneConfig cfg;
34 cfg.nBinsX = m_nBinsTanPhi;
35 cfg.nBinsY = m_nBinsIntercept;
36 // configure the peak finder for the phi-extension.
37 // Expect "shallow" maxima with 2-3 hits.
38 ActsPeakFinderForMuonCfg peakFinderCfg;
39 peakFinderCfg.fractionCutoff = 0.4;
40 peakFinderCfg.threshold = 2; // 2D spacepoints receive a weight of 2
41 peakFinderCfg.minSpacingBetweenPeaks = {0., 30.};
42 data.houghPlane = std::make_unique<HoughPlane>(cfg);
43 data.peakFinder = std::make_unique<ActsPeakFinderForMuon>(peakFinderCfg);
44}
45
46int PhiHoughTransformAlg::countIncompatibleEtaHits(const ActsPeakFinderForMuon::Maximum& phiMaximum,
47 const HoughMaximum& etaMaximum) const {
48 std::unordered_map<const xAOD::UncalibratedMeasurement*, bool> foundEtas;
49 // loop over the original eta maximum and check all hits measuring the eta-coordinate
50 for (auto& hit : etaMaximum.getHitsInMax()) {
51 if (hit->measuresEta() && hit->measuresPhi()) {
52 auto [iter, added] = foundEtas.emplace(hit->primaryMeasurement(), false);
53 // test if the PRD for the eta-measurement appears on at least
54 // one space point of this phi extension.
55 // This will be done for all space points containing the eta-PRD
56 iter->second |= phiMaximum.hitIdentifiers.count(hit);
57 }
58 }
59 // count the number of eta PRD not compatible with this extension.
60 return std::ranges::count_if(foundEtas,
61 [](const std::pair<const xAOD::UncalibratedMeasurement*, bool>& p) {
62 return !p.second;
63 });
64}
65std::unique_ptr<SegmentSeed>
67 const ActsPeakFinderForMuon::Maximum & phiMax) const {
68 // book a new hit list
69 std::vector<HoughHitType> hitsOnMax{};
70 // copy the pure eta hits onto the hit list
71 std::ranges::copy_if(etaMax.getHitsInMax(),
72 std::back_inserter(hitsOnMax), [](const HoughHitType &hit){
73 return (hit->measuresEta() && !hit->measuresPhi());
74 });
75 // and then add all hits (2D and pure phi) from the phi-extension to it
76 hitsOnMax.insert(hitsOnMax.end(), phiMax.hitIdentifiers.begin(), phiMax.hitIdentifiers.end());
77 // use this to construct the segment seed
79 std::ranges::stable_sort(hitsOnMax, sorter);
80 return std::make_unique<SegmentSeed>(etaMax.tanBeta(), etaMax.interceptY(), phiMax.x, phiMax.y, hitsOnMax.size(), std::move(hitsOnMax), etaMax.parentBucket());
81}
82
84 const HoughMaximum & maximum,
85 HoughEventData& eventData) const{
86 // reset the event data
87 eventData.phiHitsOnMax = 0;
88 eventData.searchSpaceTanAngle = std::make_pair(resetVal, -resetVal);
89 eventData.searchSpaceIntercept = std::make_pair(resetVal, -resetVal);
90 // loop over the measurements on the maximum
91 for (auto hit : maximum.getHitsInMax()) {
92 // reject the pure eta measurements - not relevant here
93 if (!hit->measuresPhi()) {
94 continue;
95 }
96 // find the direction of the IP viewed from the sector frame
97 const Amg::Vector3D extrapDir = (hit->localPosition() - hit->msSector()->globalToLocalTrans(gctx).translation()).unit();
98 ATH_MSG_VERBOSE("Direction "<<Amg::toString(extrapDir));
99 // express the x location of our phi hits on the chamber plane (z = 0) when projecting from the beam spot
100 std::optional<double> dummyIntercept = Amg::intersect<3>(hit->localPosition(), extrapDir, Amg::Vector3D::UnitZ(),0);
101 double x0 = (hit->localPosition() + dummyIntercept.value_or(0) * extrapDir).x();
102 // now we can obtain the most likely tan(phi) via the pointing vector from the origin to our hit
103 double tanAlpha = houghTanAlpha(extrapDir);
104 // update our search space with this info
105 eventData.updateSearchWindow(eventData.searchSpaceTanAngle, tanAlpha);
106 eventData.updateSearchWindow(eventData.searchSpaceIntercept, x0);
107 // and increment the hit counter
108 ++eventData.phiHitsOnMax;
109 }
110 // now use the results from the individual hits to define an axis range adapted to the binning we desire
111 double chamberCenter = (eventData.searchSpaceIntercept.second + eventData.searchSpaceIntercept.first) * 0.5;
112 double searchStart = chamberCenter - 0.5 * eventData.houghPlane->nBinsY() * m_targetResoIntercept;
113 double searchEnd = chamberCenter + 0.5 * eventData.houghPlane->nBinsY() * m_targetResoIntercept;
114 // Protection for very wide buckets - if the search space does not cover all of the bucket, widen the bin size
115 // so that we cover everything
116 searchStart = std::min(searchStart, eventData.searchSpaceIntercept.first- m_minSigmasSearchIntercept * m_targetResoIntercept);
117 searchEnd = std::max(searchEnd, eventData.searchSpaceIntercept.second + m_minSigmasSearchIntercept * m_targetResoIntercept);
118 // also treat tan(phi)
119 double tanPhiMean = 0.5 * (eventData.searchSpaceTanAngle.first + eventData.searchSpaceTanAngle.second);
120 double searchStartTanPhi = tanPhiMean - 0.5 * eventData.houghPlane->nBinsX() * m_targetResoTanPhi;
121 double searchEndTanPhi = tanPhiMean + 0.5* eventData.houghPlane->nBinsX() * m_targetResoTanPhi;
122 searchStartTanPhi = std::min(searchStartTanPhi, eventData.searchSpaceTanAngle.first- m_minSigmasSearchTanPhi * m_targetResoTanPhi);
123 searchEndTanPhi = std::max(searchEndTanPhi, eventData.searchSpaceTanAngle.second + m_minSigmasSearchTanPhi * m_targetResoTanPhi);
124
125 // and update the axis ranges for the search space according to our results
126 eventData.currAxisRanges =
127 Acts::HoughTransformUtils::HoughAxisRanges{searchStartTanPhi, searchEndTanPhi, searchStart, searchEnd};
128 ATH_MSG_VERBOSE("Accumulator search window: tanAlpha: ["<<searchStartTanPhi<<";"<<searchEndTanPhi<<"], x0: ["
129 <<searchStart<<";"<<searchEnd<<"]");
130}
131
132std::vector<ActsPeakFinderForMuon::Maximum>
134 HoughEventData & eventData, const HoughMaximum & maximum) const{
135 std::unordered_map<int, std::vector<ActsPeakFinderForMuon::Maximum>> rankedSeeds;
136 using namespace std::placeholders;
137 // reset the accumulator
138 eventData.houghPlane->reset();
139 // fill the accumulator with the phi measurements
140 for (auto hit : maximum.getHitsInMax()){
141 if (!hit->measuresPhi()) {
142 ATH_MSG_VERBOSE("Hit "<<m_idHelperSvc->toString(hit->identify())<<" does not have a phi measurement");
143 continue;
144 }
145 ATH_MSG_VERBOSE("Fill hit "<<m_idHelperSvc->toString(hit->identify())<<", "<<Amg::toString(hit->localPosition()));
146 eventData.houghPlane->fill<HoughHitType>(
147 hit, eventData.currAxisRanges,
150 // up-weigh 2D spacepoints w.r.t 1D phi hits to prevent
151 // discarding measurements known to be compatible in eta
152 (hit->measuresEta() ? 2.0 : 1.0) / (m_downWeightMultiplePrd? hit->nPhiInstanceCounts() : 1)
153 );
154 }
155 // run the peak finder
156 auto foundMaxPhi = eventData.peakFinder->findPeaks(*eventData.houghPlane, eventData.currAxisRanges);
157 if (m_visionTool.isEnabled()) {
158 m_visionTool->visualizeAccumulator(ctx,*eventData.houghPlane, eventData.currAxisRanges, foundMaxPhi,
159 "#phi accumulator");
160 }
161 // now rank the found peaks by the number of eta-compatible PRDs they would discard.
162 for (const auto& solution : foundMaxPhi) {
163 // for each solution, count how many eta PRDs would not be compatible with this phi extension.
164 // rank by the lowest number of such "holes".
165 rankedSeeds[countIncompatibleEtaHits(solution, maximum)].push_back(solution);
166 }
167 // return only the best solution(s)
168 auto best = rankedSeeds.begin();
169 // and apply the maximum hole cut.
170 if (best != rankedSeeds.end() && best->first <= m_maxEtaHolesOnMax){
171 return best->second;
172 }
173 return {};
174}
175std::unique_ptr<SegmentSeed>
177 // recovers cases of a single phi hit assuming a straight
178 // line extrapolation from the beam line to the phi measurement
179 std::vector<HoughHitType> hits{maximum.getHitsInMax()};
181 std::ranges::stable_sort(hits, sorter);
182 return std::make_unique<SegmentSeed>(maximum.tanBeta(), maximum.interceptY(),
183 data.searchSpaceTanAngle.first,
184 data.searchSpaceIntercept.first,
185 maximum.getCounts(),
186 std::move(hits), maximum.parentBucket());
187
188}
189
190
191StatusCode PhiHoughTransformAlg::execute(const EventContext& ctx) const {
192
193 // read the inputs
194 const EtaHoughMaxContainer* maxima{nullptr};
195 ATH_CHECK(SG::get(maxima, m_maxima, ctx));
196
197 const ActsTrk::GeometryContext* gctx{nullptr};
198 ATH_CHECK(SG::get(gctx, m_geoCtxKey, ctx));
199
200 // book the event data object
201 HoughEventData eventData{};
202
203 // prepare the accumulator
204 prepareHoughPlane(eventData);
205
206 // prepare our output collection
207 SG::WriteHandle writeMaxima{m_segmentSeeds, ctx};
208 ATH_CHECK(writeMaxima.record(std::make_unique<SegmentSeedContainer>()));
209
210 // loop over the previously found eta-maxima for each station
211 for (const HoughMaximum* max : *maxima) {
212 // for each maximum, pre-process
213 ATH_MSG_VERBOSE("Search extra phi hits on maximum "<<max->msSector()->identString()<<", tanBeta: "<<max->tanBeta()
214 <<", y0: "<<max->interceptY());
215 if (m_visionTool.isEnabled() && msgLvl(MSG::VERBOSE)) {
216 for (const auto& truth : m_visionTool->getLabeledSegments(max->getHitsInMax())) {
217 const Parameters truthPars = localSegmentPars(*truth);
218 ATH_MSG_VERBOSE("Truth parameters "<<toString(truthPars)<<", tanAlpha: "
219 <<houghTanAlpha(Amg::dirFromAngles(truthPars[Acts::toUnderlying(ParamDefs::phi)],
220 truthPars[Acts::toUnderlying(ParamDefs::theta)])));
221 }
222 }
223 preProcessMaximum(*gctx, *max, eventData);
224 bool foundSolution=false;
225 // if we have enough hits, run a phi transform
226 if (eventData.phiHitsOnMax > 1){
227 std::vector<ActsPeakFinderForMuon::Maximum> rankedSeeds = findRankedSegmentSeeds(ctx, eventData, *max);
228 for (auto & phiSolution : rankedSeeds){
229 foundSolution = true;
230 const SegmentSeed* seed {writeMaxima->push_back(buildSegmentSeed(*max, phiSolution))};
231 if (m_visionTool.isEnabled()) {
232 m_visionTool->visualizeSeed(ctx, *seed, "#phi pattern seed");
233 }
234 }
235 }
236 ATH_MSG_VERBOSE("Solution found: "<<foundSolution);
237 // if we do not have at least two phi-hits for a proper transform:
238 if (!foundSolution){
239 // if we have a single phi hit, we can approximate the phi
240 // solution using the beam spot (as the IP is far).
241 // This is steered by a flag, and not appropriate for splashes
242 // or cosmics.
243 if (m_recoverSinglePhiWithBS && eventData.phiHitsOnMax == 1){
244 const SegmentSeed* singleMax{writeMaxima->push_back(recoverSinglePhiMax(eventData,*max))};
245 if (m_visionTool.isEnabled()) {
246 m_visionTool->visualizeSeed(ctx, *singleMax, "Single #phi hit recovery");
247 }
248 }
249 // otherwise we have no phi-solution, and we fall back to writing a 1D eta-maximum
250 else{
251 writeMaxima->push_back(std::make_unique<SegmentSeed>(*max));
252 }
253 }
254 }
255 // add the maxima for this station to the output
256
257 return StatusCode::SUCCESS;
258}
259}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_VERBOSE(x)
char data[hepevt_bytes_allocation_ATLAS]
Definition HepEvt.cxx:11
#define max(a, b)
Definition cfImp.cxx:41
bool msgLvl(const MSG::Level lvl) const
Data class to represent an eta maximum in hough space.
double interceptY() const
getter
double getCounts() const
getter
double tanBeta() const
getter
const std::vector< HitType > & getHitsInMax() const
getter
const SpacePointBucket * parentBucket() const
getter
SG::ReadHandleKey< EtaHoughMaxContainer > m_maxima
ServiceHandle< Muon::IMuonIdHelperSvc > m_idHelperSvc
Handle to the IdHelperSvc.
virtual StatusCode execute(const EventContext &ctx) const override
SG::ReadHandleKey< ActsTrk::GeometryContext > m_geoCtxKey
virtual StatusCode initialize() override
SG::WriteHandleKey< SegmentSeedContainer > m_segmentSeeds
void preProcessMaximum(const ActsTrk::GeometryContext &gctx, const MuonR4::HoughMaximum &maximum, HoughEventData &data) const
pre-processing for a given input eta-maximum Counts potential phi-hits and defines the search space
void prepareHoughPlane(HoughEventData &data) const
prepare the hough plane once per event.
std::unique_ptr< SegmentSeed > buildSegmentSeed(const HoughMaximum &etaMax, const MuonR4::ActsPeakFinderForMuon::Maximum &phiMax) const
constructs a segment seed from an eta maximum and a phi-extension.
std::unique_ptr< SegmentSeed > recoverSinglePhiMax(HoughEventData &data, const MuonR4::HoughMaximum &maximum) const
extend an eta maximum with just a single attached phi measurement.
std::vector< MuonR4::ActsPeakFinderForMuon::Maximum > findRankedSegmentSeeds(const EventContext &ctx, HoughEventData &data, const MuonR4::HoughMaximum &maximum) const
perform a hough search for the most promising phi extension of an eta-maximum Performs a local hough ...
int countIncompatibleEtaHits(const MuonR4::ActsPeakFinderForMuon::Maximum &phiMaximum, const MuonR4::HoughMaximum &etaMaximum) const
helper to count the number of eta measurements that would be discarded for a given phi extension cand...
ToolHandle< MuonValR4::IPatternVisualizationTool > m_visionTool
Pattern visualization tool.
Representation of a segment seed (a fully processed hough maximum) produced by the hough transform.
Definition SegmentSeed.h:14
The SpacePointPerLayerSorter sort two given space points by their layer Identifier.
bool measuresEta() const
: Does the space point contain an eta measurement
unsigned nPhiInstanceCounts() const
How many space points have been built in total with the same phi prd.
StatusCode record(std::unique_ptr< T > data)
Record a const object to the store.
std::optional< double > intersect(const AmgVector(N)&posA, const AmgVector(N)&dirA, const AmgVector(N)&posB, const AmgVector(N)&dirB)
Calculates the point B' along the line B that's closest to a second line A.
std::string toString(const Translation3D &translation, int precision=4)
GeoPrimitvesToStringConverter.
Amg::Vector3D dirFromAngles(const double phi, const double theta)
Constructs a direction vector from the azimuthal & polar angles.
Eigen::Matrix< double, 3, 1 > Vector3D
double houghWidthStrip(double tanAlpha, const MuonR4::HoughHitType &dc, double targetReso)
Uncertainty parametrisation for strip measurements.
double houghParamStrip(double tanAlpha, const MuonR4::HoughHitType &dc)
straight line parametrisation for strip detector measurements, in the x-direction
Parameters localSegmentPars(const xAOD::MuonSegment &seg)
Returns the localSegPars decoration from a xAODMuon::Segment.
Acts::Experimental::CompositeSpacePointLineFitter::ParamVec_t Parameters
std::string toString(const Parameters &pars)
Dumps the parameters into a string with labels in front of each number.
This header ties the generic definitions in this package.
DataVector< HoughMaximum > EtaHoughMaxContainer
HoughEventData_impl< ActsPeakFinderForMuon, ActsPeakFinderForMuonCfg > HoughEventData
Acts::HoughTransformUtils::PeakFinders::IslandsAroundMaxConfig ActsPeakFinderForMuonCfg
double houghTanAlpha(const Amg::Vector3D &v)
: Returns the hough tanAlpha [x] / [z]
const SpacePoint * HoughHitType
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
std::pair< double, double > searchSpaceIntercept
static void updateSearchWindow(std::pair< double, double > &searchWindow, double value)
Updates a search space window to account for a value.
std::unique_ptr< peakFinder_t > peakFinder
std::unique_ptr< HoughPlane > houghPlane
std::pair< double, double > searchSpaceTanAngle
Acts::HoughTransformUtils::HoughAxisRanges currAxisRanges