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()->globalToLocalTransform(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
190std::unique_ptr<SegmentSeed>
192 const HoughMaximum& etaMax) const {
194 return std::make_unique<SegmentSeed>(etaMax);
195 }
196
197
198 const double tanBeta = etaMax.tanBeta();
199 const double iceptY = etaMax.interceptY();
200
201 double iceptX{0.}, tanAlpha{0.};
202 unsigned counts{0};
203
204 const Amg::Vector3D bsPos = etaMax.parentBucket()->msSector()->globalToLocalTransform(gctx).translation();
205
206 for (const SpacePoint* sp : etaMax.getHitsInMax()) {
207 const double spTanAlpha = houghTanAlpha(bsPos - sp->localPosition());
208 const double spX = HoughHelpers::Phi::houghParamStrip(spTanAlpha, sp);
209 iceptX += spX;
210 tanAlpha+=spTanAlpha;
211 ++counts;
212 }
213 if (std::abs(tanAlpha) < Acts::s_epsilon) {
214 return std::make_unique<SegmentSeed>(etaMax);
215 }
216 tanAlpha /= counts;
217 iceptX /= counts;
218 auto hits = etaMax.getHitsInMax();
219 return std::make_unique<SegmentSeed>(tanBeta, iceptY, tanAlpha, iceptX, etaMax.getCounts(),
220 std::move(hits), etaMax.parentBucket());
221}
222
223StatusCode PhiHoughTransformAlg::execute(const EventContext& ctx) const {
224
225 // read the inputs
226 const EtaHoughMaxContainer* maxima{nullptr};
227 ATH_CHECK(SG::get(maxima, m_maxima, ctx));
228
229 const ActsTrk::GeometryContext* gctx{nullptr};
230 ATH_CHECK(SG::get(gctx, m_geoCtxKey, ctx));
231
232 // book the event data object
233 HoughEventData eventData{};
234
235 // prepare the accumulator
236 prepareHoughPlane(eventData);
237
238 // prepare our output collection
239 SG::WriteHandle writeMaxima{m_segmentSeeds, ctx};
240 ATH_CHECK(writeMaxima.record(std::make_unique<SegmentSeedContainer>()));
241
242 // loop over the previously found eta-maxima for each station
243 for (const HoughMaximum* max : *maxima) {
244 // for each maximum, pre-process
245 ATH_MSG_VERBOSE("Search extra phi hits on maximum "<<max->msSector()->identString()<<", tanBeta: "<<max->tanBeta()
246 <<", y0: "<<max->interceptY());
247 if (m_visionTool.isEnabled() && msgLvl(MSG::VERBOSE)) {
248 for (const auto& truth : m_visionTool->getLabeledSegments(max->getHitsInMax())) {
249 const Parameters truthPars = localSegmentPars(*truth);
250 ATH_MSG_VERBOSE("Truth parameters "<<toString(truthPars)<<", tanAlpha: "
251 <<houghTanAlpha(Amg::dirFromAngles(truthPars[Acts::toUnderlying(ParamDefs::phi)],
252 truthPars[Acts::toUnderlying(ParamDefs::theta)])));
253 }
254 }
255 preProcessMaximum(*gctx, *max, eventData);
256 bool foundSolution=false;
257 // if we have enough hits, run a phi transform
258 if (eventData.phiHitsOnMax > 1){
259 std::vector<ActsPeakFinderForMuon::Maximum> rankedSeeds = findRankedSegmentSeeds(ctx, eventData, *max);
260 for (auto & phiSolution : rankedSeeds){
261 foundSolution = true;
262 const SegmentSeed* seed {writeMaxima->push_back(buildSegmentSeed(*max, phiSolution))};
263 if (m_visionTool.isEnabled()) {
264 m_visionTool->visualizeSeed(ctx, *seed, "#phi pattern seed");
265 }
266 }
267 }
268 ATH_MSG_VERBOSE("Solution found: "<<foundSolution);
269 // if we do not have at least two phi-hits for a proper transform:
270 if (!foundSolution){
271 // if we have a single phi hit, we can approximate the phi
272 // solution using the beam spot (as the IP is far).
273 // This is steered by a flag, and not appropriate for splashes
274 // or cosmics.
275 if (m_recoverSinglePhiWithBS && eventData.phiHitsOnMax == 1){
276 const SegmentSeed* singleMax{writeMaxima->push_back(recoverSinglePhiMax(eventData,*max))};
277 if (m_visionTool.isEnabled()) {
278 m_visionTool->visualizeSeed(ctx, *singleMax, "Single #phi hit recovery");
279 }
280 }
281 // otherwise we have no phi-solution, and we fall back to writing a 1D eta-maximum
282 else{
283 writeMaxima->push_back(buildPhiLessSeed(*gctx, *max));
284 }
285 }
286 }
287 // add the maxima for this station to the output
288
289 return StatusCode::SUCCESS;
290}
291}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_VERBOSE(x)
static Double_t sp
#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
std::unique_ptr< SegmentSeed > buildPhiLessSeed(const ActsTrk::GeometryContext &gctx, const HoughMaximum &etaMax) const
Constructs the segment seed without phi hits.
SG::ReadHandleKey< EtaHoughMaxContainer > m_maxima
ServiceHandle< Muon::IMuonIdHelperSvc > m_idHelperSvc
Handle to the IdHelperSvc.
BooleanProperty m_refinePhiLessWithBS
Flag to steer whether the phi parameters of the seeds without phi hits are refined using the beamspot...
virtual StatusCode execute(const EventContext &ctx) const override
virtual StatusCode initialize() override
ActsTrk::GeoContextReadKey_t m_geoCtxKey
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.
The muon space point is the combination of two uncalibrated measurements one of them measures the eta...
unsigned nPhiInstanceCounts() const
How many space points have been built in total with the same phi prd.
bool measuresEta() const
: Does the space point contain an eta measurement
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 &strip)
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