ATLAS Offline Software
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 
5 #include "PhiHoughTransformAlg.h"
6 
8 
9 
14 #include "Acts/Utilities/Helpers.hpp"
15 
16 namespace {
17  constexpr double resetVal = 1.e10;
18 }
19 
20 
21 namespace MuonR4{
22 using 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 
46 int 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 }
65 std::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 
132 std::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,
149  std::bind(HoughHelpers::Phi::houghWidthStrip, _1, _2, m_targetResoIntercept), hit, 0,
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 }
175 std::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 
191 StatusCode 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 }
MuonR4::PhiHoughTransformAlg::buildSegmentSeed
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.
Definition: PhiHoughTransformAlg.cxx:66
MuonR4::PhiHoughTransformAlg::prepareHoughPlane
void prepareHoughPlane(HoughEventData &data) const
prepare the hough plane once per event.
Definition: PhiHoughTransformAlg.cxx:32
data
char data[hepevt_bytes_allocation_ATLAS]
Definition: HepEvt.cxx:11
createLinkingScheme.iter
iter
Definition: createLinkingScheme.py:62
MuonR4::PhiHoughTransformAlg::initialize
virtual StatusCode initialize() override
Definition: PhiHoughTransformAlg.cxx:24
TRTCalib_Extractor.hits
hits
Definition: TRTCalib_Extractor.py:35
MuonR4::HoughEventData_impl::peakFinder
std::unique_ptr< peakFinder_t > peakFinder
Definition: HoughEventData.h:37
phi
Scalar phi() const
phi method
Definition: AmgMatrixBasePlugin.h:67
MuonR4::HoughHelpers::Phi::houghParamStrip
double houghParamStrip(double tanAlpha, const MuonR4::HoughHitType &dc)
straight line parametrisation for strip detector measurements, in the x-direction
Definition: HoughHelperFunctions.cxx:35
MuonR4::HoughEventData_impl::phiHitsOnMax
size_t phiHitsOnMax
Definition: HoughEventData.h:67
MuonR4::HoughEventData_impl::searchSpaceTanAngle
std::pair< double, double > searchSpaceTanAngle
Definition: HoughEventData.h:65
MuonR4::SpacePointPerLayerSorter
The SpacePointPerLayerSorter sort two given space points by their layer Identifier.
Definition: SpacePointPerLayerSorter.h:15
max
constexpr double max()
Definition: ap_fixedTest.cxx:33
MuonR4::PhiHoughTransformAlg::preProcessMaximum
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
Definition: PhiHoughTransformAlg.cxx:83
min
constexpr double min()
Definition: ap_fixedTest.cxx:26
EventPrimitivesHelpers.h
theta
Scalar theta() const
theta method
Definition: AmgMatrixBasePlugin.h:75
HoughHelperFunctions.h
xAOD::etaMax
etaMax
Definition: HIEventShape_v2.cxx:46
postInclude.sorter
sorter
Definition: postInclude.SortInput.py:23
MuonR4::ActsPeakFinderForMuonCfg
Acts::HoughTransformUtils::PeakFinders::IslandsAroundMaxConfig ActsPeakFinderForMuonCfg
Definition: MuonHoughDefs.h:32
ATH_MSG_VERBOSE
#define ATH_MSG_VERBOSE(x)
Definition: AthMsgStreamMacros.h:28
SpectrometerSector.h
x
#define x
MuonR4::HoughMaximum::getHitsInMax
const std::vector< HitType > & getHitsInMax() const
getter
Definition: HoughMaximum.h:46
MuonR4::PhiHoughTransformAlg::countIncompatibleEtaHits
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...
Definition: PhiHoughTransformAlg.cxx:46
python.utils.AtlRunQueryDQUtils.p
p
Definition: AtlRunQueryDQUtils.py:209
Amg::toString
std::string toString(const Translation3D &translation, int precision=4)
GeoPrimitvesToStringConverter.
Definition: GeoPrimitivesToStringConverter.h:40
MuonR4::HoughEventData_impl::searchSpaceIntercept
std::pair< double, double > searchSpaceIntercept
Definition: HoughEventData.h:63
SG::get
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
Definition: ReadCondHandle.h:287
EL::StatusCode
::StatusCode StatusCode
StatusCode definition for legacy code.
Definition: PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/StatusCode.h:22
MuonR4::HoughMaximum::parentBucket
const SpacePointBucket * parentBucket() const
getter
Definition: HoughMaximum.h:51
MuonR4::SegmentFit::Parameters
Acts::Experimental::CompositeSpacePointLineFitter::ParamVec_t Parameters
Definition: MuonHoughDefs.h:46
python.utils.best
def best(iterable, priorities=[3, 2, 1, -1, 0])
Definition: DataQuality/DQUtils/python/utils.py:49
AnalysisUtils::copy_if
Out copy_if(In first, const In &last, Out res, const Pred &p)
Definition: IFilterUtils.h:30
ATH_CHECK
#define ATH_CHECK
Definition: AthCheckMacros.h:40
MuonR4::SegmentFit::toString
std::string toString(const Parameters &pars)
Dumps the parameters into a string with labels in front of each number.
Definition: SegmentFitterEventData.cxx:73
MuonR4::HoughMaximum::tanBeta
double tanBeta() const
getter
Definition: HoughMaximum.h:36
ActsTrk::GeometryContext
Definition: GeometryContext.h:28
MuonR4::HoughEventData_impl::currAxisRanges
Acts::HoughTransformUtils::HoughAxisRanges currAxisRanges
Definition: HoughEventData.h:61
MuonR4::PhiHoughTransformAlg::findRankedSegmentSeeds
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 ...
Definition: PhiHoughTransformAlg.cxx:133
DataVector
Derived DataVector<T>.
Definition: DataVector.h:795
MuonR4::SpacePoint
The muon space point is the combination of two uncalibrated measurements one of them measures the eta...
Definition: MuonSpectrometer/MuonPhaseII/Event/MuonSpacePoint/MuonSpacePoint/SpacePoint.h:24
PhiHoughTransformAlg.h
MuonR4::HoughMaximum::getCounts
double getCounts() const
getter
Definition: HoughMaximum.h:43
WriteCaloSwCorrections.cfg
cfg
Definition: WriteCaloSwCorrections.py:23
Amg::Vector3D
Eigen::Matrix< double, 3, 1 > Vector3D
Definition: GeoPrimitives.h:47
Amg::dirFromAngles
Amg::Vector3D dirFromAngles(const double phi, const double theta)
Constructs a direction vector from the azimuthal & polar angles.
Definition: GeoPrimitivesHelpers.h:299
MuonR4
This header ties the generic definitions in this package.
Definition: HoughEventData.h:16
MuonR4::PhiHoughTransformAlg::execute
virtual StatusCode execute(const EventContext &ctx) const override
Definition: PhiHoughTransformAlg.cxx:191
SG::WriteHandle
Definition: StoreGate/StoreGate/WriteHandle.h:73
MuonR4::HoughHelpers::Phi::houghWidthStrip
double houghWidthStrip(double tanAlpha, const MuonR4::HoughHitType &dc, double targetReso)
Uncertainty parametrisation for strip measurements.
Definition: HoughHelperFunctions.cxx:38
MuonR4::HoughMaximum
Data class to represent an eta maximum in hough space.
Definition: HoughMaximum.h:14
SegmentFitterEventData.h
unit
const PlainObject unit() const
This is a plugin that makes Eigen look like CLHEP & defines some convenience methods.
Definition: AmgMatrixBasePlugin.h:21
MuonR4::SegmentSeed
Representation of a segment seed (a fully processed hough maximum) produced by the hough transform.
Definition: SegmentSeed.h:14
MuonR4::HoughMaximum::interceptY
double interceptY() const
getter
Definition: HoughMaximum.h:40
MuonR4::SegmentFit::localSegmentPars
Parameters localSegmentPars(const xAOD::MuonSegment &seg)
Returns the localSegPars decoration from a xAODMuon::Segment.
Definition: SegmentFitterEventData.cxx:42
MuonR4::PhiHoughTransformAlg::recoverSinglePhiMax
std::unique_ptr< SegmentSeed > recoverSinglePhiMax(HoughEventData &data, const MuonR4::HoughMaximum &maximum) const
extend an eta maximum with just a single attached phi measurement.
Definition: PhiHoughTransformAlg.cxx:176
MuonR4::HoughEventData_impl::houghPlane
std::unique_ptr< HoughPlane > houghPlane
Definition: HoughEventData.h:35
python.Constants.VERBOSE
int VERBOSE
Definition: Control/AthenaCommon/python/Constants.py:13
MuonR4::HoughEventData_impl::updateSearchWindow
static void updateSearchWindow(std::pair< double, double > &searchWindow, double value)
Updates a search space window to account for a value.
Definition: HoughEventData.h:29
MuonR4::houghTanAlpha
double houghTanAlpha(const Amg::Vector3D &v)
: Returns the hough tanAlpha [x] / [z]
Definition: SegmentFitterEventData.cxx:30
SpacePointPerLayerSorter.h
MuonR4::HoughEventData_impl
Templated event data class for the phase-2 muon hough transform.
Definition: HoughEventData.h:22