ATLAS Offline Software
Loading...
Searching...
No Matches
NswSegmentFinderAlg.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
6
12
13
16
19
21
22#include "Acts/Seeding/CombinatorialSeedSolver.hpp"
23
24#include <ranges>
25#include <format>
26
27using namespace Acts::Experimental::CombinatorialSeedSolver;
28namespace {
29 inline const MuonGMR4::StripDesign& getDesign(const MuonR4::SpacePoint& sp) {
31 const auto* prd = static_cast<const xAOD::MMCluster*>(sp.primaryMeasurement());
32 return prd->readoutElement()->stripLayer(prd->measurementHash()).design();
33 } else if (sp.type() == xAOD::UncalibMeasType::sTgcStripType) {
34 const auto* prd = static_cast<const xAOD::sTgcMeasurement*>(sp.primaryMeasurement());
35 const auto* re = prd->readoutElement();
36 switch(prd->channelType()) {
38 return re->stripDesign(prd->measurementHash());
40 return re->wireDesign(prd->measurementHash());
42 return re->padDesign(prd->measurementHash());
43 }
44 }
45 THROW_EXCEPTION("Invalid space point for design retrieval "<<sp.msSector()->idHelperSvc()->toString(sp.identify()));
46 }
47 inline double stripHalfLength(const MuonR4::SpacePoint& sp) {
48 const auto& design = getDesign(sp);
50 const auto* prd = static_cast<const xAOD::MMCluster*>(sp.primaryMeasurement());
51 return 0.5* design.stripLength(prd->channelNumber());
52 } else{
53 const auto* prd = static_cast<const xAOD::sTgcMeasurement*>(sp.primaryMeasurement());
54 if(prd->channelType() == sTgcIdHelper::Pad){
55 const auto& padDesign = static_cast<const MuonGMR4::PadDesign&>(design);
56 auto padCorners = padDesign.padCorners(prd->channelNumber());
57 return 0.5* std::abs(padCorners[0].x() - padCorners[1].x());
58 }
59 return 0.5* design.stripLength(prd->channelNumber());
60 }
61
62 return 0.;
63 }
64 inline std::string sTgcChannelType(const int chType) {
65 return chType == sTgcIdHelper::Strip ? "S" :
66 chType == sTgcIdHelper::Wire ? "W" : "P";
67 }
68
69 //local struct to encapsulate hit candidates data (e.g for seed extension)
70 struct HitCandidate {
71 double minPull{std::numeric_limits<float>::max()};
72 const MuonR4::SpacePoint* spacePoint{nullptr};
73 };
74}
75
76namespace MuonR4 {
77
78using namespace SegmentFit;
79constexpr unsigned minLayers{4};
80
82
84 ATH_CHECK(m_geoCtxKey.initialize());
85 ATH_CHECK(m_etaKey.initialize());
86 ATH_CHECK(m_writeSegmentKey.initialize());
87 ATH_CHECK(m_writeSegmentSeedKey.initialize());
88 ATH_CHECK(m_idHelperSvc.retrieve());
89 ATH_CHECK(m_calibTool.retrieve());
90 ATH_CHECK(m_visionTool.retrieve(DisableTool{m_visionTool.empty()}));
91 ATH_CHECK(detStore()->retrieve(m_detMgr));
92
93 if (!(m_idHelperSvc->hasMM() || m_idHelperSvc->hasSTGC())) {
94 ATH_MSG_ERROR("MM or STGC not part of initialized detector layout");
95 return StatusCode::FAILURE;
96 }
97
99 fitCfg.calibrator = m_calibTool.get();
100 fitCfg.visionTool = m_visionTool.get();
101 fitCfg.calcAlongStrip = false;
102 fitCfg.idHelperSvc = m_idHelperSvc.get();
103 fitCfg.parsToUse = {ParamDefs::x0, ParamDefs::y0, ParamDefs::theta, ParamDefs::phi};
104
105 m_lineFitter = std::make_unique<SegmentFit::SegmentLineFitter>(name(), std::move(fitCfg));
106
108 m_ambiSolver = std::make_unique<SegmentAmbiSolver>(name(), std::move(ambicfg));
109
110
112 m_seedCounter = std::make_unique<SeedStatistics>();
113 }
114
115 return StatusCode::SUCCESS;
116}
117
120 UsedHitMarker_t emptyKeeper(sortedSp.size());
121 for (std::size_t l = 0; l < sortedSp.size(); ++l) {
122 emptyKeeper[l].resize(sortedSp[l].size(), 0);
123 }
124 return emptyKeeper;
125}
126
129
131 const auto& design = getDesign(sp);
132 if (!design.hasStereoAngle()) {
133 return StripOrient::X;
134 }
135 return design.stereoAngle() > 0. ? StripOrient::U : StripOrient::V;
136 } else if (sp.type() == xAOD::UncalibMeasType::sTgcStripType) {
137 const auto* prd = static_cast<const xAOD::sTgcMeasurement*>(sp.primaryMeasurement());
138 if (sp.dimension() == 2) {
139 return StripOrient::C;
140 }
141 //check if we have strip only or wire only measurements
142 return prd->channelType() == sTgcIdHelper::Strip ? StripOrient::X : StripOrient::P;
143
144 }
145 ATH_MSG_WARNING("Cannot classify orientation of "<<m_idHelperSvc->toString(sp.identify()));
147}
150 const Amg::Vector3D& beamSpotPos,
151 const Amg::Vector3D& dirEstUp,
152 const Amg::Vector3D& dirEstDn) const{
153
154 const Amg::Vector3D estPlaneArrivalUp = SeedingAux::extrapolateToPlane(beamSpotPos, dirEstUp, testHit);
155 const Amg::Vector3D estPlaneArrivalDn = SeedingAux::extrapolateToPlane(beamSpotPos, dirEstDn, testHit);
156
157 bool below{true}, above{true};
158 switch (classifyStrip(testHit)) {
159 using enum StripOrient;
160 case U:
161 case V:{
162 const double halfLength = 0.5* stripHalfLength(testHit);
164 const Amg::Vector3D leftEdge = testHit.localPosition() - halfLength * testHit.sensorDirection();
165 const Amg::Vector3D rightEdge = testHit.localPosition() + halfLength * testHit.sensorDirection();
166
168 below = estPlaneArrivalDn.y() > std::max(leftEdge.y(), rightEdge.y());
170 above = estPlaneArrivalUp.y() < std::min(leftEdge.y(), rightEdge.y());
171 break;
172 } case X:
173 case C: {
175 const double hY = testHit.localPosition().y();
176 below = estPlaneArrivalDn.y() > hY;
178 above = estPlaneArrivalUp.y() < hY;
179 break;
180 }
181 case P:{
182 break;
183 }
184 case Unknown:{
185 break;
186 }
187
188 }
189 ATH_MSG_VERBOSE("Hit " << m_idHelperSvc->toString(testHit.identify())
190 << (below || above ? " is outside the window" : " is inside the window"));
191 if(below) {
192 return HitWindow::tooLow;
193 }
194 if(above) {
195 return HitWindow::tooHigh;
196 }
197 return HitWindow::inside;
198};
199
201#define TEST_HIT_CORRIDOR(LAYER, HIT_ITER, START_LAYER) \
202{ \
203 const SpacePoint* testMe = combinatoricLayers[LAYER].get()[HIT_ITER]; \
204 if (usedHits[LAYER].get()[HIT_ITER] > m_maxUsed) { \
205 ATH_MSG_VERBOSE(__func__<<":"<<__LINE__<<" - " \
206 <<m_idHelperSvc->toString(testMe->identify()) \
207 <<" already used in good seed." ); \
208 continue; \
209 } \
210 const HitWindow inWindow = hitFromIPCorridor(*testMe, beamSpot, dirEstUp, dirEstDn); \
211 if(inWindow == HitWindow::tooHigh) { \
212 ATH_MSG_VERBOSE(__func__<<":"<<__LINE__<<" - Hit " \
213 <<m_idHelperSvc->toString(testMe->identify()) \
214 <<" is beyond the corridor. Break loop"); \
215 break; \
216 } else if (inWindow == HitWindow::tooLow) { \
217 START_LAYER = HIT_ITER + 1; \
218 ATH_MSG_VERBOSE(__func__<<":"<<__LINE__<<" - Hit " \
219 <<m_idHelperSvc->toString(testMe->identify()) \
220 <<" is still below the corridor. Update start to " \
221 <<START_LAYER); \
222 continue; \
223 } \
224}
225
227 const HitLaySpan_t& combinatoricLayers,
228 const UsedHitSpan_t& usedHits,
229 InitialSeedVec_t& seedHitsFromLayers) const {
231 seedHitsFromLayers.clear();
232 std::size_t maxSize{1};
233 for (const HitVec& hitVec : combinatoricLayers) {
234 maxSize = maxSize * hitVec.size();
235 }
236 seedHitsFromLayers.reserve(maxSize);
237
238 unsigned iterLay0{0}, iterLay1{0}, iterLay2{0}, iterLay3{0};
239 unsigned startLay1{0}, startLay2{0}, startLay3{0};
240
241 for( ; iterLay0 < combinatoricLayers[0].get().size() ; ++iterLay0){
243 if (usedHits[0].get()[iterLay0] > m_maxUsed) {
244 continue;
245 }
246 const SpacePoint* hit0 = combinatoricLayers[0].get()[iterLay0];
248 const Amg::Vector3D initSeedDir{(beamSpot - hit0->localPosition()).unit()};
249 const Amg::Vector3D dirEstUp = Amg::dirFromAngles(initSeedDir.phi(), initSeedDir.theta() - m_windowTheta);
250 const Amg::Vector3D dirEstDn = Amg::dirFromAngles(initSeedDir.phi(), initSeedDir.theta() + m_windowTheta);
251
252 ATH_MSG_VERBOSE("Reference hit: "<<m_idHelperSvc->toString(hit0->identify())
253 <<", position: "<<Amg::toString(hit0->localPosition())
254 <<", seed dir: "<<Amg::toString(initSeedDir)
255 <<", seed plane: "<<Amg::toString(SeedingAux::extrapolateToPlane(beamSpot, initSeedDir, *hit0)));
257 for( iterLay1 = startLay1; iterLay1 < combinatoricLayers[1].get().size() ; ++iterLay1){
258 TEST_HIT_CORRIDOR(1, iterLay1, startLay1);
259 for( iterLay2 = startLay2; iterLay2 < combinatoricLayers[2].get().size() ; ++iterLay2){
260 TEST_HIT_CORRIDOR(2, iterLay2, startLay2);
261 for( iterLay3 = startLay3; iterLay3 < combinatoricLayers[3].get().size(); ++iterLay3){
262 TEST_HIT_CORRIDOR(3, iterLay3, startLay3);
263 seedHitsFromLayers.emplace_back(std::array{hit0, combinatoricLayers[1].get()[iterLay1],
264 combinatoricLayers[2].get()[iterLay2],
265 combinatoricLayers[3].get()[iterLay3]});
266 }
267 }
268 }
269 }
270}
271#undef TEST_HIT_CORRIDOR
272
275 const Amg::Vector3D& direction,
276 const HitLaySpan_t& extensionLayers,
277 const UsedHitSpan_t& usedHits) const {
278
279 //the hits we need to return to extend the segment seed
280 HitVec combinatoricHits;
281
282 for (std::size_t i = 0; i < extensionLayers.size(); ++i) {
283 const HitVec& layer{extensionLayers[i].get()};
284 const Amg::Vector3D extrapPos = SeedingAux::extrapolateToPlane(startPos, direction, *layer.front());
285
286
287 unsigned triedHit{0};
288 HitCandidate precisionHit, noPrecisionHit;
289 ATH_MSG_VERBOSE("Moving to next layer");
290
291 for (unsigned j = 0; j < layer.size(); ++j) {
292 if (usedHits[i].get().at(j) > m_maxUsed) {
293 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Hit " << (*layer[j])<< " already used with counter " << usedHits[i].get().at(j) << ". Skip.");
294 continue;
295 }
296 auto hit = layer.at(j);
297 const double pull = std::sqrt(SeedingAux::chi2Term(extrapPos, direction, *hit));
298 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Trying extension with hit " << *hit<<" and pull "<<pull<<" has truth: "<< (getTruthMatchedHit(*hit->primaryMeasurement()) != nullptr ? "oui" : "non"));
299 bool isPrecision = isPrecisionHit(*hit);
300 double minPull = isPrecision ? precisionHit.minPull : noPrecisionHit.minPull;
301 ATH_MSG_VERBOSE("Current min pull for this layer is: "<<minPull);
302 //find the hit with the minimum pull (check at least three hits after we have increasing pulls)
303 if (pull > minPull) {
304 triedHit+=1;
305 continue;
306 }
307
308 if(triedHit>3){
309 break;
310 }
311
312 if(isPrecision){
313 precisionHit.spacePoint = hit;
314 precisionHit.minPull = pull;
315 continue;
316 }
317
318 noPrecisionHit.spacePoint = hit;
319 noPrecisionHit.minPull = pull;
320 }
321
322 // complete the seed with the extended hits
323 //we first choose the precision hit and if does not exist then we pick the non precision hit
324 const SpacePoint* bestCand{nullptr};
325 if(precisionHit.minPull < m_minPullThreshold){
326 bestCand = precisionHit.spacePoint;
327 }else if(noPrecisionHit.minPull < m_minPullThreshold){
328 bestCand = noPrecisionHit.spacePoint;
329 }else{
330 ATH_MSG_VERBOSE("No hit found in layer "<<i<<" with pull below threshold "<<m_minPullThreshold);
331 continue;
332 }
333 ATH_MSG_VERBOSE("Extension successfull - hit" << m_idHelperSvc->toString(bestCand->identify())
334 <<", pos: "<<Amg::toString(bestCand->localPosition())
335 <<", dir: "<<Amg::toString(bestCand->sensorDirection()));
336 combinatoricHits.push_back(bestCand);
337 }
338
339 return combinatoricHits;
340}
341
342std::unique_ptr<SegmentSeed>
344 const AmgSymMatrix(2)& bMatrix,
345 const HoughMaximum& max,
346 const HitLaySpan_t& extensionLayers,
347 const UsedHitSpan_t& usedHits) const {
348 bool allValid = std::any_of(initialSeed.begin(), initialSeed.end(),
349 [this](const auto& hit){
350 if (hit->type() == xAOD::UncalibMeasType::MMClusterType) {
351 const auto* mmClust = static_cast<const xAOD::MMCluster*>(hit->primaryMeasurement());
352 return mmClust->stripNumbers().size() >= m_minClusSize;
353 }
354 return true;
355 });
356
357 if (!allValid) {
358 ATH_MSG_VERBOSE("Seed rejection: Not all clusters meet minimum strip size");
359 return nullptr;
360 }
361
362
363 std::array<double, 4> params = defineParameters(bMatrix, initialSeed);
364
365 const auto [segPos, direction] = seedSolution(initialSeed, params);
366
367 // check the consistency of the parameters - expected to lay in the strip's
368 // length
369 for (std::size_t i = 0; i < 4; ++i) {
370 const double halfLength = stripHalfLength(*initialSeed[i]);
371
372 if (std::abs(params[i]) > halfLength) {
373 ATH_MSG_VERBOSE("Seed Rejection: Invalid seed - outside of the strip's length "<< m_idHelperSvc->toString(initialSeed[i]->identify())
374 <<", param: "<<params[i]<<", halfLength: "<<halfLength);
375 return nullptr;
376 }
377 }
378 double tanAlpha = houghTanAlpha(direction);
379 double tanBeta = houghTanBeta(direction);
380
381 double interceptX = segPos.x();
382 double interceptY = segPos.y();
383
384 //seed quality check - we expect the tanAlpha not to be too big which would mean big deflection along the strip layers
385 if(std::abs(tanAlpha) > m_maxTanAlpha){
386 ATH_MSG_VERBOSE("Seed Rejection: Invalid seed - tanAlpha "<<tanAlpha<<" above threshold "<<m_maxTanAlpha);
387 return nullptr;
388 }
389
390
391 // extend the seed to the segment -- include hits from the other layers too
392 auto extendedHits = extendHits(segPos, direction, extensionLayers, usedHits);
393 HitVec hits{initialSeed.begin(),initialSeed.end()};
394 std::ranges::move(extendedHits, std::back_inserter(hits));
395
396 return std::make_unique<SegmentSeed>(tanBeta, interceptY, tanAlpha,
397 interceptX, hits.size(),
398 std::move(hits), max.parentBucket());
399}
400
401
402std::unique_ptr<Segment> NswSegmentFinderAlg::fitSegmentSeed(const EventContext& ctx,
403 const ActsTrk::GeometryContext& gctx,
404 const SegmentSeed* patternSeed) const{
405
406 if(patternSeed->getHitsInMax().size() < m_minSeedHits){
407 ATH_MSG_VERBOSE("Not enough hits in the SegmentSeed to fit a segment");
408 return nullptr;
409 }
410
411 ATH_MSG_VERBOSE("Fit the SegmentSeed");
412 if (msgLvl(MSG::VERBOSE)) {
413 std::stringstream hitStream{};
414 for (const auto& hit : patternSeed->getHitsInMax()) {
415 hitStream<<"**** "<< (*hit)<<std::endl;
416 }
417 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__ <<": Uncalibrated space points for the segment fit: "<<std::endl
418 <<hitStream.str());
419 }
420
421 //Calibration of the seed spacepoints
422 CalibSpacePointVec calibratedHits = m_calibTool->calibrate(ctx, patternSeed->getHitsInMax(),
423 patternSeed->localPosition(),
424 patternSeed->localDirection(), 0.);
425
426 const Amg::Transform3D& locToGlob{patternSeed->msSector()->localToGlobalTransform(gctx)};
427
428 return m_lineFitter->fitSegment(ctx, patternSeed, patternSeed->parameters(),
429 locToGlob, std::move(calibratedHits));
430}
431
432void NswSegmentFinderAlg::processSegment(std::unique_ptr<Segment> segment,
433 const HitVec& seedHits,
434 const HitLayVec& hitLayers,
435 UsedHitMarker_t& usedHits,
436 SegmentVec_t& segments) const {
437
438 if (!segment) {
439 ATH_MSG_VERBOSE("Seed Rejection: Segment fit failed");
440
441 if (m_markHitsFromSeed && seedHits.size() > m_minSeedHits) {
442 // Mark hits from extended seed (used increment by 1)
443 markHitsAsUsed(seedHits, hitLayers, usedHits, 1, false);
444 }
445 return;
446 }
447
448 // -------- success path --------
449 ATH_MSG_DEBUG("Segment built with "
450 << segment->measurements().size()
451 << " hits, chi2/ndof: "
452 << segment->chi2() / std::max(1u,segment->nDoF()));
453
454 HitVec segMeasSP;
455 segMeasSP.reserve(segment->measurements().size());
456
457 std::ranges::transform(
458 segment->measurements(),
459 std::back_inserter(segMeasSP),
460 [](const auto& m) { return m->spacePoint(); }
461 );
462
463 // Mark segment hits as fully used (used increment by 10,
464 // hits are effectively removed)
465 markHitsAsUsed(segMeasSP, hitLayers, usedHits, 10, true);
466 segments.push_back(std::move(segment));
467
468}
469
471 SegmentVec_t& segmentCandidates) const{
472
473 if(segmentCandidates.size()<=1){
474 ATH_MSG_VERBOSE("No segments to resolve ambiguities");
475 return;
476 }
477
478 ATH_MSG_VERBOSE("Resolving ambiguities for "<<segmentCandidates.size()<<" segments");
479
482
483 //sort segments per chamber and resolve ambiguities per chamber
484 for (std::unique_ptr<Segment>& seg : segmentCandidates) {
485 const MuonGMR4::SpectrometerSector* chamb = seg->msSector();
486 segmentsPerChamber[chamb].push_back(std::move(seg));
487 }
488 segmentCandidates.clear();
489 for (auto& [chamber, resolveMe] : segmentsPerChamber) {
490 SegmentVec_t resolvedSegments = m_ambiSolver->resolveAmbiguity(gctx, std::move(resolveMe));
491 segmentCandidates.insert(segmentCandidates.end(),
492 std::make_move_iterator(resolvedSegments.begin()),
493 std::make_move_iterator(resolvedSegments.end()));
494 }
495
496}
497
498std::pair<NswSegmentFinderAlg::SegmentSeedVec_t, NswSegmentFinderAlg::SegmentVec_t>
500 const ActsTrk::GeometryContext &gctx,
501 const HitLayVec& hitLayers,
502 const HoughMaximum& max,
503 const Amg::Vector3D& beamSpotPos,
504 UsedHitMarker_t& usedHits) const {
505
506 //go through the layers and build seeds from the combinations of hits
507 //starting from the outermost layers with 2D measurements (excluding pads)
508 SegmentSeedVec_t seeds{};
509 SegmentVec_t segments{};
510 std::size_t layerSize = hitLayers.size();
511 double thetaWindowCut{std::cos(2*m_windowTheta)};
512
513 // lamda helper to check if the spacepoint is combined (but not pad) and unused in an already constructed seed
514 auto isUnusedCombined = [&](std::size_t layIdx, std::size_t hitIdx) -> bool {
515 const SpacePoint* sp = hitLayers[layIdx][hitIdx];
517 THROW_EXCEPTION("Space point is not of sTgc type: "<<sp->msSector()->idHelperSvc()->toString(sp->identify()));
518 }
519 if(sp->dimension() != 2){
520 ATH_MSG_VERBOSE("Ignoring the 1D measurement for seeding: "<<m_idHelperSvc->toString(sp->identify()));
521 return false;
522 }
523 const auto* prdPrim = static_cast<const xAOD::sTgcMeasurement*>(sp->primaryMeasurement());
524 const auto* prdSec = static_cast<const xAOD::sTgcMeasurement*>(sp->secondaryMeasurement());
525 //we know we have a 2D spacepoint can be: pad, strip+pad, strip+wire, wire+pad and we dont want pads appearing in this seeding stage
526 bool hasPad = prdSec ? prdSec->channelType()==sTgcIdHelper::sTgcChannelTypes::Pad : prdPrim->channelType()==sTgcIdHelper::sTgcChannelTypes::Pad;
527 if(hasPad){
528 ATH_MSG_VERBOSE("Ignoring the 2D pad measurement for seeding because of pads: "<<m_idHelperSvc->toString(sp->identify()));
529 return false;
530 }
531 bool isPrecision = isPrecisionHit(*sp);
532 return isPrecision && usedHits[layIdx].at(hitIdx) <= m_maxUsed;
533
534 };
535
536 // find the 2D measurements from the outermost layers - even move one layer inside
537 for(std::size_t layIdx1 = 0; layIdx1 < 2; ++layIdx1){
538 for(std::size_t layIdx2 = layerSize-1; layIdx2 >= layerSize-2; --layIdx2){
539 //in case of MM layers we stop - the layers are sorted in Z
540 if(hitLayers[layIdx1].front()->type() == xAOD::UncalibMeasType::MMClusterType ||
541 hitLayers[layIdx2].front()->type() == xAOD::UncalibMeasType::MMClusterType){
542 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<"Outermost layers are MM - stop searching for sTgc Measurements");
543 return std::make_pair(std::move(seeds), std::move(segments));
544 }
545
546 //check if we have 2D measurements on these layers that are unused (excluding the pads)
547 for(std::size_t hitIdx1 = 0; hitIdx1 < hitLayers[layIdx1].size(); ++hitIdx1) {
548 const SpacePoint* hit1 = hitLayers[layIdx1][hitIdx1];
549 if(!isUnusedCombined(layIdx1, hitIdx1)){
550 continue;
551 }
552 for(std::size_t hitIdx2 = 0; hitIdx2 < hitLayers[layIdx2].size(); ++hitIdx2) {
553 const SpacePoint* hit2 = hitLayers[layIdx2][hitIdx2];
554 if(!isUnusedCombined(layIdx2, hitIdx2)){
555 continue;
556 }
557 //test if this selection of the hits from the two layers is aligned with the beam spot
558 const Amg::Vector3D beamSpotHitDir{(beamSpotPos - hit1->localPosition()).unit()};
559 const Amg::Vector3D seedDir{(hit2->localPosition() - hit1->localPosition()).unit()};
560 const double cosAngle = beamSpotHitDir.dot(seedDir);
561 //accept the deflection of direction with a tolerance of 1 deg
562 if(std::abs(cosAngle) < thetaWindowCut){
563 continue;
564 }
565 //found 2D hits on the outermost layers - build a seed
566 HitVec seedHits{hit1, hit2};
567 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<"Attempt for STGC segment starting with the hits: " << *hit1 << ", " << *hit2);
568 //get the seed direction and position from the two 2D hits
569 const Amg::Vector3D seedPos = hit1->localPosition();
570 //express position in z=0
571 const Amg::Vector3D seedPosZ0 = seedPos + Amg::intersect<3>(seedPos, seedDir, Amg::Vector3D::UnitZ(), 0.).value_or(0.)*seedDir;
572 // extend the seed to the other layers
573 HitLaySpan_t extensionLayers{};
574 UsedHitSpan_t usedExtensionHits{};
575 extensionLayers.reserve(hitLayers.size());
576 usedExtensionHits.reserve(hitLayers.size());
577 for (std::size_t e = 0 ; e < hitLayers.size(); ++e) {
578 if (!(e == layIdx1 || e == layIdx2)){
579 extensionLayers.emplace_back(hitLayers[e]);
580 usedExtensionHits.emplace_back(usedHits[e]);
581 }
582 }
583 auto extendedHits = extendHits(seedPosZ0, seedDir, extensionLayers, usedExtensionHits);
584 std::ranges::move(extendedHits, std::back_inserter(seedHits));
585
586 //make seed
587 auto seed = std::make_unique<SegmentSeed>(houghTanBeta(seedDir), seedPosZ0.y(),
588 houghTanAlpha(seedDir), seedPosZ0.x(),
589 seedHits.size(), std::move(seedHits),
590 max.parentBucket());
591
592 //skip segment fit with less than 5 hits
593 if(seed->getHitsInMax().size() < m_minSeedHits){
594 seeds.push_back(std::move(seed));
595 continue;
596 }
597 //fit the segment seed
598 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Start to fit an STGC segment seed with "<<seed->getHitsInMax().size()<<" hits \n"<<print(seed->getHitsInMax()));
599 std::unique_ptr<Segment> segment = fitSegmentSeed(ctx, gctx, seed.get());
600 processSegment(std::move(segment), seed->getHitsInMax(), hitLayers, usedHits, segments);
601 seeds.push_back(std::move(seed));
602
603 }
604 }
605 }
606 }
607 return std::make_pair(std::move(seeds),std::move(segments));
608}
609
610std::pair<NswSegmentFinderAlg::SegmentSeedVec_t, NswSegmentFinderAlg::SegmentVec_t>
612 const ActsTrk::GeometryContext &gctx,
613 const HitLayVec& hitLayers,
614 const HoughMaximum& max,
615 const Amg::Vector3D& beamSpotPos,
616 UsedHitMarker_t& usedHits,
617 bool useOnlyMM) const {
618
619 //go through the layers and build seeds from the combinations of hits
620 SegmentSeedVec_t seeds{};
621 SegmentVec_t segments{};
622 std::size_t layerSize = hitLayers.size();
623
624 if(layerSize < minLayers){
625 ATH_MSG_VERBOSE("Not enough layers to build a seed");
626 return {std::move(seeds), std::move(segments)};
627 }
628
629 //lamda helper to find the first unused strip hit on the layer
630 auto unusedStripHit = [&](const HitVec& layerHits, unsigned int layIdx) -> const SpacePoint* {
631 //in case of MM only combinatorial seeding - we consider only MM strip hits for seeding
632 bool isMM = useOnlyMM ? layerHits.front()->type() == xAOD::UncalibMeasType::MMClusterType : true;
633 for (auto [idx, hit] : Acts::enumerate(layerHits)) {
634 auto spOrient = classifyStrip(*hit);
635 bool isStrip = spOrient == StripOrient::X || spOrient == StripOrient::U || spOrient == StripOrient::V;
636 bool isUnused = usedHits[layIdx].at(idx) <= m_maxUsed;
637 if (isStrip && isUnused && isMM) {
638 return hit;
639 }
640 }
641 return nullptr;
642 };
643
644 std::array<const SpacePoint*, 4> seedHits{};
645 InitialSeedVec_t preLimSeeds{};
646
647 for (std::size_t i = 0; i < layerSize - 3; ++i) {
648 seedHits[0] = unusedStripHit(hitLayers[i], i);
649 if(!seedHits[0]) {
650 continue;
651 }
652 for (std::size_t j = i + 1; j < layerSize - 2; ++j) {
653 seedHits[1] = unusedStripHit(hitLayers[j], j);
654 if(!seedHits[1]){
655 continue;
656 }
657 for (std::size_t l = layerSize - 1; l > j+1; --l) {
658 seedHits[3] = unusedStripHit(hitLayers[l], l);
659 if(!seedHits[3]){
660 continue;
661 }
662 for (std::size_t k = l-1; k > j ; --k) {
663 seedHits[2] = unusedStripHit(hitLayers[k], k);
664 if(!seedHits[2]){
665 continue;
666 }
667
668 const HitLaySpan_t layers{hitLayers[i], hitLayers[j], hitLayers[k], hitLayers[l]};
669 //skip combination with at least one too busy layer
670 bool tooBusy = std::ranges::any_of(layers,
671 [this](const auto& layer) {
672 return layer.get().size() > m_maxClustersInLayer;
673 });
674 if (tooBusy) {
675 continue; // skip this combination
676 }
677
678 AmgSymMatrix(2) bMatrix = betaMatrix(seedHits);
679
680 if (std::abs(bMatrix.determinant()) < 1.e-6) {
681 continue;
682 }
683 ATH_MSG_DEBUG("Space point positions for seed layers: \n"
684 <<(*seedHits[0]) << ",\n"
685 <<(*seedHits[1]) << ",\n"
686 <<(*seedHits[2]) << ",\n"
687 <<(*seedHits[3]));
688
689
690 UsedHitSpan_t usedHitsSpan{usedHits[i], usedHits[j], usedHits[k], usedHits[l]};
691 // each layer may have more than one hit - take the hit combinations
692 constructPreliminarySeeds(beamSpotPos, layers, usedHitsSpan, preLimSeeds);
693
694 //the layers not participated in the seed build - gonna be used for the extension
695 HitLaySpan_t extensionLayers{};
696 UsedHitSpan_t usedExtensionHits{};
697 usedExtensionHits.reserve(hitLayers.size());
698 extensionLayers.reserve(hitLayers.size());
699 for (std::size_t e = 0 ; e < hitLayers.size(); ++e) {
700 if (!(e == i || e == j || e == k || e == l)){
701 extensionLayers.emplace_back(hitLayers[e]);
702 usedExtensionHits.emplace_back(usedHits[e]);
703 }
704 }
705 // we have made sure to have hits from all the four layers -
706 // start by 4 hits for the seed and try to build the extended seed for the combinatorics found
707 for (auto &combinatoricHits : preLimSeeds) {
708 auto seed = constructCombinatorialSeed(combinatoricHits, bMatrix, max, extensionLayers, usedExtensionHits);
709 if(!seed){
710 continue;
711 }
712 if (seed->getHitsInMax().size() < m_minSeedHits) {
713 seeds.push_back(std::move(seed));
714 continue;
715 }
716 std::unique_ptr<Segment> segment = fitSegmentSeed(ctx, gctx, seed.get());
717 processSegment(std::move(segment), seed->getHitsInMax(), hitLayers, usedHits, segments);
718 seeds.push_back(std::move(seed));
719
720 }
721 }
722 }
723 }
724 }
725 return std::make_pair(std::move(seeds),std::move(segments));
726}
727
728std::pair<NswSegmentFinderAlg::SegmentSeedVec_t, NswSegmentFinderAlg::SegmentVec_t>
730 const ActsTrk::GeometryContext &gctx,
731 const EventContext& ctx) const {
732 // first sort the hits per layer from the maximum
733 SpacePointPerLayerSplitter hitLayers{max.getHitsInMax()};
734
735 const HitLayVec& stripHitsLayers{hitLayers.stripHits()};
736 const std::size_t layerSize = stripHitsLayers.size();
737
738 //seeds and segments containers
739 SegmentSeedVec_t seeds{};
740 SegmentVec_t segments{};
741
742 const Amg::Transform3D globToLocal = max.msSector()->globalToLocalTransform(gctx);
743 //counters for the number of seeds, extented seeds and segments
744
745 if (layerSize < minLayers) {
746 ATH_MSG_VERBOSE("Not enough layers to build a seed");
747 return std::make_pair(std::move(seeds),std::move(segments));
748 }
749
750 if (m_visionTool.isEnabled()) {
752 constexpr double legX{0.2};
753 double legY{0.8};
754 for (const SpacePoint* sp : max.getHitsInMax()) {
755 const xAOD::MuonSimHit* simHit = getTruthMatchedHit(*sp->primaryMeasurement());
756 if (!simHit) {
757 continue;
758 }
759
760 const MuonGMR4::MuonReadoutElement* reEle = m_detMgr->getReadoutElement(simHit->identify());
761 const Amg::Transform3D toChamb = reEle->msSector()->globalToLocalTransform(gctx) *
762 reEle->localToGlobalTransform(gctx, sp->identify());
763
764 const Amg::Vector3D hitPos = toChamb * xAOD::toEigen(simHit->localPosition());
765 const Amg::Vector3D hitDir = toChamb.linear() * xAOD::toEigen(simHit->localDirection());
766 const double pull = std::sqrt(SeedingAux::chi2Term(hitPos, hitDir, *sp));
767
769 const auto* mmClust = static_cast<const xAOD::MMCluster*>(sp->primaryMeasurement());
770 const MuonGMR4::MmReadoutElement* mmEle = mmClust->readoutElement();
771 const auto& design = mmEle->stripLayer(mmClust->measurementHash()).design();
772 std::string stereoDesign{!design.hasStereoAngle() ? "X" : design.stereoAngle() >0 ? "U": "V"};
773 primitives.push_back(MuonValR4::drawLabel(std::format("ml: {:1d}, gap: {:1d}, {:}, pull: {:.2f}",
774 mmEle->multilayer(), mmClust->gasGap(),
775 stereoDesign, pull), legX, legY, 14));
776 } else if(sp->type() == xAOD::UncalibMeasType::sTgcStripType) {
777 const auto* sTgcMeas = static_cast<const xAOD::sTgcMeasurement*>(sp->primaryMeasurement());
778 std::string channelString = sp->secondaryMeasurement() == nullptr ?
779 sTgcChannelType(sTgcMeas->channelType()) :
780 std::format("{:}/{:}", sTgcChannelType(sTgcMeas->channelType()),
781 sTgcChannelType(static_cast<const xAOD::sTgcMeasurement*>(sp->secondaryMeasurement())->channelType()));
782 primitives.push_back(MuonValR4::drawLabel(std::format("ml: {:1d}, gap: {:1d}, type: {:}, pull: {:.2f}",
783 sTgcMeas->readoutElement()->multilayer(), sTgcMeas->gasGap(),
784 channelString, pull), legX, legY, 14));
785 }
786 legY-=0.05;
787 }
788 m_visionTool->visualizeBucket(ctx, *max.parentBucket(),
789 "truth", std::move(primitives));
790 }
791
792 //dump spacepoints associated with truth sim hits to an obj file
793 if(m_dumpObj){
794 Acts::ObjVisualization3D visualHelper{};
795 for (const SpacePoint* sp : max.getHitsInMax()) {
796 const xAOD::MuonSimHit* simHit = getTruthMatchedHit(*sp->primaryMeasurement());
797 if (!simHit) {
798 continue;
799 }
800 MuonValR4::drawSpacePoint(gctx.context(), *sp, visualHelper);
801 }
802 visualHelper.write(std::format("Event_{:}_{:}_spacepoints_truth.obj", ctx.eventID().event_number(), max.getHitsInMax().front()->chamber()->identString()));
803 }
804
805
806 UsedHitMarker_t allUsedHits = emptyBookKeeper(stripHitsLayers);
807 std::size_t nSeeds{0}, nExtSeeds{0}, nSegments{0}; //for the seed statistics
808
809 // helper lamda function to increase counters and fill the seeds and segments we want to return after we construct them
810 // the extended seeds are returned even if they did not make it to a segment and the segments only if successfully fitted
811 auto processSeedsAndSegments = [&](std::pair<SegmentSeedVec_t, SegmentVec_t>&& seedSegmentPairs, std::string_view source) {
812 auto& [returnSeeds, returnSegments] = seedSegmentPairs;
813 ATH_MSG_DEBUG("From " << source << ": built " << returnSeeds.size() << " seeds and " << returnSegments.size() << " segments.");
814 for(auto& seed : returnSeeds) {
815 ++nSeeds;
816 Acts::ObjVisualization3D visualHelper{};
817 if(seed->getHitsInMax().size() < m_minSeedHits){
818 ATH_MSG_VERBOSE("Seed with "<< seed->getHitsInMax().size() <<" hits rejected");
819 for(const auto& hit : seed->getHitsInMax()){
820 ATH_MSG_VERBOSE("Hit "<<m_idHelperSvc->toString(hit->identify())<<", "
821 <<Amg::toString(hit->localPosition())<<", dir: "
822 <<Amg::toString(hit->sensorDirection()));
823 if(m_dumpObj){
824 MuonValR4::drawSpacePoint(gctx.context(), *hit, visualHelper);
825 }
826
827 }
828 if(m_dumpObj){
829 visualHelper.write(std::format("Event_{:}_{:}_notExtendedSeed.obj", ctx.eventID().event_number(), seed->getHitsInMax().front()->chamber()->identString()));
830 }
831 continue;
832 }
833 ++nExtSeeds;
834 seeds.push_back(std::move(seed));
835 }
836 //move all the segments to the output container
837 std::ranges::move(returnSegments, std::back_inserter(segments));
838 nSegments += returnSegments.size();
839 };
840
841 //Start from outermost sTgc layers with combined 2D measurements
842 ATH_MSG_VERBOSE("Start building seed from sTgc outermost layers");
843 processSeedsAndSegments(buildSegmentsFromSTGC(ctx, gctx, stripHitsLayers, max, globToLocal.translation(), allUsedHits), "sTgc segment seeds");
844
845 //continue with the combinatorial seeding for the strip measurements
847
848 ATH_MSG_VERBOSE("Start building combinatoric seeds only from Micromegas");
849 processSeedsAndSegments(buildSegmentsFromMM(ctx, gctx, stripHitsLayers, max, globToLocal.translation(), allUsedHits, true), "MM combinatoric segment seeds");
850
851 }else{
852
853 ATH_MSG_VERBOSE("Start building combinatoric seeds from Micromegas and sTgc hits");
854 processSeedsAndSegments(buildSegmentsFromMM(ctx, gctx, stripHitsLayers, max, globToLocal.translation(), allUsedHits, true), "MM combinatoric segment seeds");
855 processSeedsAndSegments(buildSegmentsFromMM(ctx, gctx, stripHitsLayers, max, globToLocal.translation(), allUsedHits, false), "MM and STGC combinatoric segment seeds");
856
857 }
858
859 if(m_seedCounter) {
860 m_seedCounter->addToStat(max.msSector(), nSeeds, nExtSeeds, nSegments);
861 }
862
863 return std::make_pair(std::move(seeds),std::move(segments));
864}
865
867 const HitLayVec& allSortHits,
868 UsedHitMarker_t& usedHitMarker,
869 unsigned incr,
870 bool markNeighborHits) const {
871
872 SpacePointPerLayerSorter layerSorter{};
873
874 for(const auto& sp : spacePoints){
875 // Proection against the auxiliary measurement
876 if(!sp){
877 continue;
878 }
879
880 unsigned measLayer = layerSorter.sectorLayerNum(*sp);
881
882 Amg::Vector2D spPosX{Amg::Vector2D::Zero()};
884 switch (sp->primaryMeasurement()->numDimensions()) {
885 case 1:
886 spPosX[Amg::x] = sp->primaryMeasurement()->localPosition<1>().x();
887 break;
888 case 2:
889 spPosX = xAOD::toEigen(sp->primaryMeasurement()->localPosition<2>());
890 break;
891 default:
892 THROW_EXCEPTION("Unsupported dimension");
893 }
894
895 for (std::size_t lIdx = 0; lIdx < allSortHits.size(); ++lIdx) {
896 const HitVec& hVec{allSortHits[lIdx]};
897 //check if they are not in the same layer
898 unsigned hitLayer = layerSorter.sectorLayerNum(*hVec.front());
899 if(hitLayer != measLayer) {
900 ATH_MSG_VERBOSE("Not in the same layer since measLayer = "<< measLayer << " and "<<hitLayer);
901 continue;
902 }
903 for (std::size_t hIdx = 0 ; hIdx < hVec.size(); ++hIdx) {
904 //check the dY between the measurement and the hits
905 auto testHit = hVec[hIdx];
906 if (testHit == sp) {
907 usedHitMarker[lIdx][hIdx] += incr;
908 if(!markNeighborHits){
909 break;
910 }
911 } else if (markNeighborHits) {
912 Amg::Vector2D testPosX{Amg::Vector2D::Zero()};
914 switch (testHit->primaryMeasurement()->numDimensions()) {
915 case 1:
916 testPosX[Amg::x] = testHit->primaryMeasurement()->localPosition<1>().x();
917 break;
918 case 2:
919 testPosX = xAOD::toEigen(testHit->primaryMeasurement()->localPosition<2>());
920 break;
921 default:
922 THROW_EXCEPTION("Unsupported dimension");
923 }
924 //if the hit not found let's see if it is too close to the segment's measurement
925 double deltaX = (testPosX - spPosX).mag();
926 if(deltaX < m_maxdYWindow){
927 usedHitMarker[lIdx][hIdx] += incr;
928 }
929 }
930 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<"- Marking hit "<<(*testHit)<<", used count: "
931 <<usedHitMarker[lIdx][hIdx]);
932 }
933 }
934 }
935}
936
937StatusCode NswSegmentFinderAlg::execute(const EventContext &ctx) const {
938 // read the inputs
939 const EtaHoughMaxContainer *maxima{nullptr};
940 ATH_CHECK(SG::get( maxima, m_etaKey, ctx));
941
942 const ActsTrk::GeometryContext *gctx{nullptr};
943 ATH_CHECK(SG::get(gctx, m_geoCtxKey, ctx));
944
945 // prepare our output collection
946 SG::WriteHandle writeSegments{m_writeSegmentKey, ctx};
947 ATH_CHECK(writeSegments.record(std::make_unique<SegmentContainer>()));
948
949 SG::WriteHandle writeSegmentSeeds{m_writeSegmentSeedKey, ctx};
950 ATH_CHECK(writeSegmentSeeds.record(std::make_unique<SegmentSeedContainer>()));
951
952 // we use the information from the previous eta-hough transform
953 // to get the combined hits that belong in the same maxima
954 for (const HoughMaximum *max : *maxima) {
955
956 auto [seeds, segments] = findSegmentsFromMaximum(*max, *gctx, ctx);
957
958 if (msgLvl(MSG::VERBOSE)) {
959 ATH_MSG_VERBOSE("Hits from Hough maximum");
960 for(const auto& hitMax : max->getHitsInMax()){
961 ATH_MSG_VERBOSE("Hit "<<m_idHelperSvc->toString(hitMax->identify())<<", "
962 <<Amg::toString(hitMax->localPosition())<<", dir: "
963 <<Amg::toString(hitMax->sensorDirection()));
964 }
965 }
966
967 for(auto& seed: seeds){
968
969 if (msgLvl(MSG::VERBOSE)){
970 std::stringstream sstr{};
971 sstr<<"Seed tanBeta = "<<seed->tanBeta()<<", y0 = "<<seed->interceptY()
972 <<", tanAlpha = "<<seed->tanAlpha()<<", x0 = "<<seed->interceptX()<<", hits in the seed "
973 <<seed->getHitsInMax().size()<<std::endl;
974
975 for(const auto& hit : seed->getHitsInMax()){
976 sstr<<" *** Hit "<<m_idHelperSvc->toString(hit->identify())<<", "
977 << Amg::toString(hit->localPosition())<<", dir: "<<Amg::toString(hit->sensorDirection())<<std::endl;
978 }
979 ATH_MSG_VERBOSE(sstr.str());
980 }
981 if (m_visionTool.isEnabled()) {
982 m_visionTool->visualizeSeed(ctx, *seed, "#phi-combinatorialSeed");
983 }
984
985 writeSegmentSeeds->push_back(std::move(seed));
986
987 }
988
989 //Resolve ambiguities between segments before writing them to the output
990 ATH_MSG_VERBOSE("Before ambiguity resolution, there are in total "<<segments.size()<<" segments:");
991 for(const auto& seg : segments){
992
993 if(msgLvl(MSG::VERBOSE)){
994 std::stringstream sstr{};
995 sstr<<"Segment chi2/ndof = "<<seg->chi2()/std::max(1u,seg->nDoF())<<", hits in the segment "
996 <<seg->measurements().size()<<std::endl;
997
998 const Parameters pars = localSegmentPars(*gctx, *seg);
999 sstr<<"Segment parameters : "<<toString(pars)<<std::endl;
1000 for(const auto& hit : seg->measurements()){
1001 bool hasTruth{false};
1002 if(hit->type()!=xAOD::UncalibMeasType::Other){
1003 hasTruth = (getTruthMatchedHit(*hit->spacePoint()->primaryMeasurement()) !=nullptr);
1004
1005 sstr<<" *** Hit "<<m_idHelperSvc->toString(hit->spacePoint()->identify())<<", "
1006 << Amg::toString(hit->spacePoint()->localPosition())<<", dir: "
1007 <<Amg::toString(hit->spacePoint()->sensorDirection())<<", has truth matched: "<<hasTruth<<std::endl;
1008 }
1009 }
1010 ATH_MSG_VERBOSE(sstr.str());
1011
1012 }
1013
1014 }
1015
1016
1017 resolveAmbiguities(*gctx, segments);
1018 ATH_MSG_VERBOSE("After ambiguity resolution, there are in total "<<segments.size()<<" segments:");
1019
1020 for (auto &seg : segments) {
1021 if(msgLvl(MSG::VERBOSE)){
1022 std::stringstream sstr{};
1023 sstr<<"Segment chi2/ndof = "<<seg->chi2()/std::max(1u,seg->nDoF())<<", hits in the segment "
1024 <<seg->measurements().size()<<std::endl;
1025 const Parameters pars = localSegmentPars(*gctx, *seg);
1026 sstr<<"Segment parameters : "<<toString(pars)<<std::endl;
1027
1028 for(const auto& hit : seg->measurements()){
1029 bool hasTruth{false};
1030 if(hit->type()!=xAOD::UncalibMeasType::Other){
1031 hasTruth = getTruthMatchedHit(*hit->spacePoint()->primaryMeasurement()) !=nullptr;
1032
1033 sstr<<" *** Hit "<<m_idHelperSvc->toString(hit->spacePoint()->identify())<<", "
1034 << Amg::toString(hit->spacePoint()->localPosition())<<", dir: "
1035 <<Amg::toString(hit->spacePoint()->sensorDirection())<<", has truth matched: "<<hasTruth<<std::endl;
1036 }
1037 }
1038 ATH_MSG_VERBOSE(sstr.str());
1039 }
1040
1041
1042 if (m_visionTool.isEnabled()) {
1043 m_visionTool->visualizeSegment(ctx, *seg, "#phi-segment");
1044 }
1045
1046 if(m_dumpObj){
1047 Acts::ObjVisualization3D visualHelper{};
1048 MuonValR4::drawSegmentMeasurements(gctx->context(), *seg, visualHelper);
1049 MuonValR4::drawSegmentLine(gctx->context(), *seg, visualHelper);
1050 visualHelper.write(std::format("Event_{:}_segment_{:}.obj", ctx.eventID().event_number(), seg->msSector()->identString()));
1051 }
1052
1053 writeSegments->push_back(std::move(seg));
1054
1055 }
1056
1057
1058 }
1059
1060 return StatusCode::SUCCESS;
1061}
1062
1064 if(m_seedCounter) {
1065 m_seedCounter->printTableSeedStats(msgStream());
1066 }
1067 return StatusCode::SUCCESS;
1068}
1069
1070void NswSegmentFinderAlg::SeedStatistics::addToStat(const MuonGMR4::SpectrometerSector* msSector, unsigned seeds, unsigned extSeeds, unsigned segments){
1071 std::unique_lock guard{m_mutex};
1072 SectorField key{};
1073 key.chIdx = msSector->chamberIndex();
1074 key.phi = msSector->stationPhi();
1075 key.eta = msSector->chambers().front()->stationEta();
1076
1077 auto &entry = m_seedStat[key];
1078 entry.nSeeds += seeds;
1079 entry.nExtSeeds += extSeeds;
1080 entry.nSegments += segments;
1081}
1082
1084
1085
1086 std::stringstream sstr{};
1087 sstr<<"Seed statistics per sector:"<<std::endl;
1088 sstr<<"-----------------------------------------------------"<<std::endl;
1089 sstr<<"| Chamber | Phi | Eta | Seeds | ExtSeeds | FittedSegments |"<<std::endl;
1090 sstr<<"-----------------------------------------------------"<<std::endl;
1091
1092 using namespace Muon::MuonStationIndex;
1093
1094 for (const auto& [sector, stats] : m_seedStat) {
1095 sstr << "| " << std::setw(3) << chName(sector.chIdx)
1096 << " | " << std::setw(2) << static_cast<unsigned>(sector.phi)
1097 << " | " << std::setw(3) << static_cast<int>(sector.eta)
1098 << " | " << std::setw(7) << stats.nSeeds
1099 << " | " << std::setw(8) << stats.nExtSeeds
1100 << " | " << std::setw(8) << stats.nSegments
1101 << " |"<<std::endl;
1102 }
1103
1104 sstr<<"------------------------------------------------------------"<<std::endl;
1105 msg<<MSG::ALWAYS<<"\n"<<sstr.str()<<endmsg;
1106 }
1107
1108
1109} // namespace MuonR4
const std::regex re(r_e)
Scalar mag() const
mag method
#define endmsg
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
#define AmgSymMatrix(dim)
ATLAS-specific HepMC functions.
static Double_t sp
static Double_t P(Double_t *tt, Double_t *par)
#define TEST_HIT_CORRIDOR(LAYER, HIT_ITER, START_LAYER)
Macro to check whether a hit is compatible with the hit corridor.
size_t size() const
Number of registered mappings.
#define x
#define max(a, b)
Definition cfImp.cxx:41
Acts::GeometryContext context() const
const ServiceHandle< StoreGateSvc > & detStore() const
bool msgLvl(const MSG::Level lvl) const
const StripLayer & stripLayer(const Identifier &measId) const
int multilayer() const
Returns the multi layer of the element [1-2].
MuonReadoutElement is an abstract class representing the geometry of a muon detector.
const SpectrometerSector * msSector() const
Returns the pointer to the envelope volume enclosing all chambers in the sector.
const Amg::Transform3D & localToGlobalTransform(const ActsTrk::GeometryContext &ctx) const override final
Returns the transformation from the local coordinate system of the readout element into the global AT...
A spectrometer sector forms the envelope of all chambers that are placed in the same MS sector & laye...
const Amg::Transform3D & localToGlobalTransform(const ActsTrk::GeometryContext &gctx) const
Returns the local -> global tarnsformation from the sector.
Amg::Transform3D globalToLocalTransform(const ActsTrk::GeometryContext &gctx) const
Returns the global -> local transformation from the ATLAS global.
const ChamberSet & chambers() const
Returns the associated chambers with this sector.
int stationPhi() const
: Returns the station phi of the sector
Muon::MuonStationIndex::ChIndex chamberIndex() const
Returns the chamber index scheme.
const StripDesign & design(bool phiView=false) const
Returns the underlying strip design.
Data class to represent an eta maximum in hough space.
std::vector< CalibSpacePointPtr > CalibSpacePointVec
void addToStat(const MuonGMR4::SpectrometerSector *msSector, unsigned int nSeeds, unsigned int nExtSeeds, unsigned int nSegments)
const MuonGMR4::MuonDetectorManager * m_detMgr
UnsignedIntegerProperty m_maxUsed
std::pair< SegmentSeedVec_t, SegmentVec_t > findSegmentsFromMaximum(const HoughMaximum &max, const ActsTrk::GeometryContext &gctx, const EventContext &ctx) const
Find seed and segment from an eta hough maximum.
void resolveAmbiguities(const ActsTrk::GeometryContext &gctx, SegmentVec_t &segmentCandidates) const
Resolve the ambiguities of the segments per chamber and return the surviving segments.
std::unique_ptr< SegmentSeed > constructCombinatorialSeed(const InitialSeed_t &initialSeed, const AmgSymMatrix(2)&bMatrix, const HoughMaximum &max, const HitLaySpan_t &extensionLayers, const UsedHitSpan_t &usedHits) const
Construct a combinatorial seed from the initial 4-layer seed hits.
std::unique_ptr< SegmentFit::SegmentLineFitter > m_lineFitter
std::pair< SegmentSeedVec_t, SegmentVec_t > buildSegmentsFromMM(const EventContext &ctx, const ActsTrk::GeometryContext &gctx, const HitLayVec &hitLayers, const HoughMaximum &max, const Amg::Vector3D &beamSpotPos, UsedHitMarker_t &usedHits, bool useOnlyMM) const
Build the final segment seed from strip like measurements using the combinatorial seeding for MicroMe...
UnsignedIntegerProperty m_maxClustersInLayer
virtual StatusCode initialize() override
virtual StatusCode execute(const EventContext &ctx) const override
std::pair< SegmentSeedVec_t, SegmentVec_t > buildSegmentsFromSTGC(const EventContext &ctx, const ActsTrk::GeometryContext &gctx, const HitLayVec &hitLayers, const HoughMaximum &max, const Amg::Vector3D &beamSpotPos, UsedHitMarker_t &usedHits) const
Build the segment for a seed from STGC 2D measurement layers directly and then attempt to append hits...
HitWindow
To fastly check whether a hit is roughly compatible with a muon trajectory a narrow corridor is opene...
@ inside
The hit is below the predefined corridor.
@ tooHigh
The hit is inside the defined window and hence an initial candidate.
std::unique_ptr< Segment > fitSegmentSeed(const EventContext &ctx, const ActsTrk::GeometryContext &gctx, const SegmentSeed *patternSeed) const
Fit the segment seeds.
ToolHandle< MuonValR4::IPatternVisualizationTool > m_visionTool
Pattern visualization tool.
std::vector< std::reference_wrapper< const HitVec > > HitLaySpan_t
Abbrivation of the space comprising multiple hit vectors without copy.
std::array< const SpacePoint *, 4 > InitialSeed_t
Abbrivation of the initial seed.
SG::WriteHandleKey< SegmentSeedContainer > m_writeSegmentSeedKey
HitWindow hitFromIPCorridor(const SpacePoint &testHit, const Amg::Vector3D &beamSpotPos, const Amg::Vector3D &dirEstUp, const Amg::Vector3D &dirEstDn) const
The hit is above the predefined corridor.
std::vector< std::unique_ptr< SegmentSeed > > SegmentSeedVec_t
Abbrivation of the seed vector.
void constructPreliminarySeeds(const Amg::Vector3D &beamSpot, const HitLaySpan_t &combinatoricLayers, const UsedHitSpan_t &usedHits, InitialSeedVec_t &outVec) const
Construct a set of prelimnary seeds from the selected combinatoric layers.
void processSegment(std::unique_ptr< Segment > segment, const HitVec &seedHits, const HitLayVec &hitLayers, UsedHitMarker_t &usedHits, SegmentVec_t &segments) const
Process the segment and mark the hits if it is successfully built or not by differently mark the hits...
void markHitsAsUsed(const HitVec &spacePoints, const HitLayVec &allSortHits, UsedHitMarker_t &usedHitMarker, unsigned int increase, bool markNeighborHits) const
Hits that are used in a good seed/segment built should be flagged as used and not contribute to other...
UsedHitMarker_t emptyBookKeeper(const HitLayVec &sortedSp) const
Constructs an empty HitMarker from the split space points.
virtual StatusCode finalize() override
ServiceHandle< Muon::IMuonIdHelperSvc > m_idHelperSvc
SpacePointPerLayerSplitter::HitLayVec HitLayVec
SpacePointPerLayerSplitter::HitVec HitVec
std::vector< InitialSeed_t > InitialSeedVec_t
Vector of initial seeds.
StripOrient classifyStrip(const SpacePoint &spacePoint) const
Determines the orientation of the strip space point.
StripOrient
Enumeration to classify the orientation of a NSW strip.
@ X
Stereo strips with negative angle.
@ V
Stereo strips with positive angle.
@ Unknown
Combined 2D space point (sTGC wire + strip / sTgc pad).
std::vector< std::unique_ptr< Segment > > SegmentVec_t
Abbrivation of the final segment vector.
SG::WriteHandleKey< SegmentContainer > m_writeSegmentKey
ActsTrk::GeoContextReadKey_t m_geoCtxKey
std::vector< std::vector< unsigned int > > UsedHitMarker_t
Abbrivation of the container book keeping whether a hit is used or not.
ToolHandle< ISpacePointCalibrator > m_calibTool
HitVec extendHits(const Amg::Vector3D &startPos, const Amg::Vector3D &direction, const HitLaySpan_t &extensionLayers, const UsedHitSpan_t &usedHits) const
Extend the seed with the hits from the other layers.
UnsignedIntegerProperty m_minSeedHits
SG::ReadHandleKey< EtaHoughMaxContainer > m_etaKey
std::unique_ptr< SegmentFit::SegmentAmbiSolver > m_ambiSolver
std::vector< std::reference_wrapper< std::vector< unsigned int > > > UsedHitSpan_t
Abbrivation of the container to pass a subset of markers wtihout copy.
Representation of a segment seed (a fully processed hough maximum) produced by the hough transform.
Definition SegmentSeed.h:14
const std::vector< HitType > & getHitsInMax() const
Returns the list of assigned hits.
const Parameters & parameters() const
Returns the parameter array.
Amg::Vector3D localDirection() const
Returns the direction of the seed in the sector frame.
const MuonGMR4::SpectrometerSector * msSector() const
Returns the associated chamber.
Amg::Vector3D localPosition() const
Returns the position of the seed in the sector frame.
The SpacePointPerLayerSorter sort two given space points by their layer Identifier.
unsigned int sectorLayerNum(const SpacePoint &sp) const
method returning the logic layer number
The SpacePointPerLayerSplitter takes a set of spacepoints already sorted by layer Identifier (see Muo...
const HitLayVec & stripHits() const
Returns the sorted strip hits.
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
StatusCode record(std::unique_ptr< T > data)
Record a const object to the store.
ConstVectorMap< 3 > localDirection() const
Returns the local direction of the traversing particle.
Identifier identify() const
Returns the global ATLAS identifier of the SimHit.
ConstVectorMap< 3 > localPosition() const
Returns the local postion of the traversing particle.
T * get(TKey *tobj)
get a TObject* from a TKey* (why can't a TObject be a TKey?)
Definition hcg.cxx:132
struct color C
std::optional< double > intersect(const AmgVector(N)&posA, const AmgVector(N)&dirA, const AmgVector(N)&posB, const AmgVector(N)&dirB)
Calculates the point B' along the line B that's closest to a second line A.
std::string toString(const Translation3D &translation, int precision=4)
GeoPrimitvesToStringConverter.
Amg::Vector3D dirFromAngles(const double phi, const double theta)
Constructs a direction vector from the azimuthal & polar angles.
Eigen::Affine3d Transform3D
Eigen::Matrix< double, 2, 1 > Vector2D
Eigen::Matrix< double, 3, 1 > Vector3D
Parameters localSegmentPars(const xAOD::MuonSegment &seg)
Returns the localSegPars decoration from a xAODMuon::Segment.
Acts::Experimental::CompositeSpacePointLineFitter::ParamVec_t Parameters
std::string toString(const Parameters &pars)
Dumps the parameters into a string with labels in front of each number.
This header ties the generic definitions in this package.
ISpacePointCalibrator::CalibSpacePointVec CalibSpacePointVec
double houghTanBeta(const Amg::Vector3D &v)
Returns the hough tanBeta [y] / [z].
DataVector< HoughMaximum > EtaHoughMaxContainer
const xAOD::MuonSimHit * getTruthMatchedHit(const xAOD::MuonMeasurement &prdHit)
Returns the MuonSimHit, if there's any, matched to the uncalibrated muon measurement.
constexpr unsigned minLayers
bool isPrecisionHit(const SpacePoint &hit)
Returns whether the uncalibrated spacepoint is a precision hit (Mdt, micromegas, stgc strips).
SpacePointPerLayerSplitter::HitVec HitVec
std::string print(const cont_t &container)
Print a space point container to string.
double houghTanAlpha(const Amg::Vector3D &v)
: Returns the hough tanAlpha [x] / [z]
void drawSegmentMeasurements(const Acts::GeometryContext &tgContext, const xAOD::MuonSegment &segment, Acts::ObjVisualization3D &visualHelper, const Acts::ViewConfig &viewConfig=Acts::s_viewSensitive)
Draw all uncalibrated measurements associated to the segment.
std::unique_ptr< TLatex > drawLabel(const std::string &text, const double xPos, const double yPos, const double textSize=18, const bool useNDC=true, const int color=kBlack)
Create a TLatex label,.
void drawSegmentLine(const Acts::GeometryContext &tgContext, const xAOD::MuonSegment &segment, Acts::ObjVisualization3D &visualHelper, const Acts::ViewConfig &viewConfig=Acts::s_viewLine, const double standardLength=1.*Gaudi::Units::m)
Draw a segment line inside the obj file.
void drawSpacePoint(const Acts::GeometryContext &tgContext, const MuonR4::SpacePoint &spacePoint, Acts::ObjVisualization3D &visualHelper, const Acts::ViewConfig &viewConfig=Acts::s_viewSensitive)
Draw an uncalibrated space point inside the obj file.
const std::string & chName(ChIndex index)
convert ChIndex into a string
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
MuonSimHit_v1 MuonSimHit
Defined the version of the MuonSimHit.
Definition MuonSimHit.h:12
MMCluster_v1 MMCluster
sTgcMeasurement_v1 sTgcMeasurement
Configuration object to stree the ambiguties.
const ISpacePointCalibrator * calibrator
Pointer to the calibrator.
const Muon::IMuonIdHelperSvc * idHelperSvc
Pointer to the idHelperSvc.
const MuonValR4::IPatternVisualizationTool * visionTool
Pointer to the visualization tool.
#define THROW_EXCEPTION(MESSAGE)
Definition throwExcept.h:10