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)
bool hit(const Container &ids, int pdgId)
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...
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