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);
210 iceptX = sp->localPosition().x();
211 tanAlpha = spTanAlpha;
212 iceptX = spX;
213 counts = 1;
214 break;
215 }
216 iceptX += spX;
217 tanAlpha+=spTanAlpha;
218 ++counts;
219 }
220 tanAlpha /= counts;
221 iceptX /= counts;
222 auto hits = etaMax.getHitsInMax();
223 return std::make_unique<SegmentSeed>(tanBeta, iceptY, tanAlpha, iceptX, etaMax.getCounts(),
224 std::move(hits), etaMax.parentBucket());
225}
226
227StatusCode PhiHoughTransformAlg::execute(const EventContext& ctx) const {
228
229 // read the inputs
230 const EtaHoughMaxContainer* maxima{nullptr};
231 ATH_CHECK(SG::get(maxima, m_maxima, ctx));
232
233 const ActsTrk::GeometryContext* gctx{nullptr};
234 ATH_CHECK(SG::get(gctx, m_geoCtxKey, ctx));
235
236 // book the event data object
237 HoughEventData eventData{};
238
239 // prepare the accumulator
240 prepareHoughPlane(eventData);
241
242 // prepare our output collection
243 SG::WriteHandle writeMaxima{m_segmentSeeds, ctx};
244 ATH_CHECK(writeMaxima.record(std::make_unique<SegmentSeedContainer>()));
245
246 // loop over the previously found eta-maxima for each station
247 for (const HoughMaximum* max : *maxima) {
248 // for each maximum, pre-process
249 ATH_MSG_VERBOSE("Search extra phi hits on maximum "<<max->msSector()->identString()<<", tanBeta: "<<max->tanBeta()
250 <<", y0: "<<max->interceptY());
251 if (m_visionTool.isEnabled() && msgLvl(MSG::VERBOSE)) {
252 for (const auto& truth : m_visionTool->getLabeledSegments(max->getHitsInMax())) {
253 const Parameters truthPars = localSegmentPars(*truth);
254 ATH_MSG_VERBOSE("Truth parameters "<<toString(truthPars)<<", tanAlpha: "
255 <<houghTanAlpha(Amg::dirFromAngles(truthPars[Acts::toUnderlying(ParamDefs::phi)],
256 truthPars[Acts::toUnderlying(ParamDefs::theta)])));
257 }
258 }
259 preProcessMaximum(*gctx, *max, eventData);
260 bool foundSolution=false;
261 // if we have enough hits, run a phi transform
262 if (eventData.phiHitsOnMax > 1){
263 std::vector<ActsPeakFinderForMuon::Maximum> rankedSeeds = findRankedSegmentSeeds(ctx, eventData, *max);
264 for (auto & phiSolution : rankedSeeds){
265 foundSolution = true;
266 const SegmentSeed* seed {writeMaxima->push_back(buildSegmentSeed(*max, phiSolution))};
267 if (m_visionTool.isEnabled()) {
268 m_visionTool->visualizeSeed(ctx, *seed, "#phi pattern seed");
269 }
270 }
271 }
272 ATH_MSG_VERBOSE("Solution found: "<<foundSolution);
273 // if we do not have at least two phi-hits for a proper transform:
274 if (!foundSolution){
275 // if we have a single phi hit, we can approximate the phi
276 // solution using the beam spot (as the IP is far).
277 // This is steered by a flag, and not appropriate for splashes
278 // or cosmics.
279 if (m_recoverSinglePhiWithBS && eventData.phiHitsOnMax == 1){
280 const SegmentSeed* singleMax{writeMaxima->push_back(recoverSinglePhiMax(eventData,*max))};
281 if (m_visionTool.isEnabled()) {
282 m_visionTool->visualizeSeed(ctx, *singleMax, "Single #phi hit recovery");
283 }
284 }
285 // otherwise we have no phi-solution, and we fall back to writing a 1D eta-maximum
286 else{
287 writeMaxima->push_back(buildPhiLessSeed(*gctx, *max));
288 }
289 }
290 }
291 // add the maxima for this station to the output
292
293 return StatusCode::SUCCESS;
294}
295}
#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
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
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.
The muon space point is the combination of two uncalibrated measurements one of them measures the eta...
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 &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