ATLAS Offline Software
Loading...
Searching...
No Matches
MdtSegmentSeedGenerator.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
9
10#include <Acts/Utilities/Enumerate.hpp>
11#include <Acts/Definitions/Units.hpp>
12#include <Acts/Utilities/UnitVectors.hpp>
13
14#include <CxxUtils/sincos.h>
15#include <format>
16
17using namespace Acts;
18using namespace Acts::UnitLiterals;
19namespace MuonR4::SegmentFit{
22 constexpr auto covIdx = Acts::toUnderlying(AxisDefs::etaCov);
23
24
28 return static_cast<const xAOD::MdtDriftCircle*>(prd)->status();
29 }
30 return Muon::MdtDriftCircleStatus::MdtStatusUnDefined;
31 }
32
33 inline bool isGoodDC(const SpacePoint& dc) {
34 return dcStatus(dc) == Muon::MdtDriftCircleStatus::MdtStatusDriftTime;
35 }
36
39 inline bool moveToNextHit(const HitVec& hits, std::size_t& hitIdx) {
40 while(++hitIdx < hits.size() && !isGoodDC(*hits[hitIdx])) {
41 }
42 return hitIdx < hits.size() && isGoodDC(*hits[hitIdx]);
43 }
44
47 inline bool firstGoodHit(const HitVec& hits, std::size_t& hitIdx) {
48 hitIdx = 0;
49 return isGoodDC(*hits[hitIdx]) || moveToNextHit(hits, hitIdx);
50 }
51
52 std::ostream& MdtSegmentSeedGenerator::SeedSolution::print(std::ostream& ostr) const{
53 ostr<<"two circle solution with ";
54 ostr<<"theta: "<<(theta / 1._degree) <<" pm "<<(dTheta / 1._degree)<<", ";
55 ostr<<"y0: "<<y0<<" pm "<<dY0;
56 return ostr;
57 }
61
64 const SegmentSeed* segmentSeed,
65 const Config& configuration):
66 AthMessaging{name},
67 m_cfg{configuration},
68 m_segmentSeed{segmentSeed} {
69
70 if (m_hitLayers.mdtHits().empty()) return;
71
72 if (std::ranges::any_of(m_hitLayers.mdtHits(), [this](const HitVec& vec){
73 return vec.size() > m_cfg.busyLayerLimit;
74 })) {
75 m_cfg.startWithPattern = false;
76 }
77 // Set the start for the upper layer
78 m_upperLayer = m_hitLayers.mdtHits().size()-1;
79
81 while (m_lowerLayer < m_upperLayer){
82 const HitVec& lower{m_hitLayers.mdtHits()[m_lowerLayer]};
83 if (lower.size() > m_cfg.busyLayerLimit || !firstGoodHit(lower, m_lowerHitIndex)) {
85 } else {
86 break;
87 }
88
89 }
91 while (m_lowerLayer < m_upperLayer){
92 const HitVec& upper{m_hitLayers.mdtHits()[m_upperLayer]};
93 if (upper.size() > m_cfg.busyLayerLimit || !firstGoodHit(upper, m_upperHitIndex)) {
95 } else {
96 break;
97 }
98
99 }
100
101 if (msgLvl(MSG::VERBOSE)) {
102 std::stringstream sstr{};
103 for (const auto [layCount, layer] : Acts::enumerate(m_hitLayers.mdtHits())) {
104 sstr<<"Mdt-hits in layer "<<layCount<<": "<<layer.size()<<std::endl;
105 for (const HoughHitType& hit : layer) {
106 sstr<<" **** "<<(*hit)<<std::endl;
107 }
108 }
109 for (const auto [layCount, layer] : Acts::enumerate(m_hitLayers.stripHits())) {
110 sstr<<"Hits in layer "<<layCount<<": "<<layer.size()<<std::endl;
111 for (const HoughHitType& hit : layer) {
112 sstr<<" **** "<<(*hit)<<std::endl;
113 }
114 }
115 ATH_MSG_VERBOSE("SeedGenerator - sorting of hits done. Mdt layers: "<<m_hitLayers.mdtHits().size()
116 <<", strip layers: "<<m_hitLayers.stripHits().size()<<std::endl<<sstr.str()<<std::endl<<std::endl);
117 }
118 }
119
121 return m_nGenSeeds;
122 }
124 const HitVec& lower = m_hitLayers.mdtHits()[m_lowerLayer];
125 const HitVec& upper = m_hitLayers.mdtHits()[m_upperLayer];
127 if (++m_signComboIndex < s_signCombos.size()) {
128 return;
129 }
130 m_signComboIndex = 0;
131
133 if (moveToNextHit(lower, m_lowerHitIndex)) {
134 return;
135 }
137 if (firstGoodHit(lower, m_lowerHitIndex)) {
140 return;
141 }
142 }
145 while (m_lowerLayer < m_upperLayer) {
146 const HitVec& nextLower{m_hitLayers.mdtHits()[++m_lowerLayer]};
147 if (nextLower.size() > m_cfg.busyLayerLimit) {
148 continue;
149 }
151 if (firstGoodHit(nextLower, m_lowerHitIndex)) {
152 break;
153 }
154 }
155
158 return;
159 }
161 if (m_lowerLayer >= m_hitLayers.firstLayerFrom2ndMl() && numGenerated()){
163 return;
164 }
166 m_lowerLayer = 0;
167 do {
169 const HitVec& nextLower{m_hitLayers.mdtHits()[m_lowerLayer]};
170 if (nextLower.size() > m_cfg.busyLayerLimit) {
171 continue;
172 }
174 if (firstGoodHit(nextLower, m_lowerHitIndex)) {
175 break;
176 }
177 } while (++m_lowerLayer < m_upperLayer);
178
179 while (m_lowerLayer < m_upperLayer) {
180 const HitVec& nextUpper{m_hitLayers.mdtHits()[--m_upperLayer]};
181 if (nextUpper.size() > m_cfg.busyLayerLimit) {
182 continue;
183 }
184 if (firstGoodHit(nextUpper, m_upperHitIndex)) {
185 break;
186 }
187 }
188 }
189 std::optional<MdtSegmentSeedGenerator::DriftCircleSeed>
190 MdtSegmentSeedGenerator::nextSeed(const EventContext& ctx) {
191 std::optional<DriftCircleSeed> found = std::nullopt;
192 if (!m_nGenSeeds && m_cfg.startWithPattern) {
193 ++m_nGenSeeds;
194 found = std::make_optional<DriftCircleSeed>();
195 found->parameters = m_segmentSeed->parameters();
196 found->measurements = m_cfg.calibrator->calibrate(ctx,
197 m_segmentSeed->getHitsInMax(),
198 m_segmentSeed->localPosition(),
199 m_segmentSeed->localDirection(),0.);
200 found->parentBucket = m_segmentSeed->parentBucket();
201 found->nMdt = std::ranges::count_if(m_segmentSeed->getHitsInMax(),
202 [](const SpacePoint* hit){
203 return hit->type() == xAOD::UncalibMeasType::MdtDriftCircleType;
204 });
205 SeedSolution patternSeed{};
206 patternSeed.seedHits = m_segmentSeed->getHitsInMax();
207 patternSeed.solutionSigns.resize(2*m_hitLayers.mdtHits().size());
208 patternSeed.y0 = m_segmentSeed->interceptY();
209 patternSeed.theta = m_segmentSeed->tanBeta();
210 m_seenSolutions.push_back(std::move(patternSeed));
211 return found;
212 }
213
214 while (m_lowerLayer < m_upperLayer) {
215 const HitVec& lower = m_hitLayers.mdtHits().at(m_lowerLayer);
216 const HitVec& upper = m_hitLayers.mdtHits().at(m_upperLayer);
217 ATH_MSG_VERBOSE("Layers with hits: "<<m_hitLayers.mdtHits().size()
218 <<" -- next bottom hit: "<<m_lowerLayer<<", hit: "<<m_lowerHitIndex
219 <<" ("<<lower.size()<<"), top hit " <<m_upperLayer<<", "<<m_upperHitIndex
220 <<" ("<<upper.size()<<") - ambiguity "
221 <<LineSeeder_t::toString(s_signCombos[m_signComboIndex]));
222
227 if (found) {
228 return found;
229 }
230 }
231 return std::nullopt;
232 }
233 Line_t::ParamVector MdtSegmentSeedGenerator::constructLinePars(const double theta, const double y0) const {
234 Line_t::ParamVector pars{};
235 pars[Acts::toUnderlying(ParamDefs::y0)] = y0;
236 pars[Acts::toUnderlying(ParamDefs::x0)] = m_segmentSeed->interceptX();
237 if (Acts::abs(m_segmentSeed->tanAlpha()) > std::numeric_limits<double>::epsilon()) {
238 const Amg::Vector3D dirFromTan = Acts::makeDirectionFromAxisTangents(m_segmentSeed->tanAlpha(),
239 std::tan(theta));
240 pars[Acts::toUnderlying(ParamDefs::phi)] = dirFromTan.phi();
241 pars[Acts::toUnderlying(ParamDefs::theta)] = dirFromTan.theta();
242 } else {
243 pars[Acts::toUnderlying(ParamDefs::phi)] = 90._degree;
244 pars[Acts::toUnderlying(ParamDefs::theta)] = theta;
245 }
246 return pars;
247 }
249 if (solution.theta < m_cfg.thetaRange[0] ||
250 solution.theta > m_cfg.thetaRange[1]) {
251 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<": The theta angle: "
252 <<(solution.theta / 1._degree)
253 <<" is out of the valid range ["<<(m_cfg.thetaRange[0] / 1._degree)
254 <<"-"<<(m_cfg.thetaRange[1] / 1._degree)<<"].");
255 return false;
256 }
257 if (solution.y0 < m_cfg.interceptRange[0] ||
258 solution.y0 > m_cfg.interceptRange[1]) {
259 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<": The intercept: "
260 <<solution.y0
261 <<" is out of the valid range ["<<m_cfg.interceptRange[0]
262 <<"-"<<m_cfg.interceptRange[1] <<"].");
263 return false;
264 }
265 return true;
266
267 }
268 std::optional<MdtSegmentSeedGenerator::DriftCircleSeed>
269 MdtSegmentSeedGenerator::buildSeed(const EventContext& ctx,
270 const HoughHitType& topHit,
271 const HoughHitType& bottomHit,
272 const TangentAmbi ambi) {
273
274 const Muon::IMuonIdHelperSvc* idHelperSvc{topHit->msSector()->idHelperSvc()};
275 if (!isGoodDC(*bottomHit) || !isGoodDC(*topHit)) {
276 THROW_EXCEPTION("Bad hit detected, despite that should have been captured upstream "
277 <<isGoodDC(*bottomHit)<<", "<<isGoodDC(*topHit)<<" - lowerLayer: "<<m_lowerLayer
278 <<", upperLayer"<<m_upperLayer<<", "<<"upperHit: "<<m_upperHitIndex<<", lowerHit: "<<m_lowerHitIndex<<" - "
279 <<m_hitLayers.mdtHits().at(m_lowerLayer).size()<<", "<<m_hitLayers.mdtHits().at(m_upperLayer).size());
280 }
281
282 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<": Attempt to construct seed from "<<idHelperSvc->toString(bottomHit->identify())
283 <<" && top tube "<<idHelperSvc->toString(topHit->identify()));
284
285 SeedSolution solCandidate{};
286 static_cast<TangentLine&>(solCandidate) = LineSeeder_t::constructTangentLine(*topHit, *bottomHit, ambi);
287
288 solCandidate.theta = LineSeeder_t::makeDirection(*bottomHit, solCandidate.theta).theta();
289
290 if (!isValidLine(solCandidate)) {
291 return std::nullopt;
292 }
293
294 std::unique_ptr<CalibratedSpacePoint> calibBottom{}, calibTop{};
296 const double t0 = m_segmentSeed->parameters()[Acts::toUnderlying(ParamDefs::t0)];
297 if (m_cfg.recalibSeedCircles) {
298 m_line.updateParameters(constructLinePars(solCandidate.theta, solCandidate.y0));
301 calibBottom = m_cfg.calibrator->calibrate(ctx, bottomHit, m_line.position(), m_line.direction(), t0);
302 calibTop = m_cfg.calibrator->calibrate(ctx, topHit, m_line.position(), m_line.direction(), t0);
303 static_cast<TangentLine&>(solCandidate) = LineSeeder_t::constructTangentLine(*calibTop, *calibBottom, ambi);
304 solCandidate.theta = LineSeeder_t::makeDirection(*calibBottom, solCandidate.theta).theta();
305 if (!isValidLine(solCandidate)) {
306 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<": Recalibrated segment seed is invalid");
307 return std::nullopt;
308 }
309 }
310
311 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<": Test new "<<solCandidate<<". "<<m_seenSolutions.size());
312
313 if (std::ranges::find_if(m_seenSolutions,
314 [&solCandidate, this] (const SeedSolution& seen) {
315 const double deltaY = std::abs(seen.y0 - solCandidate.y0);
316 const double limitY = std::hypot(seen.dY0, solCandidate.dY0);
317 const double dTheta = std::abs(seen.theta - solCandidate.theta);
318 const double limitTh = std::hypot(seen.dTheta, solCandidate.dTheta);
319 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<": "<<seen
320 <<std::format(" delta Y: {:.2f} {:} {:.2f}", deltaY, deltaY < limitY ? '<' : '>', limitY)
321 <<std::format(" delta theta: {:.2f} {:} {:.2f}", dTheta, dTheta < limitTh ? '<' : '>', limitTh) );
322 return deltaY < limitY && dTheta < limitTh;;
323 }) != m_seenSolutions.end()){
324 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<": Reject due to similarity");
325 return std::nullopt;
326 }
327
328 DriftCircleSeed candidateSeed{};
329 candidateSeed.parentBucket = m_segmentSeed->parentBucket();
330
331 const auto finalSeedPars = constructLinePars(solCandidate.theta,solCandidate.y0);
332 m_line.updateParameters(finalSeedPars);
334 for (const auto [layerNr, hitsInLayer] : Acts::enumerate(m_hitLayers.mdtHits())) {
335 ATH_MSG_VERBOSE( __func__<<"() "<<__LINE__<<": "<<hitsInLayer.size()<<" hits in layer "<<(layerNr +1));
336 bool hadGoodHit{false};
337 for (const HoughHitType testMe : hitsInLayer) {
338 using namespace Acts::detail::LineHelper;
339 const double distance = Acts::abs(signedDistance(testMe->localPosition(), testMe->sensorDirection(),
340 m_line.position(), m_line.direction()));
341 const double pull = Acts::abs(distance - testMe->driftRadius()) / std::sqrt(testMe->covariance()[covIdx]);
342
343 const auto* re = static_cast<const xAOD::MdtDriftCircle*>(testMe->primaryMeasurement())->readoutElement();
344
345 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<": Test hit "<<idHelperSvc->toString(testMe->identify())
346 <<" "<<Amg::toString(testMe->localPosition())<<", pull: "<<pull<<", distance: "<<distance);
347 if (pull < m_cfg.hitPullCut && distance < re->tubeRadius()) {
348 hadGoodHit = true;
349 solCandidate.seedHits.emplace_back(testMe);
350 candidateSeed.nMdt += isGoodDC(*testMe);
351 }
352 else if (hadGoodHit) {
353 break;
354 }
355 }
356 }
358 const unsigned hitCut = std::max(1.*m_cfg.nMdtHitCut,
359 m_cfg.nMdtLayHitCut * m_hitLayers.mdtHits().size());
360
361 if (1.*candidateSeed.nMdt < hitCut) {
362 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<": Too few hits associated "<<candidateSeed.nMdt<<", expect: "<<hitCut<<" hits.");
363 return std::nullopt;
364 }
365 /* Calculate the left-right signs of the used hits */
366 if (m_cfg.overlapCorridor) {
367 solCandidate.solutionSigns = SeedingAux::strawSigns(m_line, solCandidate.seedHits);
368 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<": Circle solutions for seed "
369 <<idHelperSvc->toStringChamber(bottomHit->identify())<<" - "<<solCandidate);
371 for (unsigned int a = m_cfg.startWithPattern; a< m_seenSolutions.size() ;++a) {
372 const SeedSolution& accepted = m_seenSolutions[a];
373 unsigned int nOverlap{0};
374 std::vector<int> corridor = SeedingAux::strawSigns(m_line, accepted.seedHits);
375 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<": Test seed against accepted "<<accepted<<", updated signs: "<<corridor);
377 for (unsigned int l = 0; l < accepted.seedHits.size(); ++l){
378 nOverlap += (corridor[l] == accepted.solutionSigns[l]);
379 }
382 if (nOverlap == corridor.size() && accepted.seedHits.size() >= solCandidate.seedHits.size()) {
383 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<": Same set of hits collected within the same corridor");
384 return std::nullopt;
385 }
386 }
387 }
388 candidateSeed.parameters[Acts::toUnderlying(ParamDefs::t0)] = t0;
389 for (const auto p : { ParamDefs::x0, ParamDefs::theta, ParamDefs::phi, ParamDefs::y0}) {
390 candidateSeed.parameters[Acts::toUnderlying(p)] = finalSeedPars[Acts::toUnderlying(p)];
391 }
393 for (const HoughHitType& hit : solCandidate.seedHits){
394 //calibBottom is nullptr after it has been moved, so...
395 //cppcheck-suppress accessMoved
396 if (hit == bottomHit && calibBottom) {
397 candidateSeed.measurements.emplace_back(std::move(calibBottom));
398 }
399 //calibTop is nullptr after it has been moved, so...
400 //cppcheck-suppress accessMoved
401 else if (hit == topHit && calibTop) {
402 candidateSeed.measurements.emplace_back(std::move(calibTop));
403 } else {
404 candidateSeed.measurements.emplace_back(m_cfg.calibrator->calibrate(ctx, hit, m_line.position(),
405 m_line.direction(), t0));
406 }
407 }
408
410 m_seenSolutions.emplace_back(std::move(solCandidate));
413 if (m_cfg.tightenHitCut) {
414 m_cfg.nMdtHitCut = std::max(m_cfg.nMdtHitCut, candidateSeed.nMdt);
415 }
416 ++m_nGenSeeds;
417 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<": In event "<<ctx.eventID().event_number()<<" found new seed solution "<<toString(candidateSeed.parameters));
418
420 for (const std::vector<HoughHitType>& hitsInLayer : m_hitLayers.stripHits()) {
421 HoughHitType bestHitLoc0{nullptr}, bestHitLoc1{nullptr};
422 double bestPullLoc0{m_cfg.hitPullCut}, bestPullLoc1{m_cfg.hitPullCut};
423 for (const HoughHitType testMe : hitsInLayer) {
424 const double pull = std::sqrt(SeedingAux::chi2Term(m_line, *testMe));
425 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<": Test hit "<<idHelperSvc->toString(testMe->identify())
426 <<" "<<Amg::toString(testMe->localPosition())<<", pull: "<<pull<<".");
427 if (testMe->measuresLoc0() && pull < bestPullLoc0) {
428 bestPullLoc0 = pull;
429 bestHitLoc0 = testMe;
430 }
431 if (testMe->measuresLoc1() && pull < bestPullLoc1) {
432 bestPullLoc1 = pull;
433 bestHitLoc1 = testMe;
434 }
435 }
436 if (bestHitLoc0) {
437 candidateSeed.measurements.push_back(m_cfg.calibrator->calibrate(ctx, bestHitLoc0, m_line.position(),
438 m_line.direction(), t0));
439 }
440 if (bestHitLoc1 && bestHitLoc1 != bestHitLoc0) {
441 candidateSeed.measurements.push_back(m_cfg.calibrator->calibrate(ctx, bestHitLoc1, m_line.position(),
442 m_line.direction(), t0));
443 }
444
445 }
446 return candidateSeed;
447 }
448}
const boost::regex re(r_e)
Scalar theta() const
theta method
#define ATH_MSG_VERBOSE(x)
std::vector< size_t > vec
int upper(int c)
static Double_t a
static Double_t t0
bool msgLvl(const MSG::Level lvl) const
Test the output level.
AthMessaging(IMessageSvc *msgSvc, const std::string &name)
Constructor.
const Muon::IMuonIdHelperSvc * idHelperSvc() const
Returns the IdHelpeSvc.
std::size_t m_signComboIndex
Index of the left-right ambiguity between the circles.
Line_t m_line
Line to instantiate the seed parameters.
bool isValidLine(const TangentLine &solution) const
Checks whether the intercept and the angle are witihn the allowed ranges.
LineSeeder_t::TwoCircleTangentPars TangentLine
unsigned int numGenerated() const
Returns how many seeds have been generated.
std::size_t m_upperLayer
Considered layer to pick the top drift circle from.
std::size_t m_lowerHitIndex
Explicit hit to pick in the selected bottom layer.
std::size_t m_lowerLayer
Considered layer to pick the bottom drift circle from.
std::vector< SeedSolution > m_seenSolutions
Vector caching equivalent solutions to avoid double seeding.
static constexpr std::array< TangentAmbi, 4 > s_signCombos
std::optional< DriftCircleSeed > buildSeed(const EventContext &ctx, const HoughHitType &topHit, const HoughHitType &bottomHit, const TangentAmbi ambi)
Tries to build the seed from the two hits.
const Config & config() const
Returns the current seed configuration.
MdtSegmentSeedGenerator(const std::string &name, const SegmentSeed *segmentSeed, const Config &configuration)
Standard constructor taking the segmentSeed to start with and then few configuration tunes.
Line_t::ParamVector constructLinePars(const double theta, const double y0) const
Construct the 3D-Line parameters from the estimates theta & y0 from the tangent line.
void moveToNextCandidate()
Prepares the generator to generate the seed from the next pair of drift circles.
unsigned int m_nGenSeeds
Counter on how many seeds have been generated.
std::size_t m_upperHitIndex
Explicit hit to pick in the selected top layer.
std::optional< DriftCircleSeed > nextSeed(const EventContext &ctx)
returns the next seed in the row
Representation of a segment seed (a fully processed hough maximum) produced by the hough transform.
Definition SegmentSeed.h:14
std::vector< const SpacePoint * > HitVec
The muon space point is the combination of two uncalibrated measurements one of them measures the eta...
const Identifier & identify() const
: Identifier of the primary measurement
const xAOD::UncalibratedMeasurement * primaryMeasurement() const
Interface for Helper service that creates muon Identifiers and can be used to print Identifiers.
virtual std::string toStringChamber(const Identifier &id) const =0
print all fields up to chamber to string
virtual std::string toString(const Identifier &id) const =0
print all fields to string
virtual xAOD::UncalibMeasType type() const =0
Returns the type of the measurement type as a simple enumeration.
std::string toString(const Translation3D &translation, int precision=4)
GeoPrimitvesToStringConverter.
Eigen::Matrix< double, 3, 1 > Vector3D
SpacePointPerLayerSplitter::HitLayVec HitLayerVec
Muon::MdtDriftCircleStatus dcStatus(const SpacePoint &dc)
bool moveToNextHit(const HitVec &hits, std::size_t &hitIdx)
Move to the next space point with valid drift radius.
SpacePointPerLayerSplitter::HitVec HitVec
bool isGoodDC(const SpacePoint &dc)
Returns whether the Mdt measurement has a valid space point.
std::string toString(const Parameters &pars)
Dumps the parameters into a string with labels in front of each number.
bool firstGoodHit(const HitVec &hits, std::size_t &hitIdx)
Find the first good hit in a layer.
const SpacePoint * HoughHitType
MdtDriftCircleStatus
Enum to represent the 'status' of Mdt measurements e.g.
MdtDriftCircle_v1 MdtDriftCircle
UncalibratedMeasurement_v1 UncalibratedMeasurement
Define the version of the uncalibrated measurement class.
Helper to simultaneously calculate sin and cos of the same angle.
std::vector< std::unique_ptr< CalibratedSpacePoint > > measurements
List of calibrated measurements.
const SpacePointBucket * parentBucket
Pointer to the parent bucket.
std::vector< int > solutionSigns
Vector of radial signs of the valid hits.
#define THROW_EXCEPTION(MESSAGE)
Definition throwExcept.h:10