ATLAS Offline Software
Loading...
Searching...
No Matches
MuonR4::SegmentFit::SegmentLineFitter Class Reference

The SegmentLineFitter is a standalone module to fit a straight line to calibrated muon space points. More...

#include <SegmentLineFitter.h>

Inheritance diagram for MuonR4::SegmentFit::SegmentLineFitter:
Collaboration diagram for MuonR4::SegmentFit::SegmentLineFitter:

Classes

struct  ConfigSwitches
 Configuration object of the ATLAS implementation. More...
struct  Config
 Full configuration object. More...

Public Types

using Fitter_t = Acts::Experimental::CompositeSpacePointLineFitter
 Abrivation of the actual line fitter.
using LinePar_t = Fitter_t::ParamVec_t
 Abrivation of the fitted line parameters.
using Hit_t = Segment::MeasType
 Abrivation of the space point type to use.
using HitVec_t = std::vector<Hit_t>
 Collection of space points.
using FitPars_t = Fitter_t::FitParameters
 Abrivation of the fit parameters.
using Result_t = Fitter_t::FitResult<HitVec_t>
 Abrivation of the fit result.
using FitOpts_t = Fitter_t::FitOptions<HitVec_t, ISpacePointCalibrator>
 Abrivation of the fit options.
using Selector_t = Fitter_t::Selector_t<CalibratedSpacePoint>
 Abrivation of the hit selector to choose valid hits.
using HitState = CalibratedSpacePoint::State
 Abrivation of the fit state flag.

Public Member Functions

 SegmentLineFitter (const std::string &name, Config &&config)
 Standard constructor.
std::unique_ptr< SegmentfitSegment (const EventContext &ctx, const SegmentSeed *parent, const LinePar_t &startPars, const Amg::Transform3D &localToGlobal, HitVec_t &&calibHits) const
 Fit a set of measurements to a straight segment line.
bool msgLvl (const MSG::Level lvl) const
 Test the output level.
MsgStream & msg () const
 The standard message stream.
MsgStream & msg (const MSG::Level lvl) const
 The standard message stream.
void setLevel (MSG::Level lvl)
 Change the current logging level.

Private Member Functions

Result_t callLineFit (const Acts::CalibrationContext &cctx, const Parameters &startPars, const Amg::Transform3D &localToGlobal, HitVec_t &&calibHits) const
 Calls the underlying line fitter to determine the segment parameters.
bool removeOutliers (const Acts::CalibrationContext &cctx, const SegmentSeed &seed, const Amg::Transform3D &localToGlobal, const LinePar_t &startPars, Result_t &fitResult) const
 Cleans the fitted segment from the most outlier hit and then attempts to refit the segment.
bool plugHoles (const Acts::CalibrationContext &cctx, const SegmentSeed &seed, const Amg::Transform3D &localToGlobal, Result_t &toRecover) const
 Recovery of missed hits.
void eraseWrongHits (Result_t &candidate) const
 Removes all hits from the segment which are obvious outliers.
void cleanStripLayers (HitVec_t &hits) const
 Marks duplicate hits on a strip layer as outliers to avoid competing contributions from the same layers in the fit.
bool betterResult (const Result_t &newResult, const Result_t &oldResult) const
 Returns whether the new fit result is better than the one from the previous iteration.
std::unique_ptr< SegmentconvertToSegment (const Amg::Transform3D &locToGlobTrf, const SegmentSeed *parentSeed, Result_t &&toConvert) const
 Converts the fit result into a segment object.
bool checkPrecHitCount (const HitVec_t &candidateHits) const
 Checks if the candidate has enough precision hits to fit a segment.
void initMessaging () const
 Initialize our message level and MessageSvc.

Private Attributes

Fitter_t m_fitter
 Actual implementation of the straight line fit.
ConfigSwitches m_cfg {}
 Configuration switches of the ATLAS fitter implementation.
Selector_t m_goodHitSel {}
 Selector to identify the valid hits.
std::string m_nm
 Message source name.
boost::thread_specific_ptr< MsgStream > m_msg_tls
 MsgStream instance (a std::cout like with print-out levels).
std::atomic< IMessageSvc * > m_imsg { nullptr }
 MessageSvc pointer.
std::atomic< MSG::Level > m_lvl { MSG::NIL }
 Current logging level.
std::atomic_flag m_initialized ATLAS_THREAD_SAFE = ATOMIC_FLAG_INIT
 Messaging initialized (initMessaging).

Detailed Description

The SegmentLineFitter is a standalone module to fit a straight line to calibrated muon space points.

The CompositeSpacePointLineFitter from the ACTS toolkit is used to perform the actual fit to the measurements. The SegmentLineFitter is a wrapper class taking care of relaunching fits of poor quality but with cleaned measurements and also to put back meaurements on the line that have been missed by the initial line fit.

Definition at line 28 of file SegmentLineFitter.h.

Member Typedef Documentation

◆ FitOpts_t

Abrivation of the fit options.

Definition at line 43 of file SegmentLineFitter.h.

◆ FitPars_t

using MuonR4::SegmentFit::SegmentLineFitter::FitPars_t = Fitter_t::FitParameters

Abrivation of the fit parameters.

Definition at line 39 of file SegmentLineFitter.h.

◆ Fitter_t

using MuonR4::SegmentFit::SegmentLineFitter::Fitter_t = Acts::Experimental::CompositeSpacePointLineFitter

Abrivation of the actual line fitter.

Definition at line 31 of file SegmentLineFitter.h.

◆ Hit_t

Abrivation of the space point type to use.

Definition at line 35 of file SegmentLineFitter.h.

◆ HitState

Abrivation of the fit state flag.

Definition at line 47 of file SegmentLineFitter.h.

◆ HitVec_t

Collection of space points.

Definition at line 37 of file SegmentLineFitter.h.

◆ LinePar_t

Abrivation of the fitted line parameters.

Definition at line 33 of file SegmentLineFitter.h.

◆ Result_t

Abrivation of the fit result.

Definition at line 41 of file SegmentLineFitter.h.

◆ Selector_t

Abrivation of the hit selector to choose valid hits.

Definition at line 45 of file SegmentLineFitter.h.

Constructor & Destructor Documentation

◆ SegmentLineFitter()

MuonR4::SegmentFit::SegmentLineFitter::SegmentLineFitter ( const std::string & name,
Config && config )

Standard constructor.

Parameters
nameName to be printed in the messaging
configFit configuration parameters

Definition at line 79 of file SegmentLineFitter.cxx.

79 :
82 m_cfg{config} {
83 m_goodHitSel.connect<isGoodHit>();
84 }
std::unique_ptr< const Acts::Logger > makeActsAthenaLogger(IMessageSvc *svc, const std::string &name, int level, std::optional< std::string > parent_name)
AthMessaging(IMessageSvc *msgSvc, const std::string &name)
Constructor.
Selector_t m_goodHitSel
Selector to identify the valid hits.
ConfigSwitches m_cfg
Configuration switches of the ATLAS fitter implementation.
Fitter_t m_fitter
Actual implementation of the straight line fit.
bool isGoodHit(const CalibratedSpacePoint &hit)
Returns whether the calibrated spacepoint is valid and therefore suitable to be used in the segment f...

Member Function Documentation

◆ betterResult()

bool MuonR4::SegmentFit::SegmentLineFitter::betterResult ( const Result_t & newResult,
const Result_t & oldResult ) const
inlineprivate

Returns whether the new fit result is better than the one from the previous iteration.

Selection criterion is the chi2 estimation for the same number of degrees of freedom and then the one which has more degree of freedom but still remains under the good segment threshold

Parameters
newResultThe first fit result
oldResultThe second fit result

Definition at line 417 of file SegmentLineFitter.cxx.

417 {
418 if (!newResult.converged) {
419 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" The new result did not converge");
420 return false;
421 }
422 const double redChi2New = calcRedChi2(newResult);
423 const double redChi2Old = calcRedChi2(oldResult);
424 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" Compare results -- old chi2: "<<redChi2Old<<", nDoF: "
425 <<oldResult.nDoF<<" vs. new chi2: "<<redChi2New<<", nDoF: "<<newResult.nDoF
426 <<" -- outlier removal: "<<m_cfg.outlierRemovalCut);
427 if (newResult.nDoF == oldResult.nDoF) {
428 return redChi2New < redChi2Old;
429 }
430 return (redChi2New < m_cfg.outlierRemovalCut && newResult.nDoF > oldResult.nDoF) ||
431 (redChi2New > m_cfg.outlierRemovalCut && redChi2New < redChi2Old);
432 }
#define ATH_MSG_VERBOSE(x)

◆ callLineFit()

Result_t MuonR4::SegmentFit::SegmentLineFitter::callLineFit ( const Acts::CalibrationContext & cctx,
const Parameters & startPars,
const Amg::Transform3D & localToGlobal,
HitVec_t && calibHits ) const
private

Calls the underlying line fitter to determine the segment parameters.

Parameters
cctxCalibration context to fetch later the measurement's calib constants from StoreGate (It's a packed EventContext*)
startParsInitial line parameters guess
localToGlobalTransform to align the segment's station inside ATLAS (Mainly neede if the time is fit)
calibHitsList of hits that will be fitted

Check whether a beamspot constraint should be appended

placeholder for a very generous beam spot: 300mm in X,Y (tracking volume), 20000 along Z

Recall that the time is not the same in Acts & Athena

Fit the measurements

Convert back to athena time units

Cache the chi2 terms of the measurements w.r.t. the segment

Definition at line 85 of file SegmentLineFitter.cxx.

88 {
89
91 bool appendsBS = m_cfg.doBeamSpot && countPhiHits(calibHits) > 0;
92
93
95 //check the degrees of freedom before try the fit
96 if (const std::size_t nPars = m_fitter.config().parsToUse.size(); nPars > 0ul) {
97 auto dOF = m_fitter.countDoF(calibHits, m_goodHitSel);
98 if (dOF.bending + dOF.nonBending < nPars) {
99 return result;
100 }
101 // check that there are at least two crossing stereo measurements
102 if (dOF.nonBending == 0ul && nPars == 4ul){
103 bool foundU{false}, foundV{false};
104 for (const HitVec_t::value_type& hit : calibHits) {
105 if (hit->type() != xAOD::UncalibMeasType::MMClusterType || !isGoodHit(*hit)) {
106 continue;
107 }
108 const auto* mmClust = dynamic_cast<const xAOD::MMCluster*>(hit->spacePoint()->primaryMeasurement());
109 assert(mmClust != nullptr);
110 const auto& design = mmClust->readoutElement()->stripLayer(mmClust->layerHash()).design();
111 if (!design.hasStereoAngle()) {
112 continue;
113 }
114 if (design.stereoAngle() > 0.) {
115 foundU = true;
116 } else {
117 foundV = true;
118 }
119 if (foundU && foundV) {
120 break;
121 }
122 }
123 if (!foundU || !foundV) {
124 result.measurements = std::move(calibHits);
125 result.parameters = startPars;
126 return result;
127 }
128 if (m_cfg.doBeamSpot) {
129 appendsBS = true;
130 }
131 }
132 }
133 if (appendsBS) {
134 const Amg::Transform3D globToLoc{localToGlobal.inverse()};
135 Amg::Vector3D beamSpot{globToLoc.translation()};
136 Amg::Vector3D beamLine{globToLoc.linear().col(2)};
137 SpacePoint::Cov_t covariance{};
138 covariance[toUnderlying(AxisDefs::etaCov)] = square(m_cfg.beamSpotRadius);
139 covariance[toUnderlying(AxisDefs::phiCov)] = square(m_cfg.beamSpotLength);
141 auto beamSpotSP = std::make_unique<CalibratedSpacePoint>(nullptr, std::move(beamSpot));
142 beamSpotSP->setBeamDirection(std::move(beamLine));
143 beamSpotSP->setCovariance(std::move(covariance));
144 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__<<": Beam spot constraint "
145 <<Amg::toString(beamSpotSP->localPosition())<<", "<<beamSpotSP->covariance());
146 calibHits.emplace_back(std::move(beamSpotSP));
147 }
148 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__ <<": Start segment fit with parameters "
149 <<toString(startPars) <<", plane location: "<<Amg::toString(localToGlobal)
150 <<std::endl<<print(calibHits));
151
152 FitOpts_t fitOpts{};
153 fitOpts.calibContext = cctx;
154 fitOpts.calibrator = m_cfg.calibrator;
155 fitOpts.selector = m_goodHitSel;
156
157 fitOpts.measurements = std::move(calibHits);
158 fitOpts.localToGlobal = localToGlobal;
159 fitOpts.startParameters = startPars;
161 constexpr auto t0idx = toUnderlying(ParamDefs::t0);
162 fitOpts.startParameters[t0idx] = ActsTrk::timeToActs(fitOpts.startParameters[t0idx]);
164 result = m_fitter.fit(std::move(fitOpts));
166 if (m_fitter.config().fitT0) {
167 result.parameters[t0idx] = ActsTrk::timeToAthena(result.parameters[t0idx]);
168 result.covariance(t0idx, t0idx) = Acts::square(ActsTrk::timeToAthena(1.)) * result.covariance(t0idx, t0idx);
169 for (ParamDefs p : {ParamDefs::x0, ParamDefs::y0, ParamDefs::phi, ParamDefs::theta}) {
170 auto pidx = toUnderlying(p);
171 result.covariance(t0idx, pidx) = ActsTrk::timeToAthena(result.covariance(t0idx, pidx));
172 result.covariance(pidx, t0idx) = ActsTrk::timeToAthena(result.covariance(pidx, t0idx));
173 }
174 }
176 {
177 const auto[segPos, segDir] = makeLine(result.parameters);
178 for (Hit_t& hit : result.measurements) {
179 hit->setChi2Term(SeedingAux::chi2Term(segPos, segDir, *hit));
180 }
181 }
182 return result;
183 }
Segment::MeasType Hit_t
Abrivation of the space point type to use.
Fitter_t::FitResult< HitVec_t > Result_t
Abrivation of the fit result.
Fitter_t::FitOptions< HitVec_t, ISpacePointCalibrator > FitOpts_t
Abrivation of the fit options.
std::array< double, 3 > Cov_t
Abrivation of the covariance type.
constexpr double timeToAthena(T actsT)
Converts a time unit from Acts to Athena units.
constexpr auto timeToActs(T athenaT)
Converts a time unit from Athena to Acts units.
std::string toString(const Translation3D &translation, int precision=4)
GeoPrimitvesToStringConverter.
Eigen::Affine3d Transform3D
Eigen::Matrix< double, 3, 1 > Vector3D
SeedingAux::FitParIndex ParamDefs
Use the same parameter indices as used by the CompSpacePointAuxiliaries.
std::pair< Amg::Vector3D, Amg::Vector3D > makeLine(const Parameters &pars)
Returns the parsed parameters into an Eigen line parametrization.
std::string toString(const Parameters &pars)
Dumps the parameters into a string with labels in front of each number.
std::string print(const cont_t &container)
Print a space point container to string.
AthConfigFlags beamSpot(AthConfigFlags flags, str instanceName, str recoMode)
MMCluster_v1 MMCluster

◆ checkPrecHitCount()

bool MuonR4::SegmentFit::SegmentLineFitter::checkPrecHitCount ( const HitVec_t & candidateHits) const
inlineprivate

Checks if the candidate has enough precision hits to fit a segment.

In case of NSW, we check also the orientation of the strips to ensure they provide indipendent constraints on the segment.

Parameters
candidateHitsList of hits on the candidate segment
seedSegment seed from which the segment was built. Needed for station index

Check whether there is at least one of each micromega strip type. To have a sane topology we need to have at least 2 strips from one kind.

Definition at line 590 of file SegmentLineFitter.cxx.

590 {
591 using namespace Muon::MuonStationIndex;
592
593 const size_t nPrecHits = countPrecHits(candidateHits);
594 if (nPrecHits < m_cfg.nPrecHitCut) {
595 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__<<": Not enough precision hits for segment fit. "
596 <<nPrecHits<<" < "<<m_cfg.nPrecHitCut);
597 return false;
598 }
599
600 const auto firstHit {std::ranges::find_if(candidateHits, [](const Hit_t& hit){
601 return hit->spacePoint() != nullptr;
602 })};
603 assert(firstHit != candidateHits.end());
604 if (toStationIndex((*firstHit)->spacePoint()->msSector()->chamberIndex()) == StIndex::EI &&
605 std::ranges::any_of(candidateHits, [](const Hit_t& hit){
606 return xAOD::isNSW(hit->type()); })) {
607
608 std::array<std::size_t, 3> nStrips{Acts::filledArray<std::size_t, 3>(0u)};
609 std::size_t nPhiHits {0u};
610 for (const Hit_t& hit : candidateHits) {
611 if (!isGoodHit(*hit)) {
612 continue;
613 }
614
615 if (hit->type() == xAOD::UncalibMeasType::sTgcStripType) {
616 nStrips[0] += isPrecisionHit(*hit);
617 nPhiHits += hit->measuresPhi();
618 continue;
619 } else if (hit->type() == xAOD::UncalibMeasType::MMClusterType) {
620 const auto* mmClust = dynamic_cast<const xAOD::MMCluster*>(hit->spacePoint()->primaryMeasurement());
621 assert(mmClust);
622 const auto& design = mmClust->readoutElement()->stripLayer(mmClust->measurementHash()).design();
623 if (!design.hasStereoAngle()) {
624 ++nStrips[0];
625 } else if (design.stereoAngle() > 0.) {
626 ++nStrips[1];
627 } else {
628 ++nStrips[2];
629 }
630 }
631 }
634
635 std::size_t nEtaOrientations =
636 std::ranges::count_if(nStrips, [](std::size_t n){ return n > 0; });
637 if (nEtaOrientations == 3u) {
638 nEtaOrientations += std::ranges::any_of( nStrips, [](std::size_t n){ return n > 1; });
639 }
640 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__<<": nHits: "<<candidateHits.size()
641 <<", nPhiHits: "<<nPhiHits<<", nEtaOrientations: "<<nEtaOrientations
642 <<", N X-strips: "<<nStrips[0]<<", U-strips: "<<nStrips[1]<<", V-strips: "<<nStrips[2]);
643
644 if ( nEtaOrientations == 4u ||
645 (nEtaOrientations == 3u && nPhiHits >= 1u) ||
646 (nEtaOrientations == 2u && nPhiHits >= 2u)||
647 (std::ranges::any_of(nStrips, [](std::size_t n){ return n >= 2u; }) && nPhiHits >= 2u)) {
648 return true;
649 }
650 return false;
651 }
652 return true;
653 }
int nStrips(const MuonGM::TgcReadoutElement &readoutEle, int layer)
bool isPrecisionHit(const SpacePoint &hit)
Returns whether the uncalibrated spacepoint is a precision hit (Mdt, micromegas, stgc strips).
StIndex toStationIndex(ChIndex index)
convert ChIndex into StIndex
@ u
Enums for curvilinear frames.
Definition ParamDefs.h:77

◆ cleanStripLayers()

void MuonR4::SegmentFit::SegmentLineFitter::cleanStripLayers ( HitVec_t & hits) const
inlineprivate

Marks duplicate hits on a strip layer as outliers to avoid competing contributions from the same layers in the fit.

Hits on the same layer are sorted by their chi2 and the worse ones are rejected if they don't provide additional information

Parameters
hitsList of hit measurements to clean

We need to sort out strip hits on the same layer

Loop over the hits to mark the less compatible hits on the layer as outlier

Both hits measure eta. They've been sorted by lower chi2 -> reject b

Definition at line 349 of file SegmentLineFitter.cxx.

349 {
350 const SpacePointPerLayerSorter sorter{};
352 std::ranges::sort(hits, [&](const Hit_t&a ,const Hit_t& b){
353 if (a->isStraw() || b->isStraw()) {
354 return !a->isStraw();
355 }
356 if (a->type() == xAOD::UncalibMeasType::Other ||
357 b->type() == xAOD::UncalibMeasType::Other) {
358 return a->type() != xAOD::UncalibMeasType::Other;
359 }
360 const unsigned lay_a = sorter.sectorLayerNum(*a->spacePoint());
361 const unsigned lay_b = sorter.sectorLayerNum(*b->spacePoint());
362 if (lay_a != lay_b) {
363 return lay_a < lay_b;
364 }
365 const double chi2a = a->chi2Term();
366 const double chi2b = b->chi2Term();
367 /* Do not accept pad hits even though they've smaller chi2
368 * than the neighbouring strip */
370 const auto* sTgcA = static_cast<const xAOD::sTgcMeasurement*>(a->spacePoint()->primaryMeasurement());
371 const auto* sTgcB = static_cast<const xAOD::sTgcMeasurement*>(b->spacePoint()->primaryMeasurement());
372 if (sTgcA->channelType() == xAOD::sTgcMeasurement::sTgcChannelTypes::Pad &&
373 sTgcB->channelType() == xAOD::sTgcMeasurement::sTgcChannelTypes::Strip) {
374 return chi2b > m_cfg.recoveryPull;
375 } else if (sTgcB->channelType() == xAOD::sTgcMeasurement::sTgcChannelTypes::Pad &&
376 sTgcA->channelType() == xAOD::sTgcMeasurement::sTgcChannelTypes::Strip) {
377 return chi2a < m_cfg.recoveryPull;
378 }
379 }
380 return chi2a < chi2b;
381 });
382
383 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__ <<": Check for duplicate strip hits");
385 for (HitVec_t::iterator itr = hits.begin(); itr != hits.end(); ++itr) {
386 const Hit_t& hit_a{*itr};
387 if (hit_a->isStraw()){
388 break;
389 }
390 if(hit_a->fitState() == HitState::Duplicate ||
391 hit_a->type() == xAOD::UncalibMeasType::Other) {
392 continue;
393 }
394 const unsigned lay_a = sorter.sectorLayerNum(*hit_a->spacePoint());
396 for (HitVec_t::iterator itr2 = itr + 1; itr2 != hits.end(); ++itr2) {
397 const Hit_t& hit_b{*itr2};
398 if (hit_b->type() == xAOD::UncalibMeasType::Other ||
399 hit_b->fitState() == HitState::Duplicate) {
400 continue;
401 }
402 if (lay_a != sorter.sectorLayerNum(*hit_b->spacePoint())) {
403 break;
404 }
406 if ((hit_a->measuresEta() && hit_b->measuresEta()) ||
407 (hit_a->measuresPhi() && hit_b->measuresPhi())) {
408 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__ <<": Duplicate hit on same layer"<<std::endl
409 <<" -- reject: "<<(*hit_b)<<std::endl
410 <<" -- accept: "<<(*hit_a));
411 hit_b->setFitState(HitState::Duplicate);
412 }
413 }
414 }
415 }
static Double_t a
sTgcMeasurement_v1 sTgcMeasurement

◆ convertToSegment()

std::unique_ptr< Segment > MuonR4::SegmentFit::SegmentLineFitter::convertToSegment ( const Amg::Transform3D & locToGlobTrf,
const SegmentSeed * parentSeed,
Result_t && toConvert ) const
private

Converts the fit result into a segment object.

Parameters
locToGlobTrfLocal to global transform to translate the segment parameters into global parameters
parentSeedSegment seed from which the segment was built
toConvertFitted segment that needs conversion

Definition at line 223 of file SegmentLineFitter.cxx.

225 {
226 const auto [locPos, locDir] = makeLine(data.parameters);
227 Amg::Vector3D globPos = locToGlob * locPos;
228 Amg::Vector3D globDir = locToGlob.linear()* locDir;
229
230 std::ranges::sort(data.measurements, [](const Hit_t& a, const Hit_t& b){
231 return a->localPosition().z() < b->localPosition().z();
232 });
233 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__ <<": Create new segment "
234 <<toString(data.parameters)<<" in "<<patternSeed->msSector()->identString()
235 <<"built from:\n"<<print(data.measurements));
236
237 auto finalSeg = std::make_unique<Segment>(std::move(globPos), std::move(globDir),
238 patternSeed, std::move(data.measurements),
239 data.chi2, data.nDoF);
240 finalSeg->setCallsToConverge(data.nIter);
241 finalSeg->setParUncertainties(std::move(data.covariance));
242 if (m_fitter.config().fitT0) {
243 finalSeg->setSegmentT0(data.parameters[toUnderlying(ParamDefs::t0)]);
244 }
245 return finalSeg;
246 }

◆ eraseWrongHits()

void MuonR4::SegmentFit::SegmentLineFitter::eraseWrongHits ( Result_t & candidate) const
private

Removes all hits from the segment which are obvious outliers.

E.g. tubes which cannot be crossed by the segment.

Parameters
candidateReference of the segment candidate to prune.

The segment has never crossed the tube

Definition at line 327 of file SegmentLineFitter.cxx.

327 {
328 auto [segPos, segDir] = makeLine(candidate.parameters);
329 cleanStripLayers(candidate.measurements);
330 candidate.measurements.erase(std::remove_if(candidate.measurements.begin(),
331 candidate.measurements.end(),
332 [&](const HitVec_t::value_type& hit){
333 if (hit->fitState() == HitState::Valid) {
334 return false;
335 } else if (hit->fitState() == HitState::Duplicate) {
336 return true;
337 }
340 const double dist = Amg::lineDistance(segPos, segDir,
341 hit->localPosition(),
342 hit->sensorDirection());
343 const auto* dc = static_cast<const xAOD::MdtDriftCircle*>(hit->spacePoint()->primaryMeasurement());
344 return dist >= dc->readoutElement()->innerTubeRadius();
345 }
346 return false;
347 }), candidate.measurements.end());
348 }
if(pathvar)
void cleanStripLayers(HitVec_t &hits) const
Marks duplicate hits on a strip layer as outliers to avoid competing contributions from the same laye...
DataModel_detail::iterator< DVL > remove_if(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end, Predicate pred)
Specialization of remove_if for DataVector/List.

◆ fitSegment()

std::unique_ptr< Segment > MuonR4::SegmentFit::SegmentLineFitter::fitSegment ( const EventContext & ctx,
const SegmentSeed * parent,
const LinePar_t & startPars,
const Amg::Transform3D & localToGlobal,
HitVec_t && calibHits ) const

Fit a set of measurements to a straight segment line.

Badish initial fits are cleaned and then holes are put filled back Returns a nullptr if the fit failed

Parameters
ctxEventContext to access the calibration constants
parentPointer to the seed from which the hits to fit are taken. The seed gives also access to the parent bucket to recover lost hits
startParsList of parameters serving as an initial guess
localToGlobalTransform to align the segment's station inside ATLAS (Mainly neede if the time is fit)
calibHitsList of hits that will be fitted

Definition at line 185 of file SegmentLineFitter.cxx.

189 {
190
191 const Acts::CalibrationContext cctx = ActsTrk::getCalibrationContext(ctx);
192 if (!checkPrecHitCount(calibHits) ) {
193 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__ <<": Not enough degree of freedom available. What shall be fitted?!");
194 return nullptr;
195 }
196 if (m_cfg.visionTool) {
197 Result_t preFit{};
198 preFit.parameters = startPars;
199 preFit.measurements = calibHits;
200 auto seedCopy = convertToSegment(localToGlobal, parent, std::move(preFit));
201 m_cfg.visionTool->visualizeSegment(ctx, *seedCopy, "Prefit");
202 }
203 Result_t segFit = callLineFit(cctx, startPars, localToGlobal, std::move(calibHits));
204 if (m_cfg.visionTool && segFit.converged) {
205 auto seedCopy = convertToSegment(localToGlobal, parent, Result_t{segFit});
206 m_cfg.visionTool->visualizeSegment(ctx, *seedCopy, "Intermediate fit");
207 }
208 if (!removeOutliers(cctx, *parent, localToGlobal,
209 segFit.converged? segFit.parameters : startPars,
210 segFit)) {
211 return nullptr;
212 }
213 if (!plugHoles(cctx, *parent, localToGlobal, segFit)) {
214 return nullptr;
215 }
216 auto finalSeg = convertToSegment(localToGlobal, parent, std::move(segFit));
217 if (m_cfg.visionTool) {
218 m_cfg.visionTool->visualizeSegment(ctx, *finalSeg, "Final fit");
219 }
220 return finalSeg;
221 }
bool plugHoles(const Acts::CalibrationContext &cctx, const SegmentSeed &seed, const Amg::Transform3D &localToGlobal, Result_t &toRecover) const
Recovery of missed hits.
Result_t callLineFit(const Acts::CalibrationContext &cctx, const Parameters &startPars, const Amg::Transform3D &localToGlobal, HitVec_t &&calibHits) const
Calls the underlying line fitter to determine the segment parameters.
std::unique_ptr< Segment > convertToSegment(const Amg::Transform3D &locToGlobTrf, const SegmentSeed *parentSeed, Result_t &&toConvert) const
Converts the fit result into a segment object.
bool checkPrecHitCount(const HitVec_t &candidateHits) const
Checks if the candidate has enough precision hits to fit a segment.
bool removeOutliers(const Acts::CalibrationContext &cctx, const SegmentSeed &seed, const Amg::Transform3D &localToGlobal, const LinePar_t &startPars, Result_t &fitResult) const
Cleans the fitted segment from the most outlier hit and then attempts to refit the segment.
Acts::CalibrationContext getCalibrationContext(const EventContext &ctx)
The Acts::Calibration context is piped through the Acts fitters to (re)calibrate the Acts::SourceLink...

◆ initMessaging()

void AthMessaging::initMessaging ( ) const
privateinherited

Initialize our message level and MessageSvc.

This method should only be called once.

Definition at line 39 of file AthMessaging.cxx.

40{
42 // If user did not set an explicit level, set a default
43 if (m_lvl == MSG::NIL) {
44 m_lvl = m_imsg ?
45 static_cast<MSG::Level>( m_imsg.load()->outputLevel(m_nm) ) :
46 MSG::INFO;
47 }
48}
std::string m_nm
Message source name.
std::atomic< IMessageSvc * > m_imsg
MessageSvc pointer.
std::atomic< MSG::Level > m_lvl
Current logging level.
IMessageSvc * getMessageSvc(bool quiet=false)

◆ msg() [1/2]

MsgStream & AthMessaging::msg ( ) const
inlineinherited

The standard message stream.

Returns a reference to the default message stream May not be invoked before sysInitialize() has been invoked.

Definition at line 167 of file AthMessaging.h.

168{
169 MsgStream* ms = m_msg_tls.get();
170 if (!ms) {
171 if (!m_initialized.test_and_set()) initMessaging();
172 ms = new MsgStream(m_imsg,m_nm);
173 m_msg_tls.reset( ms );
174 }
175
176 ms->setLevel (m_lvl);
177 return *ms;
178}
boost::thread_specific_ptr< MsgStream > m_msg_tls
MsgStream instance (a std::cout like with print-out levels).
void initMessaging() const
Initialize our message level and MessageSvc.

◆ msg() [2/2]

MsgStream & AthMessaging::msg ( const MSG::Level lvl) const
inlineinherited

The standard message stream.

Returns a reference to the default message stream May not be invoked before sysInitialize() has been invoked.

Definition at line 182 of file AthMessaging.h.

183{ return msg() << lvl; }
MsgStream & msg() const
The standard message stream.

◆ msgLvl()

bool AthMessaging::msgLvl ( const MSG::Level lvl) const
inlineinherited

Test the output level.

Parameters
lvlThe message level to test against
Returns
boolean Indicating if messages at given level will be printed
Return values
trueMessages at level "lvl" will be printed

Definition at line 151 of file AthMessaging.h.

152{
153 // If user did not set explicit message level we have to initialize
154 // the messaging and retrieve the default via the MessageSvc.
155 if (m_lvl==MSG::NIL && !m_initialized.test_and_set()) initMessaging();
156
157 if (m_lvl <= lvl) {
158 msg() << lvl;
159 return true;
160 } else {
161 return false;
162 }
163}

◆ plugHoles()

bool MuonR4::SegmentFit::SegmentLineFitter::plugHoles ( const Acts::CalibrationContext & cctx,
const SegmentSeed & seed,
const Amg::Transform3D & localToGlobal,
Result_t & toRecover ) const
private

Recovery of missed hits.

Hits in the space point bucket that are maximally <RecoveryPull> away from the fitted segment are put onto the segment candidate and the candidate is refitted. If the refitted candidate has a chi2/nDoF < <OutlierRemoval> the canidate is automatically choosen otherwise, its chi needs to be better.

Parameters
cctxCalibration context to fetch later the measurement's calib constants from StoreGate (It's a packed EventContext*)
seedParent seed from which the segment fit is actually triggered The seed is mainly used for visualization purposes
localToGlobalTransform to align the spectrometer sector within ATLAS mainly used for the t0 fit
fitResultPreviously achieved fit result to be checked. The measurements on the result and the paramters are updated accordingly

We've the first estimator of the segment fit

Setup a map to replace space points if they better suite

Loop over all hits in the parent bucket

Hit already used in the segment fit

If the hit is a phi measurement check at least if it can be hit by the segment

Use the pull of the uncalibrated measurement to estimate whether a calibration is actually worth

No extra hit has been found

Remove the beamspot constraint measurement

If the chi2 is less than 5, no outlier rejection is launched. So also accept any recovered segment below that threshold

Next check whether the recovery made measurements marked as outlier feasable to the hole recovery

Definition at line 433 of file SegmentLineFitter.cxx.

436 {
438 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__ <<": segment "<<toString(toRecover.parameters)
439 <<", chi2: "<< calcRedChi2(toRecover) <<", nDoF: "<<toRecover.nDoF);
441
442
443 std::unordered_set<const SpacePoint*> usedSpacePoints{};
444 for (auto& hit : toRecover.measurements) {
445 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__ <<": "<<(*hit)<<" is known");
446 usedSpacePoints.insert(hit->spacePoint());
447 }
449 const EventContext& ctx{*cctx.get<const EventContext*>()};
450
451 const double timeOff = toRecover.parameters[toUnderlying(ParamDefs::t0)];
452 HitVec_t candidateHits{};
453 std::size_t hasCandidate{0};
454 const auto [locPos, locDir] = makeLine(toRecover.parameters);
455
457 for (const auto& hit : *seed.parentBucket()){
459 if (usedSpacePoints.count(hit.get())){
460 continue;
461 }
462 Hit_t calibHit{};
463 double pull{-1.};
464 if (hit->isStraw()) {
465 using namespace Acts::detail::LineHelper;
466 const double dist = signedDistance(locPos, locDir, hit->localPosition(), hit->sensorDirection());
467 const auto* dc = static_cast<const xAOD::MdtDriftCircle*>(hit->primaryMeasurement());
468 // Check whether the tube is crossed by the hit
469 if (std::abs(dist) >= dc->readoutElement()->innerTubeRadius()) {
470 continue;
471 }
472 } else {
474 if (!hit->measuresEta() &&
475 std::abs(hit->sensorDirection().dot(hit->localPosition() -
476 SeedingAux::extrapolateToPlane(locPos,locDir, *hit))) >
477 1.1*std::sqrt(hit->covariance()[toUnderlying(AxisDefs::etaCov)])){
478 continue;
479 }
482 pull = std::sqrt(SeedingAux::chi2Term(locPos, locDir, *hit));
483 if (pull > 1.1 * m_cfg.recoveryPull) {
484 continue;
485 }
486 }
487 calibHit = m_cfg.calibrator->calibrate(ctx, hit.get(), locPos, locDir, ActsTrk::timeToActs(timeOff));
488 calibHit->setChi2Term(SeedingAux::chi2Term(locPos, locDir, *calibHit));
489 if (calibHit->chi2Term() <= Acts::square(m_cfg.recoveryPull)) {
490 hasCandidate += calibHit->fitState() == HitState::Valid;
491 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__<<": Candidate hit for recovery "
492 <<(*calibHit));
493 } else {
494 calibHit->setFitState(HitState::Outlier);
495 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__<<": Outlier hit "
496 <<(*calibHit)<<" -> limit: "<<m_cfg.recoveryPull);
497 }
498 candidateHits.push_back(std::move(calibHit));
499 }
501 if (!hasCandidate) {
502 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__<<": No space point candidates for recovery were found");
503 toRecover.measurements.insert(toRecover.measurements.end(),
504 std::make_move_iterator(candidateHits.begin()),
505 std::make_move_iterator(candidateHits.end()));
506 eraseWrongHits(toRecover);
507 return toRecover.nDoF > 0;
508 }
509 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__<<": Found "<<hasCandidate<<" space points for recovery. ");
510
511
512 HitVec_t hitsForRecovery = toRecover.measurements;
514 if (m_cfg.doBeamSpot) {
515 removeBeamSpot(hitsForRecovery);
516 }
517
518 hitsForRecovery.insert(hitsForRecovery.end(),
519 candidateHits.begin(),
520 candidateHits.end());
521
522 cleanStripLayers(hitsForRecovery);
523
524 Result_t recovered = callLineFit(cctx, toRecover.parameters, localToGlobal,
525 std::move(hitsForRecovery));
526
529 if (betterResult(recovered, toRecover)) {
530 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__<<": Accept segment with recovered "
531 <<(recovered.nDoF - toRecover.nDoF)<<" extra nDoF.");
532 recovered.nIter += toRecover.nIter;
533 toRecover = std::move(recovered);
534
535 std::vector<const CalibratedSpacePoint*> stripOutliers{};
536 stripOutliers.reserve(toRecover.measurements.size());
539 unsigned recovLoop{(candidateHits.size() != hasCandidate)*m_cfg.nRecoveryLoops};
540 while (++recovLoop <= m_cfg.nRecoveryLoops) {
541 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__<<": Enter recovery loop "<<recovLoop<<".");
542 hitsForRecovery = toRecover.measurements;
543 // Remove the beamspot
544 if (m_cfg.doBeamSpot) {
545 removeBeamSpot(hitsForRecovery);
546 }
547 // Check whether an outlier can be lifted to on-track
548 for (HitVec_t::value_type& hit : hitsForRecovery) {
549 if (hit->fitState() != HitState::Outlier) {
550 continue;
551 }
552 if (hit->chi2Term() < Acts::square(m_cfg.recoveryPull)) {
553 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__<<": Try to recover outlier "<<(*hit));
554 hit->setFitState(HitState::Valid);
555 stripOutliers.push_back(hit.get());
556 }
557 }
558 // Nothing to recover
559 if (stripOutliers.empty()) {
560 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__<<": No additional measurement found");
561 break;
562 }
563 // Ensure that only one hit per layer is fit
564 cleanStripLayers(hitsForRecovery);
565 // Recovery turned out to be duplicates on the same layer
566 if (std::ranges::none_of(stripOutliers,[](const CalibratedSpacePoint* sp){
567 return sp->fitState() == HitState::Valid;
568 })) {
569 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__<<": Outliers turned out to be duplicates.");
570 break;
571 }
572 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__<<": Start fit without the outliers.");
573 stripOutliers.clear();
574 recovered = callLineFit(cctx, toRecover.parameters, localToGlobal, std::move(hitsForRecovery));
575 if (!betterResult(recovered, toRecover)) {
576 break;
577 }
578 recovered.nIter += toRecover.nIter;
579 toRecover = std::move(recovered);
580 }
581 } else{
582 for (HitVec_t::value_type& hit : candidateHits) {
583 hit->setFitState(HitState::Outlier);
584 toRecover.measurements.push_back(std::move(hit));
585 }
586 }
587 eraseWrongHits(toRecover);
588 return true;
589 }
static Double_t sp
bool betterResult(const Result_t &newResult, const Result_t &oldResult) const
Returns whether the new fit result is better than the one from the previous iteration.
void eraseWrongHits(Result_t &candidate) const
Removes all hits from the segment which are obvious outliers.
std::vector< Hit_t > HitVec_t
Collection of space points.
double signedDistance(const Amg::Vector3D &posA, const Amg::Vector3D &dirA, const Amg::Vector3D &posB, const Amg::Vector3D &dirB)
Calculates the signed distance between two lines in 3D space.
MdtDriftCircle_v1 MdtDriftCircle

◆ removeOutliers()

bool MuonR4::SegmentFit::SegmentLineFitter::removeOutliers ( const Acts::CalibrationContext & cctx,
const SegmentSeed & seed,
const Amg::Transform3D & localToGlobal,
const LinePar_t & startPars,
Result_t & fitResult ) const
private

Cleans the fitted segment from the most outlier hit and then attempts to refit the segment.

The outlier removal is not run if the segment has already a chi2 / nDoF better than <outlierRemovalCut>. Returns false if the recovery lead to the destruction of all nDoF

Parameters
cctxCalibration context to fetch later the measurement's calib constants from StoreGate (It's a packed EventContext*)
seedParent seed from which the segment fit is actually triggered The seed is mainly used for visualization purposes
localToGlobalTransform to align the spectrometer sector within ATLAS mainly used for the t0 fit
startParsThe initial parameters from which the fit shall be launched In case of out of bound parameters, the start parameters are returned otherwise the last obtained fit parameters
fitResultPreviously achieved fit result to be checked. The measurements on the result and the paramters are updated accordingly

Remove a priori the beamspot constaint as it never should pose any problem and another one will be added anyway in the next iteration

Next sort the measurements by ascending chi2

Move the outliers to the front

Check again the available DOF and number of precision hits after hit removal

Refit the segment line without the measurement

Definition at line 248 of file SegmentLineFitter.cxx.

252 {
253
254 if (!checkPrecHitCount(fitResult.measurements) ||
255 fitResult.nIter > m_fitter.config().maxIter) {
256 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__
257 <<": No degree of freedom available. What shall be removed?!. nDoF: "
258 <<fitResult.nDoF<<", n-meas: "<<countPrecHits(fitResult.measurements)
259 <<std::endl<<print(fitResult.measurements));
260 return false;
261 }
262 if (fitResult.converged && calcRedChi2(fitResult) < m_cfg.outlierRemovalCut) {
263 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__ <<": The segment "<<toString(fitResult.parameters)
264 <<" is already of good quality "<< calcRedChi2(fitResult)<<". Don't remove outliers");
265 return true;
266 }
267 if (fitResult.nDoF == 0u){
268 return false;
269 }
270 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__ <<": Segment "
271 <<toString(fitResult.parameters)<<", nIter: "<<fitResult.nIter
272 <<" is of badish quality. "<<print(fitResult.measurements)
273 <<std::endl<<"Remove worst hit");
274
277 if (m_cfg.doBeamSpot) {
278 removeBeamSpot(fitResult.measurements);
279 }
280
282 std::ranges::sort(fitResult.measurements,
283 [](const HitVec_t::value_type& a, const HitVec_t::value_type& b){
285 if (isGoodHit(*a) != isGoodHit(*b)) {
286 return !isGoodHit(*a);
287 }
288 return a->chi2Term() < b->chi2Term();
289 });
290 fitResult.measurements.back()->setFitState(HitState::Outlier);
291 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__<<" Mark "<<(*fitResult.measurements.back())<<" as outlier");
292
294 if (!checkPrecHitCount(fitResult.measurements)) {
295 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__
296 <<": No degree of freedom available after outlier removal. n-meas: "
297 <<countPrecHits(fitResult.measurements)<<std::endl<<print(fitResult.measurements));
298 return false;
299 }
300
302 Result_t newAttempt = callLineFit(cctx, startPars, localToGlobal,
303 std::move(fitResult.measurements));
304 if (newAttempt.converged) {
305 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__<<" The outlier removal converged.");
306 newAttempt.nIter+=fitResult.nIter;
307 fitResult = std::move(newAttempt);
308 if (m_cfg.visionTool) {
309 const EventContext& ctx{*cctx.get<const EventContext*>()};
310 auto seedCopy = convertToSegment(localToGlobal, &seed, Result_t{fitResult});
311 m_cfg.visionTool->visualizeSegment(ctx, *seedCopy, "Bad fit recovery");
312 }
313 } else {
314 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__
315 <<" Outlier removal fit did not converge. Needed iterations: "<<newAttempt.nIter);
316 if (newAttempt.nIter == 0ul) {
317 return false;
318 }
319 fitResult.nIter+=newAttempt.nIter;
320 fitResult.measurements = std::move(newAttempt.measurements);
321 }
322 return removeOutliers(cctx, seed, localToGlobal,
323 fitResult.converged ? fitResult.parameters : startPars,
324 fitResult);
325 }

◆ setLevel()

void AthMessaging::setLevel ( MSG::Level lvl)
inherited

Change the current logging level.

Use this rather than msg().setLevel() for proper operation with MT.

Definition at line 28 of file AthMessaging.cxx.

29{
30 m_lvl = lvl;
31}

Member Data Documentation

◆ ATLAS_THREAD_SAFE

std::atomic_flag m_initialized AthMessaging::ATLAS_THREAD_SAFE = ATOMIC_FLAG_INIT
mutableprivateinherited

Messaging initialized (initMessaging).

Definition at line 141 of file AthMessaging.h.

◆ m_cfg

ConfigSwitches MuonR4::SegmentFit::SegmentLineFitter::m_cfg {}
private

Configuration switches of the ATLAS fitter implementation.

Definition at line 108 of file SegmentLineFitter.h.

108{};

◆ m_fitter

Fitter_t MuonR4::SegmentFit::SegmentLineFitter::m_fitter
private

Actual implementation of the straight line fit.

Definition at line 106 of file SegmentLineFitter.h.

◆ m_goodHitSel

Selector_t MuonR4::SegmentFit::SegmentLineFitter::m_goodHitSel {}
private

Selector to identify the valid hits.

Definition at line 110 of file SegmentLineFitter.h.

110{};

◆ m_imsg

std::atomic<IMessageSvc*> AthMessaging::m_imsg { nullptr }
mutableprivateinherited

MessageSvc pointer.

Definition at line 135 of file AthMessaging.h.

135{ nullptr };

◆ m_lvl

std::atomic<MSG::Level> AthMessaging::m_lvl { MSG::NIL }
mutableprivateinherited

Current logging level.

Definition at line 138 of file AthMessaging.h.

138{ MSG::NIL };

◆ m_msg_tls

boost::thread_specific_ptr<MsgStream> AthMessaging::m_msg_tls
mutableprivateinherited

MsgStream instance (a std::cout like with print-out levels).

Definition at line 132 of file AthMessaging.h.

◆ m_nm

std::string AthMessaging::m_nm
privateinherited

Message source name.

Definition at line 129 of file AthMessaging.h.


The documentation for this class was generated from the following files: