20 if (newSegment.
nDoF() == oldSegment.
nDoF()) {
21 return newSegment.
chi2() < oldSegment.
chi2();
23 return newSegment.
nDoF() > oldSegment.
nDoF();
27 unsigned countNSWHits(
const std::vector<const MuonR4::SpacePoint*>& hits,
49using namespace Muon::MuonStationIndex;
50using namespace MuonR4::SegmentFit;
51using namespace Acts::UnitLiterals;
53constexpr auto phiIdx {Acts::toUnderlying(ParamDefs::phi)};
54constexpr auto x0Idx {Acts::toUnderlying(ParamDefs::x0)};
55constexpr auto thetaIdx {Acts::toUnderlying(ParamDefs::theta)};
56constexpr auto y0Idx {Acts::toUnderlying(ParamDefs::y0)};
67 fitCfg.calcAlongStrip =
false;
68 fitCfg.recalibrate =
m_cfg.recalibInFit;
69 fitCfg.useFastFitter =
m_cfg.useFastFitter;
70 fitCfg.fastPreFitter =
m_cfg.fastPreFitter;
71 fitCfg.ignoreFailedPreFit =
m_cfg.ignoreFailedPreFit;
72 fitCfg.useHessian =
m_cfg.useHessianResidual;
77 fitCfg.maxIter =
m_cfg.maxIter;
78 fitCfg.parsToUse = {ParamDefs::y0, ParamDefs::theta};
82 nswFitCfg.parsToUse = {ParamDefs::x0, ParamDefs::y0, ParamDefs::theta, ParamDefs::phi};
84 m_fitter = std::make_unique<LineFitter>(name, std::move(fitCfg));
85 m_nswFitter = std::make_unique<LineFitter>(name, std::move(nswFitCfg));
88 MdtSegmentSeeder::Config genCfg{};
89 genCfg.hitPullCut =
m_cfg.seedHitChi2;
90 genCfg.busyLayerLimit = 3.;
91 genCfg.startWithPattern =
false;
103 std::vector<StIndex> stations {pattern.getStations()};
109 return layerRank(l1) < layerRank(l2);
112 std::vector<Segment_t> muonSegments{};
113 muonSegments.reserve(3u);
114 for (
const StIndex st : stations) {
117 if (muonSegments.size() > 2 || (st == stations.back() && muonSegments.size() == 0.)) {
122 if (std::ranges::any_of(muonSegments, [&layer](
const Segment_t& seg) {
124 seg->measurements().back()->spacePoint()->msSector()->chamberIndex()) == layer; })) {
125 ATH_MSG_VERBOSE(__func__<<
"() Already found a segment in layer " << layer
126 <<
" - skip station " << st);
130 const HitVec_t& hits {pattern.hitsInStation(st)};
131 const std::vector<Bucket_t>& buckets {pattern.bucketsInStation(st)};
134 std::unordered_map<const MuonGMR4::SpectrometerSector*, HitVec_t> hitsPerSector{};
135 std::ranges::for_each(hits, [&hitsPerSector](
Hit_t hit) {
136 hitsPerSector[
hit->msSector()].push_back(
hit);
140 std::vector<const MuonGMR4::SpectrometerSector*> sectorsInStation{};
141 sectorsInStation.reserve(hitsPerSector.size());
142 std::ranges::transform(hitsPerSector, std::back_inserter(sectorsInStation),
143 [](
const auto&
pair) {
return pair.first; });
144 std::ranges::sort(sectorsInStation, std::ranges::greater{},
146 return hitsPerSector[s].size(); });
148 std::vector<Segment_t> stSegments{};
149 stSegments.reserve(sectorsInStation.size());
153 ATH_MSG_VERBOSE(__func__<<
"() Start segment fitting in sector " << sector->identString()
154 <<
" station " << st <<
" with " << hitsPerSector[sector].size() <<
" hits.");
155 const HitVec_t& sectorHits {hitsPerSector[sector]};
159 std::vector<Bucket_t> bucketsInSector {};
160 std::ranges::copy_if(buckets, std::back_inserter(bucketsInSector),
163 if (bucketsInSector.empty()) {
164 throw std::runtime_error(std::format(
"No parent bucket found for sector {} in station {}", sector->identString(),
stName(st)));
166 Bucket_t parentBucket {bucketsInSector.size() < 2u ? bucketsInSector.back()
167 : *std::ranges::max_element(bucketsInSector, std::ranges::less{}, [§orHits](
const Bucket_t& b) {
168 return std::ranges::count_if(sectorHits, [&b](
const Hit_t&
hit) {
169 return std::ranges::any_of(*b, [&
hit](
const auto&
h) {
170 return h.get() ==
hit; });
174 Segment_t segment {
fitSegment(ctx, sector->localToGlobalTransform(gctx), parentBucket, std::move(hitsPerSector[sector]))};
177 ATH_MSG_VERBOSE(__func__<<
"() Successfully fitted segment in station "<< st <<
": Pos: "
179 <<
", chi2: "<< segment->chi2()<<
", nDoF: "<<segment->nDoF()<<std::endl <<
print(segment->measurements()));
180 stSegments.push_back(std::move(segment));
182 if (calcRedChi2(*stSegments.back()) <=
m_cfg.goodSegmentCut) {
187 ATH_MSG_VERBOSE(__func__<<
"() No segment could be fitted. Try next sector, if any.");
189 if (stSegments.empty()) {
190 ATH_MSG_DEBUG(
"No segment could be fitted in station " << st <<
" for pattern" << pattern);
193 Segment_t& bestStSegment {stSegments.size() > 1
194 ? *std::ranges::max_element(stSegments, [](
const Segment_t& s1,
const Segment_t& s2) {
195 return betterSegment(*s2, *s1); })
196 : stSegments.back()};
198 <<
", dir: " <<
Amg::toString(bestStSegment->direction()) <<
", chi2: " << bestStSegment->chi2() <<
", nDoF: " << bestStSegment->nDoF());
200 muonSegments.push_back(std::move(bestStSegment));
203 ATH_MSG_VERBOSE(__func__<<
"() Found "<< muonSegments.size()<<
" muon segments to construct a candidate.");
204 if (muonSegments.size() < 2) {
205 ATH_MSG_DEBUG(__func__<<
"() Not enough muon segments to construct a candidate - abort.");
209 std::ranges::sort(muonSegments, std::ranges::less{},
210 [](
const Segment_t& seg) {
return seg->position().perp(); });
213 const double qtimesP = 67;
215 const double eta {muonSegments[0]->position().eta()};
216 const double pt {std::abs(qtimesP) / std::cosh(
eta)};
218 xAOD::Muon* newMuon = outMuons->push_back(std::make_unique<xAOD::Muon>());
219 newMuon->
setAuthor(xAOD::Muon::Author::MuidSA);
220 newMuon->
setP4(pt,
eta, pattern.phi());
221 newMuon->
setCharge(qtimesP > 0. ? 1 : -1);
222 newMuon->
setMuonType(xAOD::Muon::MuonType::MuonStandAlone);
230 std::vector<Hit_t>&& hits)
const {
232 const unsigned firstLayer {
m_spSorter.sectorLayerNum(*hits.front())};
233 if (hits.size() < 2 ||
234 std::ranges::none_of(hits, [&](
Hit_t hit) {
235 return m_spSorter.sectorLayerNum(*hit) != firstLayer; })) {
236 ATH_MSG_DEBUG(__func__<<
"() Not enough layers with hits to fit a segment, skipping!");
243 initialPars[
phiIdx] = 90._degree;
244 initialPars[
x0Idx] = 0;
248 if (
const unsigned nNSWhits {countNSWHits(hits, parentBucket)}; nNSWhits > 0) {
250 if (nNSWhits < hits.size()) {
251 ATH_MSG_DEBUG(__func__<<
"() Mixed hit types: "<<nNSWhits<<
" NSW over "
252 <<hits.size()<<
" total hits in the segment seed.");
254 std::vector<Hit_t> selHits{};
255 const bool useNSW {nNSWhits > (hits.size() - nNSWhits)};
256 std::ranges::copy_if(hits, std::back_inserter(selHits), [useNSW](
Hit_t hit) {
262 const auto [locPos, locDir] {
makeLine(initialPars)};
264 <<
", hits: "<<
print(validHits));
266 auto houghSeed {std::make_unique<SegmentSeed>(0., 0., 0., 0., 0., std::move(validHits), parentBucket)};
270 return m_nswFitter->fitSegment(ctx, houghSeed.get(), initialPars, localToGlobal, std::move(calibHits));
275 auto houghSeed {std::make_unique<SegmentSeed>(0., 0., 0., 0., 0., std::move(hits), parentBucket)};
276 std::vector<Segment_t> segments{};
281 while (
auto seed =
m_mdtSeeder->nextSeed(cctx, seedState)) {
282 ATH_MSG_VERBOSE(__func__<<
"() Found a seed. Try to fit the segment...");
285 localToGlobal, std::move(seed->hits))};
287 segments.push_back(std::move(segment));
291 if (!segments.empty()) {
292 ATH_MSG_VERBOSE(__func__<<
"() In total "<<segments.size()<<
" segment were constructed. Keep the best one.");
293 if (
msgLvl(MSG::VERBOSE) && segments.size() > 1) {
297 <<
", dir: "<<
Amg::toString(seg->direction())<<
", chi2: "<<seg->chi2()
298 <<
", nDoF: "<<seg->nDoF()<<std::endl<<
print(seg->measurements()));
301 return std::move(*std::ranges::max_element(segments, [&](
const Segment_t& s1,
const Segment_t& s2) {
302 return betterSegment(*s2, *s1);
312 double S {0.}, Sy {0.}, Sz {0.}, Szz {0.}, Syz {0.};
314 for (
const auto&
hit : hits) {
316 const double z {locPos.z()};
317 const double y {locPos.y()};
320 ATH_MSG_WARNING(__func__<<
"() Hit"<< *
hit<<
" with very small eta covariance: " << sigma2 <<
". Skipping the measurement.");
323 validHits.push_back(
hit);
324 const double w {1./sigma2};
332 const double det {S * Szz - Sz * Sz};
334 if (std::abs(det) < 1e-6) {
335 ATH_MSG_VERBOSE(__func__<<
"() Degenerate weighted regression, using furthest hits...");
336 const auto [minZHit, maxZHit] = std::ranges::minmax_element(hits, std::ranges::less{},
337 [](
const Hit_t&
h) {
return h->localPosition().z(); });
338 const Amg::Vector3D& minLocPos {(*minZHit)->localPosition()};
339 const Amg::Vector3D& maxLocPos {(*maxZHit)->localPosition()};
340 const double deltaZ {maxLocPos.z() - minLocPos.z()};
342 assert(std::abs(deltaZ) > 1e-3);
344 pars[
thetaIdx] = std::atan2(maxLocPos.y() - minLocPos.y(), deltaZ);
345 pars[
y0Idx] = minLocPos.y() - std::tan(pars[
thetaIdx]) * minLocPos.z();
349 const double slope { (S * Syz - Sz * Sy) / det };
351 const double intercept { (Szz * Sy - Sz * Syz) / det };
354 pars[
y0Idx] = intercept;
Scalar eta() const
pseudorapidity method
#define ATH_MSG_DEBUG(x,...)
#define ATH_MSG_WARNING(x,...)
#define ATH_MSG_VERBOSE(x,...)
std::unique_ptr< const Acts::Logger > makeActsAthenaLogger(IMessageSvc *svc, const std::string &name, int level, std::optional< std::string > parent_name)
bool msgLvl(const MSG::Level lvl) const
Test the output level.
AthMessaging(IMessageSvc *msgSvc, const std::string &name)
Constructor.
A spectrometer sector forms the envelope of all chambers that are placed in the same MS sector & laye...
Muon::MuonStationIndex::ChIndex chamberIndex() const
Returns the chamber index scheme.
std::vector< Hit_t > HitVec_t
HitVec_t estimateBendingPars(HitVec_t &&hits, Parameters &pars) const
Estimate the bending parameters of a segment through a weighted linear regression.
std::unique_ptr< MdtSegmentSeeder > m_mdtSeeder
Pointer to the L-R segment seeder.
FastMuonSABuilder(const std::string &name, Config &&config)
Standard constructor.
Muon::MuonStationIndex::StIndex StIndex
Type alias for the station index.
std::unique_ptr< Segment > Segment_t
Type alias for the segment type.
SpacePointPerLayerSorter m_spSorter
Spacepoint sorter per logical measurement layer.
xAOD::Muon * buildMuonCandidate(const EventContext &ctx, const ActsTrk::GeometryContext &gctx, const GlobalPattern &pattern, MuonCont_t &outMuons) const
Main methods steering the muon candidate building.
std::unique_ptr< LineFitter > m_fitter
Pointer to the actual segment fitter.
const SpacePoint * Hit_t
Type alias for the hit type & associated vector.
const SpacePointBucket * Bucket_t
Type alias for the bucket type.
Segment_t fitSegment(const EventContext &ctx, const Amg::Transform3D &localToGlobal, Bucket_t parentBucket, std::vector< Hit_t > &&hits) const
Fit a segment using the provided seed.
std::unique_ptr< LineFitter > m_nswFitter
Pointer to the NSW segment fitter.
Config m_cfg
Global Pattern Recognition configuration.
xAOD::FillContainer< xAOD::MuonContainer, xAOD::MuonAuxContainerR4 > MuonCont_t
Define the muon container type.
SegmentFit::Parameters Parameters
Type alias for the segment fitting parameters.
Data class to represent an eta maximum in hough space.
std::vector< CalibSpacePointPtr > CalibSpacePointVec
SeedingState< HitVec_t, CalibCont_t, SeederStateBase > State_t
Define the state holder object.
Placeholder for what will later be the muon segment EDM representation.
unsigned int nDoF() const
Returns the number of degrees of freedom.
double chi2() const
Returns the chi2 of the segment fit.
: The muon space point bucket represents a collection of points that will bre processed together in t...
const MuonGMR4::SpectrometerSector * msSector() const
returns th associated muonChamber
The muon space point is the combination of two uncalibrated measurements one of them measures the eta...
void setAuthor(const Author auth)
set author
void setMuonType(MuonType type)
void setP4(double pt, double eta, double phi)
Set method for IParticle values.
void setCharge(float charge)
Set the charge (must be the same as primaryTrackParticle() ).
Acts::CalibrationContext getCalibrationContext(const EventContext &ctx)
The Acts::Calibration context is piped through the Acts fitters to (re)calibrate the Acts::SourceLink...
std::string toString(const Translation3D &translation, int precision=4)
GeoPrimitvesToStringConverter.
Eigen::Affine3d Transform3D
Eigen::Matrix< double, 3, 1 > Vector3D
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.
ISpacePointCalibrator::CalibSpacePointVec CalibSpacePointVec
std::string print(const cont_t &container)
Print a space point container to string.
StIndex toStationIndex(ChIndex index)
convert ChIndex into StIndex
bool isBarrel(const ChIndex index)
Returns true if the chamber index points to a barrel chamber.
LayerIndex
enum to classify the different layers in the muon spectrometer
const std::string & stName(StIndex index)
convert StIndex into a string
LayerIndex toLayerIndex(ChIndex index)
convert ChIndex into LayerIndex
l
Printing final latex table to .tex output file.
void swap(ElementLinkVector< DOBJ > &lhs, ElementLinkVector< DOBJ > &rhs)
bool isNSW(const UncalibMeasType aodType)
Returns whether the measurement is a NSW measurement.
Muon_v1 Muon
Reference the current persistent version:
const ISpacePointCalibrator * calibrator
Pointer to the calibrator.
bool doBeamSpot
Switch to insert a beamspot constraint if possible.
const Muon::IMuonIdHelperSvc * idHelperSvc
Pointer to the idHelperSvc.
unsigned nPrecHitCut
Minimum number of precision hits.
double outlierRemovalCut
Cut on the segment chi2 / nDoF to launch the outlier removal.
const MuonValR4::IPatternVisualizationTool * visionTool
Pointer to the visualization tool.
double recoveryPull
Maximum pull on a measurement to add it back on the line.
Full configuration object.