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();
104 fitCfg.parsToUse = {ParamDefs::x0, ParamDefs::y0, ParamDefs::theta, ParamDefs::phi};
105
106 m_lineFitter = std::make_unique<SegmentFit::SegmentLineFitter>(name(), std::move(fitCfg));
107
109 m_ambiSolver = std::make_unique<SegmentAmbiSolver>(name(), std::move(ambicfg));
110
111
113 m_seedCounter = std::make_unique<SeedStatistics>();
114 }
115
116 return StatusCode::SUCCESS;
117}
118
121 UsedHitMarker_t emptyKeeper(sortedSp.size());
122 for (std::size_t l = 0; l < sortedSp.size(); ++l) {
123 emptyKeeper[l].resize(sortedSp[l].size(), 0);
124 }
125 return emptyKeeper;
126}
127
130
132 const auto& design = getDesign(sp);
133 if (!design.hasStereoAngle()) {
134 return StripOrient::X;
135 }
136 return design.stereoAngle() > 0. ? StripOrient::U : StripOrient::V;
137 } else if (sp.type() == xAOD::UncalibMeasType::sTgcStripType) {
138 const auto* prd = static_cast<const xAOD::sTgcMeasurement*>(sp.primaryMeasurement());
139 if (sp.dimension() == 2) {
140 return StripOrient::C;
141 }
142 //check if we have strip only or wire only measurements
143 return prd->channelType() == sTgcIdHelper::Strip ? StripOrient::X : StripOrient::P;
144
145 }
146 ATH_MSG_WARNING("Cannot classify orientation of "<<m_idHelperSvc->toString(sp.identify()));
148}
151 const Amg::Vector3D& beamSpotPos,
152 const Amg::Vector3D& dirEstUp,
153 const Amg::Vector3D& dirEstDn) const{
154
155 const Amg::Vector3D estPlaneArrivalUp = SeedingAux::extrapolateToPlane(beamSpotPos, dirEstUp, testHit);
156 const Amg::Vector3D estPlaneArrivalDn = SeedingAux::extrapolateToPlane(beamSpotPos, dirEstDn, testHit);
157
158 bool below{true}, above{true};
159 switch (classifyStrip(testHit)) {
160 using enum StripOrient;
161 case U:
162 case V:{
163 const double halfLength = 0.5* stripHalfLength(testHit);
165 const Amg::Vector3D leftEdge = testHit.localPosition() - halfLength * testHit.sensorDirection();
166 const Amg::Vector3D rightEdge = testHit.localPosition() + halfLength * testHit.sensorDirection();
167
169 below = estPlaneArrivalDn.y() > std::max(leftEdge.y(), rightEdge.y());
171 above = estPlaneArrivalUp.y() < std::min(leftEdge.y(), rightEdge.y());
172 break;
173 } case X:
174 case C: {
176 const double hY = testHit.localPosition().y();
177 below = estPlaneArrivalDn.y() > hY;
179 above = estPlaneArrivalUp.y() < hY;
180 break;
181 }
182 case P:{
183 break;
184 }
185 case Unknown:{
186 break;
187 }
188
189 }
190 ATH_MSG_VERBOSE("Hit " << m_idHelperSvc->toString(testHit.identify())
191 << (below || above ? " is outside the window" : " is inside the window"));
192 if(below) {
193 return HitWindow::tooLow;
194 }
195 if(above) {
196 return HitWindow::tooHigh;
197 }
198 return HitWindow::inside;
199};
200
202#define TEST_HIT_CORRIDOR(LAYER, HIT_ITER, START_LAYER) \
203{ \
204 const SpacePoint* testMe = combinatoricLayers[LAYER].get()[HIT_ITER]; \
205 if (usedHits[LAYER].get()[HIT_ITER] > m_maxUsed) { \
206 ATH_MSG_VERBOSE(__func__<<":"<<__LINE__<<" - " \
207 <<m_idHelperSvc->toString(testMe->identify()) \
208 <<" already used in good seed." ); \
209 continue; \
210 } \
211 const HitWindow inWindow = hitFromIPCorridor(*testMe, beamSpot, dirEstUp, dirEstDn); \
212 if(inWindow == HitWindow::tooHigh) { \
213 ATH_MSG_VERBOSE(__func__<<":"<<__LINE__<<" - Hit " \
214 <<m_idHelperSvc->toString(testMe->identify()) \
215 <<" is beyond the corridor. Break loop"); \
216 break; \
217 } else if (inWindow == HitWindow::tooLow) { \
218 START_LAYER = HIT_ITER + 1; \
219 ATH_MSG_VERBOSE(__func__<<":"<<__LINE__<<" - Hit " \
220 <<m_idHelperSvc->toString(testMe->identify()) \
221 <<" is still below the corridor. Update start to " \
222 <<START_LAYER); \
223 continue; \
224 } \
225}
226
228 const HitLaySpan_t& combinatoricLayers,
229 const UsedHitSpan_t& usedHits,
230 InitialSeedVec_t& seedHitsFromLayers) const {
232 seedHitsFromLayers.clear();
233 std::size_t maxSize{1};
234 for (const HitVec& hitVec : combinatoricLayers) {
235 maxSize = maxSize * hitVec.size();
236 }
237 seedHitsFromLayers.reserve(maxSize);
238
239 unsigned iterLay0{0}, iterLay1{0}, iterLay2{0}, iterLay3{0};
240 unsigned startLay1{0}, startLay2{0}, startLay3{0};
241
242 for( ; iterLay0 < combinatoricLayers[0].get().size() ; ++iterLay0){
244 if (usedHits[0].get()[iterLay0] > m_maxUsed) {
245 continue;
246 }
247 const SpacePoint* hit0 = combinatoricLayers[0].get()[iterLay0];
249 const Amg::Vector3D initSeedDir{(beamSpot - hit0->localPosition()).unit()};
250 const Amg::Vector3D dirEstUp = Amg::dirFromAngles(initSeedDir.phi(), initSeedDir.theta() - m_windowTheta);
251 const Amg::Vector3D dirEstDn = Amg::dirFromAngles(initSeedDir.phi(), initSeedDir.theta() + m_windowTheta);
252
253 ATH_MSG_VERBOSE("Reference hit: "<<m_idHelperSvc->toString(hit0->identify())
254 <<", position: "<<Amg::toString(hit0->localPosition())
255 <<", seed dir: "<<Amg::toString(initSeedDir)
256 <<", seed plane: "<<Amg::toString(SeedingAux::extrapolateToPlane(beamSpot, initSeedDir, *hit0)));
258 for( iterLay1 = startLay1; iterLay1 < combinatoricLayers[1].get().size() ; ++iterLay1){
259 TEST_HIT_CORRIDOR(1, iterLay1, startLay1);
260 for( iterLay2 = startLay2; iterLay2 < combinatoricLayers[2].get().size() ; ++iterLay2){
261 TEST_HIT_CORRIDOR(2, iterLay2, startLay2);
262 for( iterLay3 = startLay3; iterLay3 < combinatoricLayers[3].get().size(); ++iterLay3){
263 TEST_HIT_CORRIDOR(3, iterLay3, startLay3);
264 seedHitsFromLayers.emplace_back(std::array{hit0, combinatoricLayers[1].get()[iterLay1],
265 combinatoricLayers[2].get()[iterLay2],
266 combinatoricLayers[3].get()[iterLay3]});
267 }
268 }
269 }
270 }
271}
272#undef TEST_HIT_CORRIDOR
273
276 const Amg::Vector3D& direction,
277 const HitLaySpan_t& extensionLayers,
278 const UsedHitSpan_t& usedHits) const {
279
280 //the hits we need to return to extend the segment seed
281 HitVec combinatoricHits;
282
283 for (std::size_t i = 0; i < extensionLayers.size(); ++i) {
284 const HitVec& layer{extensionLayers[i].get()};
285 const Amg::Vector3D extrapPos = SeedingAux::extrapolateToPlane(startPos, direction, *layer.front());
286
287
288 unsigned triedHit{0};
289 HitCandidate precisionHit, noPrecisionHit;
290 ATH_MSG_VERBOSE("Moving to next layer");
291
292 for (unsigned j = 0; j < layer.size(); ++j) {
293 if (usedHits[i].get().at(j) > m_maxUsed) {
294 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Hit " << (*layer[j])<< " already used with counter " << usedHits[i].get().at(j) << ". Skip.");
295 continue;
296 }
297 auto hit = layer.at(j);
298 const double pull = std::sqrt(SeedingAux::chi2Term(extrapPos, direction, *hit));
299 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Trying extension with hit " << *hit<<" and pull "<<pull<<" has truth: "<< (getTruthMatchedHit(*hit->primaryMeasurement()) != nullptr ? "oui" : "non"));
300 bool isPrecision = isPrecisionHit(*hit);
301 double minPull = isPrecision ? precisionHit.minPull : noPrecisionHit.minPull;
302 ATH_MSG_VERBOSE("Current min pull for this layer is: "<<minPull);
303 //find the hit with the minimum pull (check at least three hits after we have increasing pulls)
304 if (pull > minPull) {
305 triedHit+=1;
306 continue;
307 }
308
309 if(triedHit>3){
310 break;
311 }
312
313 if(isPrecision){
314 precisionHit.spacePoint = hit;
315 precisionHit.minPull = pull;
316 continue;
317 }
318
319 noPrecisionHit.spacePoint = hit;
320 noPrecisionHit.minPull = pull;
321 }
322
323 // complete the seed with the extended hits
324 //we first choose the precision hit and if does not exist then we pick the non precision hit
325 const SpacePoint* bestCand{nullptr};
326 if(precisionHit.minPull < m_minPullThreshold){
327 bestCand = precisionHit.spacePoint;
328 }else if(noPrecisionHit.minPull < m_minPullThreshold){
329 bestCand = noPrecisionHit.spacePoint;
330 }else{
331 ATH_MSG_VERBOSE("No hit found in layer "<<i<<" with pull below threshold "<<m_minPullThreshold);
332 continue;
333 }
334 ATH_MSG_VERBOSE("Extension successfull - hit" << m_idHelperSvc->toString(bestCand->identify())
335 <<", pos: "<<Amg::toString(bestCand->localPosition())
336 <<", dir: "<<Amg::toString(bestCand->sensorDirection()));
337 combinatoricHits.push_back(bestCand);
338 }
339
340 return combinatoricHits;
341}
342
343std::unique_ptr<SegmentSeed>
345 const AmgSymMatrix(2)& bMatrix,
346 const HoughMaximum& max,
347 const HitLaySpan_t& extensionLayers,
348 const UsedHitSpan_t& usedHits) const {
349 bool allValid = std::any_of(initialSeed.begin(), initialSeed.end(),
350 [this](const auto& hit){
351 if (hit->type() == xAOD::UncalibMeasType::MMClusterType) {
352 const auto* mmClust = static_cast<const xAOD::MMCluster*>(hit->primaryMeasurement());
353 return mmClust->stripNumbers().size() >= m_minClusSize;
354 }
355 return true;
356 });
357
358 if (!allValid) {
359 ATH_MSG_VERBOSE("Seed rejection: Not all clusters meet minimum strip size");
360 return nullptr;
361 }
362
363
364 std::array<double, 4> params = defineParameters(bMatrix, initialSeed);
365
366 const auto [segPos, direction] = seedSolution(initialSeed, params);
367
368 // check the consistency of the parameters - expected to lay in the strip's
369 // length
370 for (std::size_t i = 0; i < 4; ++i) {
371 const double halfLength = stripHalfLength(*initialSeed[i]);
372
373 if (std::abs(params[i]) > halfLength) {
374 ATH_MSG_VERBOSE("Seed Rejection: Invalid seed - outside of the strip's length "<< m_idHelperSvc->toString(initialSeed[i]->identify())
375 <<", param: "<<params[i]<<", halfLength: "<<halfLength);
376 return nullptr;
377 }
378 }
379 double tanAlpha = houghTanAlpha(direction);
380 double tanBeta = houghTanBeta(direction);
381
382 double interceptX = segPos.x();
383 double interceptY = segPos.y();
384
385 //seed quality check - we expect the tanAlpha not to be too big which would mean big deflection along the strip layers
386 if(std::abs(tanAlpha) > m_maxTanAlpha){
387 ATH_MSG_VERBOSE("Seed Rejection: Invalid seed - tanAlpha "<<tanAlpha<<" above threshold "<<m_maxTanAlpha);
388 return nullptr;
389 }
390
391
392 // extend the seed to the segment -- include hits from the other layers too
393 auto extendedHits = extendHits(segPos, direction, extensionLayers, usedHits);
394 HitVec hits{initialSeed.begin(),initialSeed.end()};
395 std::ranges::move(extendedHits, std::back_inserter(hits));
396
397 return std::make_unique<SegmentSeed>(tanBeta, interceptY, tanAlpha,
398 interceptX, hits.size(),
399 std::move(hits), max.parentBucket());
400}
401
402
403std::unique_ptr<Segment> NswSegmentFinderAlg::fitSegmentSeed(const EventContext& ctx,
404 const ActsTrk::GeometryContext& gctx,
405 const SegmentSeed* patternSeed) const{
406
407 if(patternSeed->getHitsInMax().size() < m_minSeedHits){
408 ATH_MSG_VERBOSE("Not enough hits in the SegmentSeed to fit a segment");
409 return nullptr;
410 }
411
412 ATH_MSG_VERBOSE("Fit the SegmentSeed");
413 if (msgLvl(MSG::VERBOSE)) {
414 std::stringstream hitStream{};
415 for (const auto& hit : patternSeed->getHitsInMax()) {
416 hitStream<<"**** "<< (*hit)<<std::endl;
417 }
418 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__ <<": Uncalibrated space points for the segment fit: "<<std::endl
419 <<hitStream.str());
420 }
421
422 //Calibration of the seed spacepoints
423 CalibSpacePointVec calibratedHits = m_calibTool->calibrate(ctx, patternSeed->getHitsInMax(),
424 patternSeed->localPosition(),
425 patternSeed->localDirection(), 0.);
426
427 const Amg::Transform3D& locToGlob{patternSeed->msSector()->localToGlobalTransform(gctx)};
428
429 return m_lineFitter->fitSegment(ctx, patternSeed, patternSeed->parameters(),
430 locToGlob, std::move(calibratedHits));
431}
432
433void NswSegmentFinderAlg::processSegment(std::unique_ptr<Segment> segment,
434 const HitVec& seedHits,
435 const HitLayVec& hitLayers,
436 UsedHitMarker_t& usedHits,
437 SegmentVec_t& segments) const {
438
439 if (!segment) {
440 ATH_MSG_VERBOSE("Seed Rejection: Segment fit failed");
441
442 if (m_markHitsFromSeed && seedHits.size() > m_minSeedHits) {
443 // Mark hits from extended seed (used increment by 1)
444 markHitsAsUsed(seedHits, hitLayers, usedHits, 1, false);
445 }
446 return;
447 }
448
449 // -------- success path --------
450 ATH_MSG_DEBUG("Segment built with "
451 << segment->measurements().size()
452 << " hits, chi2/ndof: "
453 << segment->chi2() / std::max(1u,segment->nDoF()));
454
455 HitVec segMeasSP;
456 segMeasSP.reserve(segment->measurements().size());
457
458 std::ranges::transform(
459 segment->measurements(),
460 std::back_inserter(segMeasSP),
461 [](const auto& m) { return m->spacePoint(); }
462 );
463
464 // Mark segment hits as fully used (used increment by 10,
465 // hits are effectively removed)
466 markHitsAsUsed(segMeasSP, hitLayers, usedHits, 10, true);
467 segments.push_back(std::move(segment));
468
469}
470
472 SegmentVec_t& segmentCandidates) const{
473
474 if(segmentCandidates.size()<=1){
475 ATH_MSG_VERBOSE("No segments to resolve ambiguities");
476 return;
477 }
478
479 ATH_MSG_VERBOSE("Resolving ambiguities for "<<segmentCandidates.size()<<" segments");
480
483
484 //sort segments per chamber and resolve ambiguities per chamber
485 for (std::unique_ptr<Segment>& seg : segmentCandidates) {
486 const MuonGMR4::SpectrometerSector* chamb = seg->msSector();
487 segmentsPerChamber[chamb].push_back(std::move(seg));
488 }
489 segmentCandidates.clear();
490 for (auto& [chamber, resolveMe] : segmentsPerChamber) {
491 SegmentVec_t resolvedSegments = m_ambiSolver->resolveAmbiguity(gctx, std::move(resolveMe));
492 segmentCandidates.insert(segmentCandidates.end(),
493 std::make_move_iterator(resolvedSegments.begin()),
494 std::make_move_iterator(resolvedSegments.end()));
495 }
496
497}
498
499std::pair<NswSegmentFinderAlg::SegmentSeedVec_t, NswSegmentFinderAlg::SegmentVec_t>
501 const ActsTrk::GeometryContext &gctx,
502 const HitLayVec& hitLayers,
503 const HoughMaximum& max,
504 const Amg::Vector3D& beamSpotPos,
505 UsedHitMarker_t& usedHits) const {
506
507 //go through the layers and build seeds from the combinations of hits
508 //starting from the outermost layers with 2D measurements (excluding pads)
509 SegmentSeedVec_t seeds{};
510 SegmentVec_t segments{};
511 std::size_t layerSize = hitLayers.size();
512 double thetaWindowCut{std::cos(2*m_windowTheta)};
513
514 // lamda helper to check if the spacepoint is combined (but not pad) and unused in an already constructed seed
515 auto isUnusedCombined = [&](std::size_t layIdx, std::size_t hitIdx) -> bool {
516 const SpacePoint* sp = hitLayers[layIdx][hitIdx];
518 THROW_EXCEPTION("Space point is not of sTgc type: "<<sp->msSector()->idHelperSvc()->toString(sp->identify()));
519 }
520 if(sp->dimension() != 2){
521 ATH_MSG_VERBOSE("Ignoring the 1D measurement for seeding: "<<m_idHelperSvc->toString(sp->identify()));
522 return false;
523 }
524 const auto* prdPrim = static_cast<const xAOD::sTgcMeasurement*>(sp->primaryMeasurement());
525 const auto* prdSec = static_cast<const xAOD::sTgcMeasurement*>(sp->secondaryMeasurement());
526 //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
527 bool hasPad = prdSec ? prdSec->channelType()==sTgcIdHelper::sTgcChannelTypes::Pad : prdPrim->channelType()==sTgcIdHelper::sTgcChannelTypes::Pad;
528 if(hasPad){
529 ATH_MSG_VERBOSE("Ignoring the 2D pad measurement for seeding because of pads: "<<m_idHelperSvc->toString(sp->identify()));
530 return false;
531 }
532 bool isPrecision = isPrecisionHit(*sp);
533 return isPrecision && usedHits[layIdx].at(hitIdx) <= m_maxUsed;
534
535 };
536
537 // find the 2D measurements from the outermost layers - even move one layer inside
538 for(std::size_t layIdx1 = 0; layIdx1 < 2; ++layIdx1){
539 for(std::size_t layIdx2 = layerSize-1; layIdx2 >= layerSize-2; --layIdx2){
540 //in case of MM layers we stop - the layers are sorted in Z
541 if(hitLayers[layIdx1].front()->type() == xAOD::UncalibMeasType::MMClusterType ||
542 hitLayers[layIdx2].front()->type() == xAOD::UncalibMeasType::MMClusterType){
543 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<"Outermost layers are MM - stop searching for sTgc Measurements");
544 return std::make_pair(std::move(seeds), std::move(segments));
545 }
546
547 //check if we have 2D measurements on these layers that are unused (excluding the pads)
548 for(std::size_t hitIdx1 = 0; hitIdx1 < hitLayers[layIdx1].size(); ++hitIdx1) {
549 const SpacePoint* hit1 = hitLayers[layIdx1][hitIdx1];
550 if(!isUnusedCombined(layIdx1, hitIdx1)){
551 continue;
552 }
553 for(std::size_t hitIdx2 = 0; hitIdx2 < hitLayers[layIdx2].size(); ++hitIdx2) {
554 const SpacePoint* hit2 = hitLayers[layIdx2][hitIdx2];
555 if(!isUnusedCombined(layIdx2, hitIdx2)){
556 continue;
557 }
558 //test if this selection of the hits from the two layers is aligned with the beam spot
559 const Amg::Vector3D beamSpotHitDir{(beamSpotPos - hit1->localPosition()).unit()};
560 const Amg::Vector3D seedDir{(hit2->localPosition() - hit1->localPosition()).unit()};
561 const double cosAngle = beamSpotHitDir.dot(seedDir);
562 //accept the deflection of direction with a tolerance of 1 deg
563 if(std::abs(cosAngle) < thetaWindowCut){
564 continue;
565 }
566 //found 2D hits on the outermost layers - build a seed
567 HitVec seedHits{hit1, hit2};
568 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<"Attempt for STGC segment starting with the hits: " << *hit1 << ", " << *hit2);
569 //get the seed direction and position from the two 2D hits
570 const Amg::Vector3D seedPos = hit1->localPosition();
571 //express position in z=0
572 const Amg::Vector3D seedPosZ0 = seedPos + Amg::intersect<3>(seedPos, seedDir, Amg::Vector3D::UnitZ(), 0.).value_or(0.)*seedDir;
573 // extend the seed to the other layers
574 HitLaySpan_t extensionLayers{};
575 UsedHitSpan_t usedExtensionHits{};
576 extensionLayers.reserve(hitLayers.size());
577 usedExtensionHits.reserve(hitLayers.size());
578 for (std::size_t e = 0 ; e < hitLayers.size(); ++e) {
579 if (!(e == layIdx1 || e == layIdx2)){
580 extensionLayers.emplace_back(hitLayers[e]);
581 usedExtensionHits.emplace_back(usedHits[e]);
582 }
583 }
584 auto extendedHits = extendHits(seedPosZ0, seedDir, extensionLayers, usedExtensionHits);
585 std::ranges::move(extendedHits, std::back_inserter(seedHits));
586
587 //make seed
588 auto seed = std::make_unique<SegmentSeed>(houghTanBeta(seedDir), seedPosZ0.y(),
589 houghTanAlpha(seedDir), seedPosZ0.x(),
590 seedHits.size(), std::move(seedHits),
591 max.parentBucket());
592
593 //skip segment fit with less than 5 hits
594 if(seed->getHitsInMax().size() < m_minSeedHits){
595 seeds.push_back(std::move(seed));
596 continue;
597 }
598 //fit the segment seed
599 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Start to fit an STGC segment seed with "<<seed->getHitsInMax().size()<<" hits \n"<<print(seed->getHitsInMax()));
600 std::unique_ptr<Segment> segment = fitSegmentSeed(ctx, gctx, seed.get());
601 processSegment(std::move(segment), seed->getHitsInMax(), hitLayers, usedHits, segments);
602 seeds.push_back(std::move(seed));
603
604 }
605 }
606 }
607 }
608 return std::make_pair(std::move(seeds),std::move(segments));
609}
610
611std::pair<NswSegmentFinderAlg::SegmentSeedVec_t, NswSegmentFinderAlg::SegmentVec_t>
613 const ActsTrk::GeometryContext &gctx,
614 const HitLayVec& hitLayers,
615 const HoughMaximum& max,
616 const Amg::Vector3D& beamSpotPos,
617 UsedHitMarker_t& usedHits,
618 bool useOnlyMM) const {
619
620 //go through the layers and build seeds from the combinations of hits
621 SegmentSeedVec_t seeds{};
622 SegmentVec_t segments{};
623 std::size_t layerSize = hitLayers.size();
624
625 if(layerSize < minLayers){
626 ATH_MSG_VERBOSE("Not enough layers to build a seed");
627 return {std::move(seeds), std::move(segments)};
628 }
629
630 //lamda helper to find the first unused strip hit on the layer
631 auto unusedStripHit = [&](const HitVec& layerHits, unsigned int layIdx) -> const SpacePoint* {
632 //in case of MM only combinatorial seeding - we consider only MM strip hits for seeding
633 bool isMM = useOnlyMM ? layerHits.front()->type() == xAOD::UncalibMeasType::MMClusterType : true;
634 for (auto [idx, hit] : Acts::enumerate(layerHits)) {
635 auto spOrient = classifyStrip(*hit);
636 bool isStrip = spOrient == StripOrient::X || spOrient == StripOrient::U || spOrient == StripOrient::V;
637 bool isUnused = usedHits[layIdx].at(idx) <= m_maxUsed;
638 if (isStrip && isUnused && isMM) {
639 return hit;
640 }
641 }
642 return nullptr;
643 };
644
645 std::array<const SpacePoint*, 4> seedHits{};
646 InitialSeedVec_t preLimSeeds{};
647
648 for (std::size_t i = 0; i < layerSize - 3; ++i) {
649 seedHits[0] = unusedStripHit(hitLayers[i], i);
650 if(!seedHits[0]) {
651 continue;
652 }
653 for (std::size_t j = i + 1; j < layerSize - 2; ++j) {
654 seedHits[1] = unusedStripHit(hitLayers[j], j);
655 if(!seedHits[1]){
656 continue;
657 }
658 for (std::size_t l = layerSize - 1; l > j+1; --l) {
659 seedHits[3] = unusedStripHit(hitLayers[l], l);
660 if(!seedHits[3]){
661 continue;
662 }
663 for (std::size_t k = l-1; k > j ; --k) {
664 seedHits[2] = unusedStripHit(hitLayers[k], k);
665 if(!seedHits[2]){
666 continue;
667 }
668
669 const HitLaySpan_t layers{hitLayers[i], hitLayers[j], hitLayers[k], hitLayers[l]};
670 //skip combination with at least one too busy layer
671 bool tooBusy = std::ranges::any_of(layers,
672 [this](const auto& layer) {
673 return layer.get().size() > m_maxClustersInLayer;
674 });
675 if (tooBusy) {
676 continue; // skip this combination
677 }
678
679 AmgSymMatrix(2) bMatrix = betaMatrix(seedHits);
680
681 if (std::abs(bMatrix.determinant()) < 1.e-6) {
682 continue;
683 }
684 ATH_MSG_DEBUG("Space point positions for seed layers: \n"
685 <<(*seedHits[0]) << ",\n"
686 <<(*seedHits[1]) << ",\n"
687 <<(*seedHits[2]) << ",\n"
688 <<(*seedHits[3]));
689
690
691 UsedHitSpan_t usedHitsSpan{usedHits[i], usedHits[j], usedHits[k], usedHits[l]};
692 // each layer may have more than one hit - take the hit combinations
693 constructPreliminarySeeds(beamSpotPos, layers, usedHitsSpan, preLimSeeds);
694
695 //the layers not participated in the seed build - gonna be used for the extension
696 HitLaySpan_t extensionLayers{};
697 UsedHitSpan_t usedExtensionHits{};
698 usedExtensionHits.reserve(hitLayers.size());
699 extensionLayers.reserve(hitLayers.size());
700 for (std::size_t e = 0 ; e < hitLayers.size(); ++e) {
701 if (!(e == i || e == j || e == k || e == l)){
702 extensionLayers.emplace_back(hitLayers[e]);
703 usedExtensionHits.emplace_back(usedHits[e]);
704 }
705 }
706 // we have made sure to have hits from all the four layers -
707 // start by 4 hits for the seed and try to build the extended seed for the combinatorics found
708 for (auto &combinatoricHits : preLimSeeds) {
709 auto seed = constructCombinatorialSeed(combinatoricHits, bMatrix, max, extensionLayers, usedExtensionHits);
710 if(!seed){
711 continue;
712 }
713 if (seed->getHitsInMax().size() < m_minSeedHits) {
714 seeds.push_back(std::move(seed));
715 continue;
716 }
717 std::unique_ptr<Segment> segment = fitSegmentSeed(ctx, gctx, seed.get());
718 processSegment(std::move(segment), seed->getHitsInMax(), hitLayers, usedHits, segments);
719 seeds.push_back(std::move(seed));
720
721 }
722 }
723 }
724 }
725 }
726 return std::make_pair(std::move(seeds),std::move(segments));
727}
728
729std::pair<NswSegmentFinderAlg::SegmentSeedVec_t, NswSegmentFinderAlg::SegmentVec_t>
731 const ActsTrk::GeometryContext &gctx,
732 const EventContext& ctx) const {
733 // first sort the hits per layer from the maximum
734 SpacePointPerLayerSplitter hitLayers{max.getHitsInMax()};
735
736 const HitLayVec& stripHitsLayers{hitLayers.stripHits()};
737 const std::size_t layerSize = stripHitsLayers.size();
738
739 //seeds and segments containers
740 SegmentSeedVec_t seeds{};
741 SegmentVec_t segments{};
742
743 const Amg::Transform3D globToLocal = max.msSector()->globalToLocalTransform(gctx);
744 //counters for the number of seeds, extented seeds and segments
745
746 if (layerSize < minLayers) {
747 ATH_MSG_VERBOSE("Not enough layers to build a seed");
748 return std::make_pair(std::move(seeds),std::move(segments));
749 }
750
751 if (m_visionTool.isEnabled()) {
753 constexpr double legX{0.2};
754 double legY{0.8};
755 primitives.push_back(MuonValR4::drawLabel("TrueHits matched with reco houghmax hits", legX, legY, 8));
756 legY -= 0.02;
757
758 for (const SpacePoint* sp : max.getHitsInMax()) {
759 const xAOD::MuonSimHit* simHit = getTruthMatchedHit(*sp->primaryMeasurement());
760 if (!simHit) {
761 continue;
762 }
763
764 const MuonGMR4::MuonReadoutElement* reEle = m_detMgr->getReadoutElement(simHit->identify());
765 const Amg::Transform3D toChamb = reEle->msSector()->globalToLocalTransform(gctx) *
766 reEle->localToGlobalTransform(gctx, sp->identify());
767
768 const Amg::Vector3D hitPos = toChamb * xAOD::toEigen(simHit->localPosition());
769 const Amg::Vector3D hitDir = toChamb.linear() * xAOD::toEigen(simHit->localDirection());
770 const double pull = std::sqrt(SeedingAux::chi2Term(hitPos, hitDir, *sp));
771
773 const auto* mmClust = static_cast<const xAOD::MMCluster*>(sp->primaryMeasurement());
774 const MuonGMR4::MmReadoutElement* mmEle = mmClust->readoutElement();
775 const auto& design = mmEle->stripLayer(mmClust->measurementHash()).design();
776 std::string stereoDesign{!design.hasStereoAngle() ? "X" : design.stereoAngle() >0 ? "U": "V"};
777 primitives.push_back(MuonValR4::drawLabel(std::format("ml: {:1d}, gap: {:1d}, {:}, pull: {:.2f}",
778 mmEle->multilayer(), mmClust->gasGap(),
779 stereoDesign, pull), legX, legY, 8));
780 } else if(sp->type() == xAOD::UncalibMeasType::sTgcStripType) {
781 const auto* sTgcMeas = static_cast<const xAOD::sTgcMeasurement*>(sp->primaryMeasurement());
782 std::string channelString = sp->secondaryMeasurement() == nullptr ?
783 sTgcChannelType(sTgcMeas->channelType()) :
784 std::format("{:}/{:}", sTgcChannelType(sTgcMeas->channelType()),
785 sTgcChannelType(static_cast<const xAOD::sTgcMeasurement*>(sp->secondaryMeasurement())->channelType()));
786 primitives.push_back(MuonValR4::drawLabel(std::format("ml: {:1d}, gap: {:1d}, type: {:}, pull: {:.2f}",
787 sTgcMeas->readoutElement()->multilayer(), sTgcMeas->gasGap(),
788 channelString, pull), legX, legY, 8));
789 }
790 legY-=0.02;
791 }
792 m_visionTool->visualizeBucket(ctx, *max.parentBucket(),
793 "bucket", std::move(primitives));
794 }
795
796 //dump spacepoints associated with truth sim hits to an obj file
797 if(m_dumpObj){
798 Acts::ObjVisualization3D visualHelper{};
799 for (const SpacePoint* sp : max.getHitsInMax()) {
800 const xAOD::MuonSimHit* simHit = getTruthMatchedHit(*sp->primaryMeasurement());
801 if (!simHit) {
802 continue;
803 }
804 MuonValR4::drawSpacePoint(gctx.context(), *sp, visualHelper);
805 }
806 visualHelper.write(std::format("Event_{:}_{:}_spacepoints_truth.obj", ctx.eventID().event_number(), max.getHitsInMax().front()->chamber()->identString()));
807 }
808
809
810 UsedHitMarker_t allUsedHits = emptyBookKeeper(stripHitsLayers);
811 std::size_t nSeeds{0}, nExtSeeds{0}, nSegments{0}; //for the seed statistics
812
813 // helper lamda function to increase counters and fill the seeds and segments we want to return after we construct them
814 // the extended seeds are returned even if they did not make it to a segment and the segments only if successfully fitted
815 auto processSeedsAndSegments = [&](std::pair<SegmentSeedVec_t, SegmentVec_t>&& seedSegmentPairs, std::string_view source) {
816 auto& [returnSeeds, returnSegments] = seedSegmentPairs;
817 ATH_MSG_DEBUG("From " << source << ": built " << returnSeeds.size() << " seeds and " << returnSegments.size() << " segments.");
818 for(auto& seed : returnSeeds) {
819 ++nSeeds;
820 Acts::ObjVisualization3D visualHelper{};
821 if(seed->getHitsInMax().size() < m_minSeedHits){
822 ATH_MSG_VERBOSE("Seed with "<< seed->getHitsInMax().size() <<" hits rejected");
823 for(const auto& hit : seed->getHitsInMax()){
824 ATH_MSG_VERBOSE("Hit "<<m_idHelperSvc->toString(hit->identify())<<", "
825 <<Amg::toString(hit->localPosition())<<", dir: "
826 <<Amg::toString(hit->sensorDirection()));
827 if(m_dumpObj){
828 MuonValR4::drawSpacePoint(gctx.context(), *hit, visualHelper);
829 }
830
831 }
832 if(m_dumpObj){
833 visualHelper.write(std::format("Event_{:}_{:}_notExtendedSeed.obj", ctx.eventID().event_number(), seed->getHitsInMax().front()->chamber()->identString()));
834 }
835 continue;
836 }
837 ++nExtSeeds;
838 seeds.push_back(std::move(seed));
839 }
840 //move all the segments to the output container
841 std::ranges::move(returnSegments, std::back_inserter(segments));
842 nSegments += returnSegments.size();
843 };
844
845 //Start from outermost sTgc layers with combined 2D measurements
846 ATH_MSG_VERBOSE("Start building seed from sTgc outermost layers");
847 processSeedsAndSegments(buildSegmentsFromSTGC(ctx, gctx, stripHitsLayers, max, globToLocal.translation(), allUsedHits), "sTgc segment seeds");
848
849 //continue with the combinatorial seeding for the strip measurements
851
852 ATH_MSG_VERBOSE("Start building combinatoric seeds only from Micromegas");
853 processSeedsAndSegments(buildSegmentsFromMM(ctx, gctx, stripHitsLayers, max, globToLocal.translation(), allUsedHits, true), "MM combinatoric segment seeds");
854
855 }else{
856
857 ATH_MSG_VERBOSE("Start building combinatoric seeds from Micromegas and sTgc hits");
858 processSeedsAndSegments(buildSegmentsFromMM(ctx, gctx, stripHitsLayers, max, globToLocal.translation(), allUsedHits, true), "MM combinatoric segment seeds");
859 processSeedsAndSegments(buildSegmentsFromMM(ctx, gctx, stripHitsLayers, max, globToLocal.translation(), allUsedHits, false), "MM and STGC combinatoric segment seeds");
860
861 }
862
863 if(m_seedCounter) {
864 m_seedCounter->addToStat(max.msSector(), nSeeds, nExtSeeds, nSegments);
865 }
866
867 return std::make_pair(std::move(seeds),std::move(segments));
868}
869
871 const HitLayVec& allSortHits,
872 UsedHitMarker_t& usedHitMarker,
873 unsigned incr,
874 bool markNeighborHits) const {
875
876 SpacePointPerLayerSorter layerSorter{};
877
878 for(const auto& sp : spacePoints){
879 // Proection against the auxiliary measurement
880 if(!sp){
881 continue;
882 }
883
884 unsigned measLayer = layerSorter.sectorLayerNum(*sp);
885
886 Amg::Vector2D spPosX{Amg::Vector2D::Zero()};
888 switch (sp->primaryMeasurement()->numDimensions()) {
889 case 1:
890 spPosX[Amg::x] = sp->primaryMeasurement()->localPosition<1>().x();
891 break;
892 case 2:
893 spPosX = xAOD::toEigen(sp->primaryMeasurement()->localPosition<2>());
894 break;
895 default:
896 THROW_EXCEPTION("Unsupported dimension");
897 }
898
899 for (std::size_t lIdx = 0; lIdx < allSortHits.size(); ++lIdx) {
900 const HitVec& hVec{allSortHits[lIdx]};
901 //check if they are not in the same layer
902 unsigned hitLayer = layerSorter.sectorLayerNum(*hVec.front());
903 if(hitLayer != measLayer) {
904 ATH_MSG_VERBOSE("Not in the same layer since measLayer = "<< measLayer << " and "<<hitLayer);
905 continue;
906 }
907 for (std::size_t hIdx = 0 ; hIdx < hVec.size(); ++hIdx) {
908 //check the dY between the measurement and the hits
909 auto testHit = hVec[hIdx];
910 if (testHit == sp) {
911 usedHitMarker[lIdx][hIdx] += incr;
912 if(!markNeighborHits){
913 break;
914 }
915 } else if (markNeighborHits) {
916 Amg::Vector2D testPosX{Amg::Vector2D::Zero()};
918 switch (testHit->primaryMeasurement()->numDimensions()) {
919 case 1:
920 testPosX[Amg::x] = testHit->primaryMeasurement()->localPosition<1>().x();
921 break;
922 case 2:
923 testPosX = xAOD::toEigen(testHit->primaryMeasurement()->localPosition<2>());
924 break;
925 default:
926 THROW_EXCEPTION("Unsupported dimension");
927 }
928 //if the hit not found let's see if it is too close to the segment's measurement
929 double deltaX = (testPosX - spPosX).mag();
930 if(deltaX < m_maxdYWindow){
931 usedHitMarker[lIdx][hIdx] += incr;
932 }
933 }
934 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<"- Marking hit "<<(*testHit)<<", used count: "
935 <<usedHitMarker[lIdx][hIdx]);
936 }
937 }
938 }
939}
940
941StatusCode NswSegmentFinderAlg::execute(const EventContext &ctx) const {
942 // read the inputs
943 const EtaHoughMaxContainer *maxima{nullptr};
944 ATH_CHECK(SG::get( maxima, m_etaKey, ctx));
945
946 const ActsTrk::GeometryContext *gctx{nullptr};
947 ATH_CHECK(SG::get(gctx, m_geoCtxKey, ctx));
948
949 // prepare our output collection
950 SG::WriteHandle writeSegments{m_writeSegmentKey, ctx};
951 ATH_CHECK(writeSegments.record(std::make_unique<SegmentContainer>()));
952
953 SG::WriteHandle writeSegmentSeeds{m_writeSegmentSeedKey, ctx};
954 ATH_CHECK(writeSegmentSeeds.record(std::make_unique<SegmentSeedContainer>()));
955
956 // we use the information from the previous eta-hough transform
957 // to get the combined hits that belong in the same maxima
958 for (const HoughMaximum *max : *maxima) {
959
960 auto [seeds, segments] = findSegmentsFromMaximum(*max, *gctx, ctx);
961
962 if (msgLvl(MSG::VERBOSE)) {
963 ATH_MSG_VERBOSE("Hits from Hough maximum");
964 for(const auto& hitMax : max->getHitsInMax()){
965 ATH_MSG_VERBOSE("Hit "<<m_idHelperSvc->toString(hitMax->identify())<<", "
966 <<Amg::toString(hitMax->localPosition())<<", dir: "
967 <<Amg::toString(hitMax->sensorDirection()));
968 }
969 }
970
971 for(auto& seed: seeds){
972
973 if (msgLvl(MSG::VERBOSE)){
974 std::stringstream sstr{};
975 sstr<<"Seed tanBeta = "<<seed->tanBeta()<<", y0 = "<<seed->interceptY()
976 <<", tanAlpha = "<<seed->tanAlpha()<<", x0 = "<<seed->interceptX()<<", hits in the seed "
977 <<seed->getHitsInMax().size()<<std::endl;
978
979 for(const auto& hit : seed->getHitsInMax()){
980 sstr<<" *** Hit "<<m_idHelperSvc->toString(hit->identify())<<", "
981 << Amg::toString(hit->localPosition())<<", dir: "<<Amg::toString(hit->sensorDirection())<<std::endl;
982 }
983 ATH_MSG_VERBOSE(sstr.str());
984 }
985 if (m_visionTool.isEnabled()) {
986 m_visionTool->visualizeSeed(ctx, *seed, "#phi-combinatorialSeed");
987 }
988
989 writeSegmentSeeds->push_back(std::move(seed));
990
991 }
992
993 //Resolve ambiguities between segments before writing them to the output
994 ATH_MSG_VERBOSE("Before ambiguity resolution, there are in total "<<segments.size()<<" segments:");
995 for(const auto& seg : segments){
996
997 if(msgLvl(MSG::VERBOSE)){
998 std::stringstream sstr{};
999 sstr<<"Segment chi2/ndof = "<<seg->chi2()/std::max(1u,seg->nDoF())<<", hits in the segment "
1000 <<seg->measurements().size()<<std::endl;
1001
1002 const Parameters pars = localSegmentPars(*gctx, *seg);
1003 sstr<<"Segment parameters : "<<toString(pars)<<std::endl;
1004 for(const auto& hit : seg->measurements()){
1005 bool hasTruth{false};
1006 if(hit->type()!=xAOD::UncalibMeasType::Other){
1007 hasTruth = (getTruthMatchedHit(*hit->spacePoint()->primaryMeasurement()) !=nullptr);
1008 std::string type = hit->fitState() != CalibratedSpacePoint::State::Valid ? "outlier" : "valid";
1009
1010 sstr<<" *** Hit "<<m_idHelperSvc->toString(hit->spacePoint()->identify())<<", "
1011 << type << ", "
1012 << Amg::toString(hit->spacePoint()->localPosition())<<", dir: "
1013 <<Amg::toString(hit->spacePoint()->sensorDirection())<<", has truth matched: "<<hasTruth<<std::endl;
1014 }
1015 }
1016 ATH_MSG_VERBOSE(sstr.str());
1017
1018 }
1019
1020 }
1021
1022
1023 resolveAmbiguities(*gctx, segments);
1024 ATH_MSG_VERBOSE("After ambiguity resolution, there are in total "<<segments.size()<<" segments:");
1025
1026 for (auto &seg : segments) {
1027 if(msgLvl(MSG::VERBOSE)){
1028 std::stringstream sstr{};
1029 sstr<<"Segment chi2/ndof = "<<seg->chi2()/std::max(1u,seg->nDoF())<<", hits in the segment "
1030 <<seg->measurements().size()<<std::endl;
1031 const Parameters pars = localSegmentPars(*gctx, *seg);
1032 sstr<<"Segment parameters : "<<toString(pars)<<std::endl;
1033
1034 for(const auto& hit : seg->measurements()){
1035 bool hasTruth{false};
1036 if(hit->type()!=xAOD::UncalibMeasType::Other){
1037 hasTruth = getTruthMatchedHit(*hit->spacePoint()->primaryMeasurement()) !=nullptr;
1038 std::string type = hit->fitState() != CalibratedSpacePoint::State::Valid ? "outlier" : "valid";
1039 sstr<<" *** Hit "<<m_idHelperSvc->toString(hit->spacePoint()->identify())<<", "
1040 << type << ", "
1041 << Amg::toString(hit->spacePoint()->localPosition())<<", dir: "
1042 <<Amg::toString(hit->spacePoint()->sensorDirection())<<", has truth matched: "<<hasTruth<<std::endl;
1043 }
1044 }
1045 ATH_MSG_VERBOSE(sstr.str());
1046 }
1047
1048
1049 if (m_visionTool.isEnabled()) {
1050 m_visionTool->visualizeSegment(ctx, *seg, "#phi-segment");
1051 }
1052
1053 if(m_dumpObj){
1054 Acts::ObjVisualization3D visualHelper{};
1055 MuonValR4::drawSegmentMeasurements(gctx->context(), *seg, visualHelper);
1056 MuonValR4::drawSegmentLine(gctx->context(), *seg, visualHelper);
1057 visualHelper.write(std::format("Event_{:}_segment_{:}.obj", ctx.eventID().event_number(), seg->msSector()->identString()));
1058 }
1059
1060 writeSegments->push_back(std::move(seg));
1061
1062 }
1063
1064
1065 }
1066
1067 return StatusCode::SUCCESS;
1068}
1069
1071 if(m_seedCounter) {
1072 m_seedCounter->printTableSeedStats(msgStream());
1073 }
1074 return StatusCode::SUCCESS;
1075}
1076
1077void NswSegmentFinderAlg::SeedStatistics::addToStat(const MuonGMR4::SpectrometerSector* msSector, unsigned seeds, unsigned extSeeds, unsigned segments){
1078 std::unique_lock guard{m_mutex};
1079 SectorField key{};
1080 key.chIdx = msSector->chamberIndex();
1081 key.phi = msSector->stationPhi();
1082 key.eta = msSector->chambers().front()->stationEta();
1083
1084 auto &entry = m_seedStat[key];
1085 entry.nSeeds += seeds;
1086 entry.nExtSeeds += extSeeds;
1087 entry.nSegments += segments;
1088}
1089
1091
1092
1093 std::stringstream sstr{};
1094 sstr<<"Seed statistics per sector:"<<std::endl;
1095 sstr<<"-----------------------------------------------------"<<std::endl;
1096 sstr<<"| Chamber | Phi | Eta | Seeds | ExtSeeds | FittedSegments |"<<std::endl;
1097 sstr<<"-----------------------------------------------------"<<std::endl;
1098
1099 using namespace Muon::MuonStationIndex;
1100
1101 for (const auto& [sector, stats] : m_seedStat) {
1102 sstr << "| " << std::setw(3) << chName(sector.chIdx)
1103 << " | " << std::setw(2) << static_cast<unsigned>(sector.phi)
1104 << " | " << std::setw(3) << static_cast<int>(sector.eta)
1105 << " | " << std::setw(7) << stats.nSeeds
1106 << " | " << std::setw(8) << stats.nExtSeeds
1107 << " | " << std::setw(8) << stats.nSegments
1108 << " |"<<std::endl;
1109 }
1110
1111 sstr<<"------------------------------------------------------------"<<std::endl;
1112 msg<<MSG::ALWAYS<<"\n"<<sstr.str()<<endmsg;
1113 }
1114
1115
1116} // 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.
bool hit(const Container &ids, int pdgId)
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.
struct TBPatternUnitContext Unknown
#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.
double recoveryPull
Maximum pull on a measurement to add it back on the line.
#define THROW_EXCEPTION(MESSAGE)
Definition throwExcept.h:10