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

Helper class to group muon sgements that may belong to a muon trajectory. More...

#include <MsTrackSeederTool.h>

Inheritance diagram for MuonR4::MsTrackSeederTool:
Collaboration diagram for MuonR4::MsTrackSeederTool:

Public Types

enum class  SeedCoords : std::uint8_t { eDetSection , eSector , ePosOnCylinder }
 Abrivation of the seed coordinates. More...
using SearchTree_t = Acts::KDTree<3, const xAOD::MuonSegment*, double, std::array, 6>
 Definition of the search tree class.
using Location = MsTrackSeed::Location
 Enum toggling whether the segment is in the endcap or barrel.
using SectorProjector = ExpandedSector::SectorProjector
 Recycle the expanded sector.
using TreeRawVec_t = SearchTree_t::vector_t
 Abbrivation of the KDTree raw data vector.

Public Member Functions

virtual StatusCode initialize () override final
virtual StatusCode findTrackSeeds (const EventContext &ctx, std::vector< MsTrackSeed > &outSeeds) const override final
 Retrieves the segment container from StoreGate and constructs TrackSeeds from them.
virtual Acts::Result< Acts::BoundTrackParameters > estimateStartParameters (const EventContext &ctx, const MsTrackSeed &seed) const override final
 Estimate the start track parameters for a given track seed.
virtual bool withinBounds (const Amg::Vector2D &projPos, const Location loc) const override final
virtual Amg::Vector2D expressOnCylinder (const Acts::GeometryContext &tgContext, const xAOD::MuonSegment &segment, const Location loc, const ExpandedSector sector) const override final
 Expresses the passed segment on the virtual cylinder constructed by the track seeder.
virtual double estimateQtimesP (const Acts::GeometryContext &tgContext, const MsTrackSeed &seed, MagField::AtlasFieldCache &fieldCache) const override final
 Estimate the charge times momentum of a muon track candidate from the contained segments.

Private Types

using PosMomPair_t = std::pair<Amg::Vector3D, Amg::Vector3D>

Private Member Functions

Amg::Vector3D segPosOntoPhiPlane (const Acts::GeometryContext &tgContext, const Amg::Vector3D &planeNormal, const xAOD::MuonSegment &segment) const
 Projects the segment position onto the plane with global phi = x The local coordinate system is arranged such that the x-axis is co-linear to the phi direction.
SearchTree_t constructTree (const Acts::GeometryContext &tgContext, const xAOD::MuonSegmentContainer &segments) const
 Construct a complete search tree from a MuonSegment container.
double estimateQtimesP (const Amg::Vector3D &planeNorm, const PosMomPair_t &p1, const PosMomPair_t &p2, const PosMomPair_t &p3, MagField::AtlasFieldCache &fieldCache) const
 Estimate the charge times momentum of a muon candidate when three points are available.
double estimateQtimesP (const Amg::Vector3D &planeNorm, const PosMomPair_t &p1, const PosMomPair_t &p2, MagField::AtlasFieldCache &fieldCache) const
 Estimate the charge times momentum of a muon candidate when two points are available.
double getPtimesQ (const Amg::Vector3D &forceIntegral, const Amg::Vector3D &deltaDir) const
 Compute the charge times momentum from the integral of lorentz force and the total change in direction.
Amg::Vector3D forceIntegration (const PosMomPair_t &point1, const PosMomPair_t &point2, const Amg::Vector3D &planeNorm, MagField::AtlasFieldCache &fieldCache) const
 Compute the integral of magnetic force (v x B ) dS along a trajectory, given the initial and final positions and directions of the trajectory.
void appendSegment (const Acts::GeometryContext &tgContext, const xAOD::MuonSegment *segment, const Location loc, TreeRawVec_t &outContainer) const
 Append the segment to the raw data container.
const MuonGMR4::SpectrometerSector * envelope (const xAOD::MuonSegment &segment) const
 Returns the spectrometer envelope associated to the segment (Coord system where the parameter are expressed).
MsTrackSeedContainer resolveOverlaps (MsTrackSeedContainer &&unresolved) const
 Removes exact duplciates or partial subsets of the MsTrackSeeds.

Private Attributes

std::vector< double > m_fieldExtpSteps {}
 The list of field steps in the force field integration.
DoubleProperty m_barrelRadius {this, "BarrelRadius", 7.*Gaudi::Units::m}
 The radius of he barrel cylinder.
DoubleProperty m_barrelLength {this, "BarrelLength", 25.*Gaudi::Units::m}
 The maximum length of the barrel cylinder, if not capped by the placement of the endcap discs.
DoubleProperty m_endcapDiscZ {this, "EndcapDiscZ", 15.*Gaudi::Units::m}
 Position of the endcap discs.
DoubleProperty m_endcapDiscRadius {this, "EndcapRadius", 13.*Gaudi::Units::m}
 Radius of the endcap discs.
DoubleProperty m_seedHalfLength {this, "SeedHalfLength", 25.*Gaudi::Units::cm}
 Maximum separation of point on the cylinder to be picked up onto a seed.
DoubleProperty m_barrelMomentumRes {this, "BarrelMomentumResolution", 0.05}
 Momentum resolution in the barrel.
DoubleProperty m_endcapMomentumRes {this, "EndcapMomentumResolution", 0.1}
 Momentum resolution in the endcap.
UnsignedIntegerProperty m_nFieldSteps {this, "nFieldSteps", 10}
 number of steps between two segments to integrate the magnetic field
ToolHandle< ISegmentSelectionTool > m_segSelector {this, "SegmentSelectionTool" , "" }
 Pointer to the segement selection tool which compares two segments for their compatibilitiy.
ServiceHandle< ActsTrk::ITrackingGeometrySvc > m_trackingGeometrySvc {this, "TrackingGeometrySvc", "ActsTrackingGeometrySvc"}
 Tracking geometry tool.
ActsTrk::ContextUtility m_ctxProvider {this}
 Utility to fetch the geometry, magnetic field and calibration context in the event.
SG::ReadHandleKey< xAOD::MuonSegmentContainer > m_segmentKey {this, "SegmentContainer", "MuonSegmentsFromR4" }
 Declare the data dependency on the standard Mdt+Rpc+Tgc segment container & on the NSW segment container.
const MuonGMR4::MuonDetectorManager * m_detMgr {nullptr}

Detailed Description

Helper class to group muon sgements that may belong to a muon trajectory.

The reconstructed muon segments are projected onto the surface of a cylinder crossing roughly the middle stations of the MS. They are then appended to a 3-dimensional search tree using the extrapolated coordinate on the cylnder, the cylinder surface index and the segment's associated sector number.

Definition at line 35 of file MsTrackSeederTool.h.

Member Typedef Documentation

◆ Location

Enum toggling whether the segment is in the endcap or barrel.

Definition at line 41 of file MsTrackSeederTool.h.

◆ PosMomPair_t

Definition at line 109 of file MsTrackSeederTool.h.

◆ SearchTree_t

using MuonR4::MsTrackSeederTool::SearchTree_t = Acts::KDTree<3, const xAOD::MuonSegment*, double, std::array, 6>

Definition of the search tree class.

Definition at line 39 of file MsTrackSeederTool.h.

◆ SectorProjector

Recycle the expanded sector.

Definition at line 43 of file MsTrackSeederTool.h.

◆ TreeRawVec_t

using MuonR4::MsTrackSeederTool::TreeRawVec_t = SearchTree_t::vector_t

Abbrivation of the KDTree raw data vector.

Definition at line 54 of file MsTrackSeederTool.h.

Member Enumeration Documentation

◆ SeedCoords

enum class MuonR4::MsTrackSeederTool::SeedCoords : std::uint8_t
strong

Abrivation of the seed coordinates.

Enumerator
eDetSection 

Encode the seed location (-1,1 -> endcaps, 0 -> barrel.

eSector 

Sector of the associated spectrometer sector.

ePosOnCylinder 

Extrapolation position along the cylinder surface.

Definition at line 45 of file MsTrackSeederTool.h.

45 : std::uint8_t{
47 eDetSection,
49 eSector,
51 ePosOnCylinder
52 };

Member Function Documentation

◆ appendSegment()

void MuonR4::MsTrackSeederTool::appendSegment ( const Acts::GeometryContext & tgContext,
const xAOD::MuonSegment * segment,
const Location loc,
TreeRawVec_t & outContainer ) const
private

Append the segment to the raw data container.

If the projection onto the barrel cylinder / endcap discs exceeds the bounds, the segment is not added. Segments at the interval boundaries of the expanded sector are mirred into the bin next, but outside the interval.

Parameters
tgContextGeometry context to find the proper wire direction.
segmentPointer to the segment to add
locSwitch whether the segment shall be projected onto barrel/endcap
outContainerRaw KDTree data vector where the segment is appended

Check whether the segment belongs to the left or right sector as well

Use the sector expanded sector coordinate

Enumeration to indicate whether the segment is expressed on the negative endcap (-1), the barrel (0) or the positive endcap

Coordinate on the cylinder

Definition at line 649 of file MsTrackSeederTool.cxx.

652 {
653
654 const unsigned segSector = segment->sector();
655 for (const auto proj : {SectorProjector::leftOverlap,
656 SectorProjector::center,
657 SectorProjector::rightOverlap}) {
659 const ExpandedSector projSector{segSector, proj};
660 if (segment->nPhiLayers() > 0 && projSector != ExpandedSector{segment->position().phi()}) {
661 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Segment @"<<Amg::toString(segment->position())
662 <<" is not in sector "<<projSector);
663 continue;
664 }
665 const Amg::Vector2D refPoint{expressOnCylinder(tgContext, *segment, loc, projSector)};
666 if (!withinBounds(refPoint, loc)) {
667 continue;
668 }
669 using enum SeedCoords;
670 std::array<double, 3> coords{Acts::filledArray<double, 3>(0.)};
672 coords[Acts::toUnderlying(eSector)] = projSector.sector();
675 coords[Acts::toUnderlying(eDetSection)] = Acts::copySign(Acts::toUnderlying(loc), refPoint[1]);
677 coords[Acts::toUnderlying(ePosOnCylinder)] = refPoint[Location::Barrel == loc];
678 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Add segment "<<::print(*segment)
679 <<" with "<<coords<<" to the search tree");
680 outContainer.emplace_back(std::move(coords), segment);
681 }
682 }
#define ATH_MSG_VERBOSE(x,...)
virtual bool withinBounds(const Amg::Vector2D &projPos, const Location loc) const override final
SeedCoords
Abrivation of the seed coordinates.
@ eSector
Sector of the associated spectrometer sector.
@ ePosOnCylinder
Extrapolation position along the cylinder surface.
@ eDetSection
Encode the seed location (-1,1 -> endcaps, 0 -> barrel.
virtual Amg::Vector2D expressOnCylinder(const Acts::GeometryContext &tgContext, const xAOD::MuonSegment &segment, const Location loc, const ExpandedSector sector) const override final
Expresses the passed segment on the virtual cylinder constructed by the track seeder.
Amg::Vector3D position() const
Returns the position as Amg::Vector.
std::uint8_t nPhiLayers() const
Returns the number of trigger phi hits.
std::string toString(const Translation3D &translation, int precision=4)
GeoPrimitvesToStringConverter.
Eigen::Matrix< double, 2, 1 > Vector2D
std::string print(const cont_t &container)
Print a space point container to string.

◆ constructTree()

SearchTree_t MuonR4::MsTrackSeederTool::constructTree ( const Acts::GeometryContext & tgContext,
const xAOD::MuonSegmentContainer & segments ) const
private

Construct a complete search tree from a MuonSegment container.

Parameters
segmentsReference to the segment container to construct.

Definition at line 683 of file MsTrackSeederTool.cxx.

684 {
685 TreeRawVec_t rawData{};
686 rawData.reserve(3*segments.size());
687 for (const xAOD::MuonSegment* segment : segments){
688 appendSegment(tgContext, segment, Location::Barrel, rawData);
689 appendSegment(tgContext, segment, Location::Endcap, rawData);
690 }
691 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Create a new tree with "<<rawData.size()<<" entries. ");
692 return SearchTree_t{std::move(rawData)};
693 }
size_type size() const noexcept
Returns the number of elements in the collection.
void appendSegment(const Acts::GeometryContext &tgContext, const xAOD::MuonSegment *segment, const Location loc, TreeRawVec_t &outContainer) const
Append the segment to the raw data container.
SearchTree_t::vector_t TreeRawVec_t
Abbrivation of the KDTree raw data vector.
Acts::KDTree< 3, const xAOD::MuonSegment *, double, std::array, 6 > SearchTree_t
Definition of the search tree class.
MuonSegment_v1 MuonSegment
Reference the current persistent version:

◆ envelope()

const MuonGMR4::SpectrometerSector * MuonR4::MsTrackSeederTool::envelope ( const xAOD::MuonSegment & segment) const
private

Returns the spectrometer envelope associated to the segment (Coord system where the parameter are expressed).

Parameters
segmentReference to the segment of interest

Definition at line 816 of file MsTrackSeederTool.cxx.

816 {
817 return m_detMgr->getSectorEnvelope(segment.chamberIndex(),
818 segment.sector(),
819 segment.etaIndex());
820 }
const MuonGMR4::MuonDetectorManager * m_detMgr
::Muon::MuonStationIndex::ChIndex chamberIndex() const
Returns the chamber index.
int etaIndex() const
Returns the eta index, which corresponds to stationEta in the offline identifiers (and the ).

◆ estimateQtimesP() [1/3]

double MuonR4::MsTrackSeederTool::estimateQtimesP ( const Acts::GeometryContext & tgContext,
const MsTrackSeed & seed,
MagField::AtlasFieldCache & fieldCache ) const
finaloverridevirtual

Estimate the charge times momentum of a muon track candidate from the contained segments.

The position and direction of the segments are projected onto a given phi plane, defined by the segments with phi information or the sector plane.
The muon trajectory is approximated as 2D trajectory within this plane to avoid side effects from (non)-present phi measurements.

Parameters
tgContextThe geometry context to align the segment w.r.t sector
seedReference to the seed of interest.
fieldCacheThe initialized magnetic field map

Calculate the averaged phi from the segments

If less than 3 segments are found check whether the track crosses the BEE or EE chamber and use that segment as the third one.

Fall back function for the truth segment test

Contrain the direction to have the same tangent in the precision plane and to lay in the bending plane

Definition at line 530 of file MsTrackSeederTool.cxx.

532 {
533 using namespace Muon::MuonStationIndex;
534
535 // Guard canEstimateQtimesP here, not only at call sites.
536 if (!canEstimateQtimesP(seed)) {
537 ATH_MSG_WARNING(__func__<<"() "<<__LINE__<<" Cannot estimate q*p from seed "<<seed
538 <<" - insufficient inner/middle/outer layer coverage.");
539 return 0.;
540 }
541
543 double deltaPhiAcc {0.};
544 std::optional<double> centralPhi {};
545 unsigned nSegsWithPhi{0};
546 for (const xAOD::MuonSegment* segment : seed.segments()) {
547 if (segment->nPhiLayers() > 0) {
548 const double segPhi {segment->position().phi()};
549 if (!centralPhi) centralPhi = segPhi;
550 deltaPhiAcc += P4Helpers::deltaPhi(*centralPhi, segPhi);
551 ++nSegsWithPhi;
552 }
553 }
554 const double circPhi {nSegsWithPhi > 0
555 ? P4Helpers::deltaPhi(*centralPhi + deltaPhiAcc / nSegsWithPhi, 0.)
556 : seed.sector().phi()};
557 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Average segment phi: "
558 <<inDeg(circPhi)<<" [deg], nSegsWithPhi: "<<nSegsWithPhi);
559
560 std::array<const xAOD::MuonSegment*, 3> segmentsToUse{};
561 // Try first to find segments in the inner, middle and outer layers. If both a barrel
562 // and endcap segments are present in the same layer, the barrel segment is preferred.
563 for (const xAOD::MuonSegment* segment : seed.segments()) {
564 ChIndex chIndex {segment->chamberIndex()};
565 switch(toLayerIndex(chIndex)) {
566 using enum LayerIndex;
567 case Inner:
568 if (!segmentsToUse[0] || isBarrel(chIndex)) {
569 segmentsToUse[0] = segment;
570 }
571 break;
572 case Middle:
573 if (!segmentsToUse[1] || isBarrel(chIndex)) {
574 segmentsToUse[1] = segment;
575 }
576 break;
577 case Outer:
578 if (!segmentsToUse[2] || isBarrel(chIndex)) {
579 segmentsToUse[2] = segment;
580 }
581 break;
582 default:
583 break;
584 }
585 }
586 unsigned nSegments = std::ranges::count_if(segmentsToUse,
587 [](const xAOD::MuonSegment* seg) { return seg != nullptr; });
588
591 if (nSegments < 3) {
592 auto missingSeg = std::ranges::find(segmentsToUse, nullptr);
593 for (const xAOD::MuonSegment* segment : seed.segments()) {
594 LayerIndex layIndex {toLayerIndex(segment->chamberIndex())};
595 if (layIndex != LayerIndex::Extended && layIndex != LayerIndex::BarrelExtended) {
596 continue;
597 }
598 assert(missingSeg != segmentsToUse.end());
599 *missingSeg = segment;
600 ++nSegments;
601 if (nSegments == 3) {
602 break;
603 }
604 missingSeg = std::ranges::find(segmentsToUse, nullptr);
605 }
606 std::ranges::sort(segmentsToUse, [](const xAOD::MuonSegment* seg1, const xAOD::MuonSegment* seg2) {
607 if (!seg1 || !seg2) {
608 return seg1 != nullptr;
609 }
610 return seg1->position().perp() < seg2->position().perp();
611 });
612 }
613 const Amg::Vector3D planeNorm {Acts::makeDirectionFromPhiTheta(circPhi + 90._degree, 90._degree)};
614 auto point = [&](const xAOD::MuonSegment* seg) {
615 const Amg::Vector3D projSegPos {segPosOntoPhiPlane(tgContext, planeNorm, *seg)};
616
618 if (nMeasurements(*seg) == 0ul) {
619 return std::make_pair(projSegPos,
620 Acts::makeDirectionFromPhiTheta(circPhi, seg->direction().theta()));
621 }
622
623 const Acts::Surface& firstSurf {xAOD::muonSurface(firstMeasurement(*seg))};
624 const Acts::TrackingVolume* volume{
625 MuonGMR4::highestAlignable(m_trackingGeometrySvc->trackingGeometry()->findVolume(volumeId(firstSurf)))};
626
627 if (!volume) {
628 ATH_MSG_WARNING("estimateQtimesP() "<<__LINE__
629 <<" - Failed to find tracking volume for seed measurement "<<volumeId(firstSurf));
630 return std::make_pair(projSegPos,
631 Acts::makeDirectionFromPhiTheta(circPhi, seg->direction().theta()));
632 }
633 const Amg::Transform3D& toLoc {volume->globalToLocalTransform(tgContext)};
634 const Amg::Vector3D locSegDir {toLoc.linear() * seg->direction()};
635 const Amg::Vector3D locNormal {toLoc.linear() * planeNorm};
638 const double tanBeta {houghTanBeta(locSegDir)};
639 const double tanAlpha {- (locNormal.y() * tanBeta + locNormal.z()) / locNormal.x()};
640 const Amg::Vector3D newDir = volume->localToGlobalTransform(tgContext).linear() *
641 Acts::makeDirectionFromAxisTangents(tanAlpha, tanBeta);
642
643 return std::make_pair(projSegPos, newDir);
644 };
645 return nSegments == 3
646 ? estimateQtimesP(planeNorm, point(segmentsToUse[0]), point(segmentsToUse[1]), point(segmentsToUse[2]), magField)
647 : estimateQtimesP(planeNorm, point(segmentsToUse[0]), point(segmentsToUse[1]), magField);
648 }
Scalar phi() const
phi method
#define ATH_MSG_WARNING(x,...)
ServiceHandle< ActsTrk::ITrackingGeometrySvc > m_trackingGeometrySvc
Tracking geometry tool.
virtual double estimateQtimesP(const Acts::GeometryContext &tgContext, const MsTrackSeed &seed, MagField::AtlasFieldCache &fieldCache) const override final
Estimate the charge times momentum of a muon track candidate from the contained segments.
Amg::Vector3D segPosOntoPhiPlane(const Acts::GeometryContext &tgContext, const Amg::Vector3D &planeNormal, const xAOD::MuonSegment &segment) const
Projects the segment position onto the plane with global phi = x The local coordinate system is arran...
Amg::Vector3D direction() const
Returns the direction as Amg::Vector.
Eigen::Affine3d Transform3D
Eigen::Matrix< double, 3, 1 > Vector3D
const Acts::TrackingVolume * highestAlignable(const Acts::TrackingVolume *volume)
Returns the highest parent volume that is alignable.
double houghTanBeta(const Amg::Vector3D &v)
Returns the hough tanBeta [y] / [z].
std::size_t nMeasurements(const xAOD::MuonSegment &segment)
Returns the number of associated Uncalibrated measurements.
Acts::GeometryIdentifier volumeId(const Acts::Surface &surface)
Returns the identifier of the volume in which the surface is embedded.
const xAOD::UncalibratedMeasurement * firstMeasurement(const xAOD::MuonSegment &segment, const bool skipOutlier=true)
Retrieves the first measurement associated with the segment.
constexpr float inDeg(const float rad)
ChIndex chIndex(const std::string &index)
convert ChIndex name string to enum
bool isBarrel(const ChIndex index)
Returns true if the chamber index points to a barrel chamber.
LayerIndex
enum to classify the different layers in the muon spectrometer
LayerIndex toLayerIndex(ChIndex index)
convert ChIndex into LayerIndex
ChIndex
enum to classify the different chamber layers in the muon spectrometer
double deltaPhi(double phiA, double phiB)
delta Phi in range [-pi,pi[
Definition P4Helpers.h:34
const Acts::Surface & muonSurface(const UncalibratedMeasurement *meas)
Returns the associated Acts surface to the measurement.

◆ estimateQtimesP() [2/3]

double MuonR4::MsTrackSeederTool::estimateQtimesP ( const Amg::Vector3D & planeNorm,
const PosMomPair_t & p1,
const PosMomPair_t & p2,
const PosMomPair_t & p3,
MagField::AtlasFieldCache & fieldCache ) const
private

Estimate the charge times momentum of a muon candidate when three points are available.

The given position and direction of each point need to be projected onto the given phi plane, and the muon trajectory is approximated as 2D trajectory within this plane.

Parameters
planeNormNormal of the bending plane containing the muon trajectory in this simplified approach
p1First segment
p2Second segment
p3Third segment
fieldCacheThe initialized magnetic field map
Returns
: Estimated Q*P value

Update the scores, encoding how well this estimate agrees with the other estimates, weighted by their reliability.

Shift scores to positive values so that reliability can be used multiplicatively when selecting the best estimate.

Find the best charge estimate and estimate the final momentum as a weighted average of the estimates that agree with the best charge

We exclude the estimation given from Pair02Pos because it is an approximation, and therefore the estimation is less precise.

Definition at line 402 of file MsTrackSeederTool.cxx.

406 {
407 // When 3 points are available, we can use each pair of segments to estimate
408 // the momentum and charge, and then combine the estimates. To make the
409 // combination, we define a struct to hold each PtimesQ estimate
410 struct Estimate {
411 double PtimesQ{0.};
412 // Weighting factor based on the magnitude of the integrated force.
413 double weight{0.};
414 // Scores as a combination of charge agreement and momentum deviation.
415 double score{0.};
416 };
417
418 const Amg::Vector3D force12 {forceIntegration(p1, p2, planeNorm, fieldCache)};
419 const Amg::Vector3D force23 {forceIntegration(p2, p3, planeNorm, fieldCache)};
420 const Amg::Vector3D force13 {force12 + force23};
421
422 std::vector<Estimate> estimates{};
423 const double weightNorm {force12.mag() + force23.mag()};
424 // Pairwise momentum estimates: 12 and 23
425 estimates.emplace_back(getPtimesQ(force12, p2.second - p1.second), force12.mag()/weightNorm, 0.);
426 estimates.emplace_back(getPtimesQ(force23, p3.second - p2.second), force23.mag()/weightNorm, 0.);
427 // Two estimates for pair 13: using segment directions and using position differences
428 const double weight13 {force13.mag()/weightNorm};
429 estimates.emplace_back(getPtimesQ(force13, p3.second - p1.second), weight13, 0.);
430 const Amg::Vector3D t12 {(p2.first - p1.first).unit()};
431 const Amg::Vector3D t23 {(p3.first - p2.first).unit()};
432 estimates.emplace_back(getPtimesQ(force13, t23 - t12), weight13, 0.);
433
434 for (std::size_t i {0}; i < estimates.size(); ++i) {
435 Estimate& est1 {estimates[i]};
436 for (std::size_t j {i+1}; j < estimates.size(); ++j) {
437 Estimate& est2 {estimates[j]};
438 double agreementScore {(chargeAgree(est1.PtimesQ, est2.PtimesQ) ? 1. : -1.) -
439 momentumDev(est1.PtimesQ, est2.PtimesQ)};
442 est1.score += est2.weight * agreementScore;
443 est2.score += est1.weight * agreementScore;
444 }
445 }
448 const double minScore {std::ranges::min_element(estimates,
449 {}, &Estimate::score)->score};
450 for (Estimate& est : estimates) {
451 est.score -= minScore;
452 }
453 if (msgLvl(MSG::VERBOSE)) {
454 std::vector<std::string> names {"Pair01", "Pair12", "Pair02Seg", "Pair02Pos"};
455 for (const auto [i, est] : Acts::enumerate(estimates)) {
456 ATH_MSG_VERBOSE(__func__<<"() Estimate "<<names[i]<<": PtimesQ: "<<est.PtimesQ*1e-3
457 <<", weight: "<<est.weight<<", score: "<<est.score);
458 }
459 }
462 const Estimate& bestEstimate {*std::ranges::max_element(estimates,
463 std::ranges::less{}, [](const Estimate& est){
464 return est.score * est.weight;})};
465 const double charge {std::copysign(1., bestEstimate.PtimesQ)};
466
467 double totalSum {0.}, totalWeight {0.};
468 for (const Estimate& est : estimates) {
471 if (&est == &estimates.back()) {
472 continue;
473 }
474 if (chargeAgree(est.PtimesQ, charge)) {
475 totalSum += est.PtimesQ * est.weight;
476 totalWeight += est.weight;
477 }
478 }
479 assert(totalWeight > Acts::s_epsilon);
480 return totalSum / totalWeight;
481 }
double charge(const T &p)
Definition AtlasPID.h:1003
detray::unit< scalar_t > unit
double getPtimesQ(const Amg::Vector3D &forceIntegral, const Amg::Vector3D &deltaDir) const
Compute the charge times momentum from the integral of lorentz force and the total change in directio...
Amg::Vector3D forceIntegration(const PosMomPair_t &point1, const PosMomPair_t &point2, const Amg::Vector3D &planeNorm, MagField::AtlasFieldCache &fieldCache) const
Compute the integral of magnetic force (v x B ) dS along a trajectory, given the initial and final po...
float j(const xAOD::IParticle &, const xAOD::TrackMeasurementValidation &hit, const Eigen::Matrix3d &jab_inv)

◆ estimateQtimesP() [3/3]

double MuonR4::MsTrackSeederTool::estimateQtimesP ( const Amg::Vector3D & planeNorm,
const PosMomPair_t & p1,
const PosMomPair_t & p2,
MagField::AtlasFieldCache & fieldCache ) const
private

Estimate the charge times momentum of a muon candidate when two points are available.

The position and direction of each point need to be projected onto the given phi plane, and the muon trajectory is approximated as 2D trajectory within this plane.

Parameters
planeNormNormal of the bending plane containing the muon trajectory in this simplified approach
seg1First segment
seg2Second segment
fieldCacheThe initialized magnetic field map
Returns
: Estimated Q*P value

Definition at line 482 of file MsTrackSeederTool.cxx.

485 {
486
487 return getPtimesQ(forceIntegration(p1, p2, planeNorm, fieldCache),
488 p2.second - p1.second);
489 }

◆ estimateStartParameters()

Acts::Result< Acts::BoundTrackParameters > MuonR4::MsTrackSeederTool::estimateStartParameters ( const EventContext & ctx,
const MsTrackSeed & seed ) const
finaloverridevirtual

Estimate the start track parameters for a given track seed.

Parameters
ctxEventContext
seedThe track seed for which to estimate start parameters
Returns
The estimated start parameters or an error

Ususally we would like to take the first segment with a sufficient amount of phi hits to set the initial position and direction of the track fit. However in some cases, the segment from the NSW has a missreconstructed phi direction which causes the track fit to loose all BW and OW hits in the first iteration. Therefore if the first segment is a NSW segment, we first try to use a non-NSW segments with enough phi hits. If we don't find any segment with enough phi hits we will use the NSW segment as reference as long as it passes the seeding quality criteria.

The middle or outer segment provide the phi information. Not so easy becasue we want to Take the y0 & precision direction from the inner segment but the phi & x0 from a straight line extrapolation onto the plane

Update the local seed direction

Extrapolate the seed segment onto the inner plane. We want to take the precision intercept from the inner segment and the non-precision intercept from the extrapolated segment

We want the drift radius coordinate from the segment and the coordinate along the tube form the back extrapolated segment

We want the precision coordinate from the segment and the coordinate along the strip from the back extrapolated segment

Trapezoids and diamonds have their x and y axis swapped

Utility lambda to find the corrct boundary surface on which the start parameters shall be created. The start parameters are linearly extrapolated onto the plane surface. The boundary surface is acceptable if it's in front of the seed position and within the surface bounds.

The extrapolation needs to go backwards and stay within the surface boundaries

Attempt first the propagation towards the bottom boundary surface

If that fails and the volume is alignable, try all the portal surfaces. Volume portals are not sorted in order but the placements are.

Calculate the initial q / p estimator

Definition at line 102 of file MsTrackSeederTool.cxx.

103 {
104 const Acts::GeometryContext tgContext = m_ctxProvider.getGeometryContext(ctx);
105 const Acts::MagneticFieldContext mfContext = m_ctxProvider.getMagneticFieldContext(ctx);
106 MagField::AtlasFieldCache magField{};
107 mfContext.get<const AtlasFieldCacheCondObj*>()->getInitializedCache(magField);
108
109 const xAOD::MuonSegment* refSeg{nullptr};
110 Acts::BoundMatrix cov{Acts::BoundMatrix::Zero()};
111 for (const xAOD::MuonSegment* segment : seed.segments()) {
118 if (!refSeg && !isNswSegment(*segment) &&
119 m_segSelector->passSeedingQuality(ctx, *segment)) {
120 refSeg = segment;
121 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Set reference segment to "<<::print(*segment));
122 }
123 Acts::BoundTrackParameters boundPars = SegmentFit::boundSegmentPars(tgContext, *m_detMgr, *segment);
124 if (!boundPars.covariance()) {
125 continue;
126 }
127 for (int i =0 ; i < cov.cols(); ++i) {
128 cov(i,i) += (*boundPars.covariance())(i,i);
129 }
130
131 }
132 //if we did not find a reference segment let's try the NSW one before we give up on the track
133 if(!refSeg){
134 for (const xAOD::MuonSegment* segment : seed.segments()) {
135 if (isNswSegment(*segment) &&
136 m_segSelector->passSeedingQuality(ctx, *segment)) {
137 refSeg = segment;
138 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - NSW is the best what we have apparently....");
139 break;
140 }
141 }
142 }
143
144 if (!refSeg) {
145 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__
146 <<" - No reference segment passing seeding quality was found.");
147 return Acts::Result<Acts::BoundTrackParameters>::failure(std::make_error_code(std::errc::invalid_argument));
148 }
149 Amg::Vector3D seedPos{atFirstSurface(tgContext, *refSeg)};
150 Amg::Vector3D seedDir{refSeg->direction()};
151 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Initial seed Pos: "<<Amg::toString(seedPos)
152 <<" theta/phi: "<<inDeg(seedPos.theta())<<" / "<<inDeg(seedPos.phi())
153 <<", dir: "<<Amg::toString(seedDir)
154 <<" theta/phi: "<<inDeg(seedDir.theta())<<" / "<<inDeg(seedDir.phi()));
155
156 const xAOD::MuonSegment* frontSegment = seed.segments().front();
157
158 const xAOD::UncalibratedMeasurement* firstMeas {firstMeasurement(*frontSegment)};
159 const Acts::Surface& firstSurf = xAOD::muonSurface(firstMeas);
160 const Acts::GeometryIdentifier volId = volumeId(firstSurf);
161
162 // Find the first measurement
163 const Acts::TrackingVolume* volume{MuonGMR4::highestAlignable(m_trackingGeometrySvc->trackingGeometry()->findVolume(volId))};
164
165 if (!volume) {
166 ATH_MSG_WARNING(__func__<<"() "<<__LINE__
167 <<" - Failed to find tracking volume for seed measurement "<<volId);
168 return Acts::Result<Acts::BoundTrackParameters>::failure(std::make_error_code(std::errc::invalid_argument));
169 }
170 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__
171 <<" - Bounding volume "<<volume->volumeName()
172 <<", trf: "<<Amg::toString(volume->localToGlobalTransform(tgContext))
173 <<", bounds: "<<volume->volumeBounds());
177 if (frontSegment != refSeg) {
178 const Amg::Vector3D frontSegPos = atFirstSurface(tgContext, *frontSegment);
179 const Amg::Transform3D toFirstTrf = firstSurf.localToGlobalTransform(tgContext).inverse();
180 const Amg::Vector3D locFrontSegPos = toFirstTrf * frontSegPos;
181 if (!volume->inside(tgContext, frontSegPos)) {
182 ATH_MSG_WARNING(__func__<<"() "<<__LINE__<<" - Segment "<<::print(*frontSegment)
183 <<" not inside mother volume: "<<volume->volumeName()<<", "
184 <<Amg::toString(volume->globalToLocalTransform(tgContext)*frontSegPos)
185 <<", bounds: "<<volume->volumeBounds()<<", "
186 <<SegmentFit::localSegmentPars(*frontSegment));
187 }
189 {
190 const Amg::Transform3D& toLoc{volume->globalToLocalTransform(tgContext)};
191 const Amg::Vector3D locSeedDir = toLoc.linear() * seedDir;
192 const Amg::Vector3D locFrontDir = toLoc.linear() * frontSegment->direction();
193 seedDir = volume->localToGlobalTransform(tgContext).linear() *
194 Acts::makeDirectionFromAxisTangents(houghTanAlpha(locSeedDir),
195 houghTanBeta(locFrontDir));
196 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Updated seed direction theta/phi: "
197 <<inDeg(seedDir.theta())<<" / "<<inDeg(seedDir.phi()));
198 }
202 const Acts::MultiIntersection firstIsect = firstSurf.intersect(tgContext, seedPos, seedDir,
203 Acts::BoundaryTolerance::Infinite());
204 const Amg::Vector3D locSeedAtFirst = toFirstTrf * firstIsect.at(0).position();
205 if (firstSurf.type() == Acts::Surface::SurfaceType::Straw) {
206 const auto& bounds = static_cast<const Acts::LineBounds&>(firstSurf.bounds());
207 using enum Acts::LineBounds::BoundValues;
210 const Amg::Vector3D locStartPos{locFrontSegPos.x(), locFrontSegPos.y(),
211 std::clamp(locSeedAtFirst.z(), -bounds.get(eHalfLengthZ), bounds.get(eHalfLengthZ))};
212 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - The first surface is a straw "
213 <<bounds<<", track seed @first: "<<Amg::toString(locSeedAtFirst)<<" vs. segment @first: "
214 <<Amg::toString(locFrontSegPos)<<", updated local start pos: "<<Amg::toString(locStartPos));
215 seedPos = firstSurf.localToGlobalTransform(tgContext) * locStartPos;
216 } else if (firstSurf.type() == Acts::Surface::SurfaceType::Plane) {
217 if (isNswSegment(*frontSegment)) {
218 seedPos = frontSegPos;
219 } else {
222 Acts::Vector2 locStartPos2D {locFrontSegPos.x(), locSeedAtFirst.y()};
223 const auto& bounds = firstSurf.bounds();
224 switch (bounds.type()) {
225 case Acts::SurfaceBounds::BoundsType::eRectangle:
226 if (!bounds.inside(locStartPos2D)) {
227 locStartPos2D = bounds.closestPoint(locStartPos2D, Acts::SquareMatrix2::Identity());
228 }
229 break;
230 case Acts::SurfaceBounds::BoundsType::eTrapezoid:
231 case Acts::SurfaceBounds::BoundsType::eDiamond:
233 std::swap(locStartPos2D.x(), locStartPos2D.y());
234 if (!bounds.inside(locStartPos2D)) {
235 locStartPos2D = bounds.closestPoint(locStartPos2D, Acts::SquareMatrix2::Identity());
236 }
237 std::swap(locStartPos2D.x(), locStartPos2D.y());
238 break;
239 default:
240 ATH_MSG_WARNING(__func__<<"() "<<__LINE__<<" - Unexpected surface bounds type "
241 <<firstSurf.bounds().type());
242 return Acts::Result<Acts::BoundTrackParameters>::failure(std::make_error_code(std::errc::invalid_argument));
243 }
244 const Amg::Vector3D locStartPos {locStartPos2D.x(),locStartPos2D.y(), locFrontSegPos.z()};
245
246 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - The first surface is a plane with bounds "
247 <<firstSurf.bounds() <<", track seed @first: "<<Amg::toString(locSeedAtFirst)<<" vs. segment @first: "
248 <<Amg::toString(locFrontSegPos)<<", updated local start pos: "<<Amg::toString(locStartPos));
249 seedPos = firstSurf.localToGlobalTransform(tgContext) * locStartPos;
250 }
251 } else {
252 ATH_MSG_WARNING(__func__<<"() "<<__LINE__<<" - Unexpected surface type "<<firstSurf.type());
253 return Acts::Result<Acts::BoundTrackParameters>::failure(std::make_error_code(std::errc::invalid_argument));
254 }
255 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Updated seed position: "<<Amg::toString(seedPos)
256 <<" theta/phi: "<<inDeg(seedPos.theta())<<" / "<<inDeg(seedPos.phi()));
257 }
258
259
260 auto boundSurf = MuonGMR4::bottomBoundary(*volume);
261 if (!boundSurf) {
262 ATH_MSG_WARNING(__func__<<"() "<<__LINE__<<" - Failed to find boundary surface for tracking volume");
263 return Acts::Result<Acts::BoundTrackParameters>::failure(std::make_error_code(std::errc::invalid_argument));
264 }
265 std::shared_ptr<const Acts::Surface> targetSurf{};
270 auto propagateToBoundary = [&](const Acts::Surface& volBoundary) -> Acts::Result<Amg::Vector3D> {
271
272 const Amg::Transform3D& trf{volBoundary.localToGlobalTransform(tgContext)};
273 using namespace Acts::PlanarHelper;
274 auto pIsect = intersectPlane(seedPos, seedDir, trf.linear().col(Amg::z), trf.translation());
276 if (pIsect.pathLength() > Acts::s_epsilon || !pIsect.isValid()) {
277 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Intersection @"<<Amg::toString(pIsect.position())
278 <<" is forward "<<pIsect.pathLength()<<" or invalid "<<(!pIsect.isValid())
279 <<" within volume "<<volume->inside(tgContext, pIsect.position()));
280 return Acts::Result<Amg::Vector3D>::failure(std::make_error_code(std::errc::invalid_argument));
281 }
282 Acts::Result<Amg::Vector2D> locPos = volBoundary.globalToLocal(tgContext, pIsect.position(),
283 Amg::Vector3D::Zero());
284 if (!locPos.ok()){
285 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Intersection is not on surface "<<
286 Amg::toString(trf.inverse()*pIsect.position()));
287 return Acts::Result<Amg::Vector3D>::failure(std::make_error_code(std::errc::invalid_argument));
288 }
289 if (!volBoundary.insideBounds(*locPos)) {
290 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Intersection is outside the boundaries: "<<
291 Amg::toString(*locPos)<<", bounds: "<<volBoundary.bounds());
292 return Acts::Result<Amg::Vector3D>::failure(std::make_error_code(std::errc::invalid_argument));
293 }
294 targetSurf = volBoundary.getSharedPtr();
295 return Acts::Result<Amg::Vector3D>::success(pIsect.position());
296 };
298 auto pIsect = propagateToBoundary(*boundSurf);
302 if (!pIsect.ok() && volume->isAlignable()) {
303 const Acts::VolumePlacementBase* placement = volume->volumePlacement();
304 for (std::size_t portal = 0; !pIsect.ok() && portal< placement->nPortalPlacements(); ++portal) {
305 pIsect = propagateToBoundary(placement->portalPlacement(portal)->surface());
306 }
307 }
308 if (!pIsect.ok()) {
309 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" Cannot create valid start parameters from seed "<<seed<<".");
310 return Acts::Result<Acts::BoundTrackParameters>::failure(std::make_error_code(std::errc::invalid_argument));
311 }
312 if (!canEstimateQtimesP(seed)) {
313 ATH_MSG_WARNING(__func__<<"() "<<__LINE__<<" Cannot estimate q*p from seed "<<seed
314 <<" - insufficient inner/middle/outer layer coverage.");
315 return Acts::Result<Acts::BoundTrackParameters>::failure(std::make_error_code(std::errc::invalid_argument));
316 }
317 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Extrapolated seed position: "<<Amg::toString(*pIsect)
318 <<" eta/phi: "<<pIsect->eta()<<" / "<<(inDeg(pIsect->phi())));
319
320 const Acts::Vector4 fourPos = ActsTrk::convertPosToActs(*pIsect, (*pIsect).mag() / Gaudi::Units::c_light);
322 const double momRes {seed.location() == Location::Barrel ? m_barrelMomentumRes : m_endcapMomentumRes};
323 const double qOverP = 1./ ActsTrk::energyToActs(estimateQtimesP(tgContext, seed, magField));
324 cov (Acts::eBoundQOverP, Acts::eBoundQOverP) = Acts::square(momRes * qOverP);
325
326 return Acts::BoundTrackParameters::create(tgContext, targetSurf,
327 fourPos, seedDir, qOverP, cov,
328 Acts::ParticleHypothesis::muon());
329 }
#define ATH_MSG_DEBUG(x,...)
ToolHandle< ISegmentSelectionTool > m_segSelector
Pointer to the segement selection tool which compares two segments for their compatibilitiy.
DoubleProperty m_endcapMomentumRes
Momentum resolution in the endcap.
ActsTrk::ContextUtility m_ctxProvider
Utility to fetch the geometry, magnetic field and calibration context in the event.
DoubleProperty m_barrelMomentumRes
Momentum resolution in the barrel.
constexpr double energyToActs(const double athenaE)
Converts an energy scalar from Athena to Acts units.
Acts::Vector4 convertPosToActs(const Amg::Vector3D &athenaPos, const double athenaTime=0.)
Converts a position vector & time from Athena units into Acts units.
@ qOverP
perigee
const Acts::Surface * bottomBoundary(const Acts::TrackingVolume &volume)
Returns the boundary surface parallel to the x-y plane at negative local z.
Acts::BoundTrackParameters boundSegmentPars(const ActsTrk::GeometryContext &gctx, const MuonGMR4::MuonDetectorManager &detMgr, const xAOD::MuonSegment &segment, const Acts::ParticleHypothesis hypot=Acts::ParticleHypothesis::muon())
Returns the segment parameters as boundTrackParameters.
Parameters localSegmentPars(const xAOD::MuonSegment &seg)
Returns the localSegPars decoration from a xAODMuon::Segment.
Amg::Vector3D atFirstSurface(const Acts::GeometryContext &gctx, const xAOD::MuonSegment &segment, const bool skipOutlier=true)
Expresses the segment position on the surface of the first measurement.
double houghTanAlpha(const Amg::Vector3D &v)
: Returns the hough tanAlpha [x] / [z]
void swap(ElementLinkVector< DOBJ > &lhs, ElementLinkVector< DOBJ > &rhs)
UncalibratedMeasurement_v1 UncalibratedMeasurement
Define the version of the uncalibrated measurement class.

◆ expressOnCylinder()

Amg::Vector2D MuonR4::MsTrackSeederTool::expressOnCylinder ( const Acts::GeometryContext & tgContext,
const xAOD::MuonSegment & segment,
const Location loc,
const ExpandedSector sector ) const
finaloverridevirtual

Expresses the passed segment on the virtual cylinder constructed by the track seeder.

The segment's position is projected onto the expanded sector plane using the sensor dircection of the first precision measurement. Then projected position and the direction vector are projected into 2D to intersect with the cylinder surface.

Parameters
tgContextThe geometry context to align the measurement surfaces associated with the segment
segmentThe segment that is to be projected.
locLocation of the projection [barrel/endcap]
sectorPhi sector onto which the segment is moved

extrapolated position

Definition at line 355 of file MsTrackSeederTool.cxx.

358 {
360 const Amg::Vector3D pos{segPosOntoPhiPlane(tgContext, sector.normalDir(), segment)};
361 const Amg::Vector3D dir{segment.direction()};
362
363 const Amg::Vector2D projPos{pos.perp(), pos.z()};
364 const Amg::Vector2D projDir{dir.perp(), dir.z()};
365
366 ATH_MSG_VERBOSE( "segment position:" << Amg::toString(segment.position())
367 << ", direction: " << Amg::toString(segment.direction()) );
368 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Express segment in @"<<Amg::toString(pos)
369 <<", direction: "<<Amg::toString(dir)<< " sector projector: " << sector
370 << " location: " << Acts::toUnderlying(loc));
371 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Projected position onto sector: "<<Amg::toString(projPos)
372 <<", projected direction: "<<Amg::toString(projDir));
373
374 double lambda{0.};
375 if (Location::Barrel == loc) {
376 lambda = Amg::intersect<2>(projPos, projDir, Amg::Vector2D::UnitX(),
377 m_barrelRadius).value_or(10. * Gaudi::Units::km);
378 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Intersect with barrel at radius: "<<m_barrelRadius<<" --> "<<Amg::toString(projPos + lambda * projDir));
379 } else {
380 lambda = Amg::intersect<2>(projPos, projDir, Amg::Vector2D::UnitY(),
381 Acts::copySign(1.*m_endcapDiscZ, projPos[1])).value_or(10. * Gaudi::Units::km);
382 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Intersect with endcap at z: "<<Acts::copySign(1.*m_endcapDiscZ, projPos[1])
383 <<" --> "<<Amg::toString(projPos + lambda * projDir));
384 }
385 return projPos + lambda * projDir;
386 }
DoubleProperty m_barrelRadius
The radius of he barrel cylinder.
DoubleProperty m_endcapDiscZ
Position of the endcap discs.
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.

◆ findTrackSeeds()

StatusCode MuonR4::MsTrackSeederTool::findTrackSeeds ( const EventContext & ctx,
std::vector< MsTrackSeed > & outSeeds ) const
finaloverridevirtual

Retrieves the segment container from StoreGate and constructs TrackSeeds from them.

The seed canddiates are pushed to the output seed container

Parameters
ctxEventContext to access the xAOD::MuonSegmentContainer from store gate and additional conditions data if needed to construct the seed
outSeedsMutable reference to the container to whichh the seeds are pushed to.

Bad segment not suitable for track seeding or the segment coordinates are just mirrored at the overlap between sector 1 -> 16

Define the search range.

Ensure that only endcap / barrel seeds are considered. The values are integers -> add tiny margin

Move 25 cm along the projected plane

Include the neighbouring sectors

Using the cube above, let the tree search for all compatible segments

Ensure that the sector overlap and momentum vectors are compatible with a MS trajectory

No segments were combined

Calculate the seed's position

Definition at line 694 of file MsTrackSeederTool.cxx.

695 {
696
697 const xAOD::MuonSegmentContainer* segments{nullptr};
698 ATH_CHECK(SG::get(segments, m_segmentKey , ctx));
699 const Acts::GeometryContext tgContext = m_ctxProvider.getGeometryContext(ctx);
700 SearchTree_t orderedSegs{constructTree(tgContext, *segments)};
701 MsTrackSeedContainer trackSeeds{};
702 using enum SeedCoords;
703 for (const auto& [coords, seedCandidate] : orderedSegs) {
706 if (!m_segSelector->passSeedingQuality(ctx, *seedCandidate)){
707 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Segment "<<::print(*seedCandidate)<<" does not pass the seeding quality.");
708 continue;
709 }
711 SearchTree_t::range_t selectRange{};
714 selectRange[Acts::toUnderlying(eDetSection)].shrink(coords[Acts::toUnderlying(eDetSection)] - 0.1,
715 coords[Acts::toUnderlying(eDetSection)] + 0.1);
717 selectRange[Acts::toUnderlying(ePosOnCylinder)].shrink(coords[Acts::toUnderlying(ePosOnCylinder)] - m_seedHalfLength,
718 coords[Acts::toUnderlying(ePosOnCylinder)] + m_seedHalfLength);
720 selectRange[Acts::toUnderlying(eSector)].shrink(coords[Acts::toUnderlying(eSector)] -0.25,
721 coords[Acts::toUnderlying(eSector)] +0.25);
722
723 MsTrackSeed newSeed{static_cast<Location>(std::abs(coords[Acts::toUnderlying(eDetSection)])),
724 ExpandedSector{static_cast<std::int8_t>(coords[Acts::toUnderlying(eSector)])}};
726 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Search for compatible segments to "<<::print(*seedCandidate)<<".");
727 orderedSegs.rangeSearchMapDiscard(selectRange, [&](
728 const SearchTree_t::coordinate_t& /*coords*/,
729 const xAOD::MuonSegment* extendWithMe) {
731 if (!m_segSelector->compatibleForTrack(ctx, *seedCandidate, *extendWithMe)) {
732 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Segment "<<::print(*extendWithMe)<<" is not compatible.");
733 return;
734 }
735 auto itr = std::ranges::find_if(newSeed.segments(), [extendWithMe](const xAOD::MuonSegment* onSeed){
736 return extendWithMe->chamberIndex() == onSeed->chamberIndex();
737 });
738 if (itr == newSeed.segments().end()){
739 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Add segment "<<::print(*extendWithMe)<<" to seed.");
740 newSeed.addSegment(extendWithMe);
741 }
742 else if (reducedChi2(**itr) > reducedChi2(*extendWithMe) &&
743 (*itr)->nPhiLayers() <= extendWithMe->nPhiLayers()) {
744
745 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Replace segment "<<::print(**itr)<<" with "
746 <<::print(*extendWithMe)<<" on seed due to better chi2.");
747 newSeed.replaceSegment(*itr, extendWithMe);
748 }
749 });
751 if (newSeed.segments().empty()) {
752 continue;
753 }
754
755 newSeed.addSegment(seedCandidate);
756
757 // Let's check if we build a single station seed and if yes reject it.
758 using namespace Muon::MuonStationIndex;
759 if(toLayerIndex(newSeed.segments().front()->chamberIndex()) == toLayerIndex(newSeed.segments().back()->chamberIndex())){
760 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Reject seed with segments in the same station.");
761 continue;
762 }
763
764 //Check if we have multiple segments from the same station, if so split the seed and create duplicate seeds
765
767 const double r = newSeed.location() == Location::Barrel ? 1.*m_barrelRadius
768 : coords[Acts::toUnderlying(ePosOnCylinder)];
769 const double z = newSeed.location() == Location::Barrel ? coords[Acts::toUnderlying(ePosOnCylinder)]
770 : coords[Acts::toUnderlying(eDetSection)]* m_endcapDiscZ;
771
772 Amg::Vector3D pos = r * newSeed.sector().radialDir()
773 + z * Amg::Vector3D::UnitZ();
774
775 newSeed.setPosition(std::move(pos));
776 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Add new seed "<<newSeed);
777 trackSeeds.emplace_back(std::move(newSeed));
778 }
779 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Found in total "<<trackSeeds.size()<<" before overlap removal");
780 // outputSeeds
781 trackSeeds = resolveOverlaps(std::move(trackSeeds));
782 outputSeeds.insert(outputSeeds.end(), std::make_move_iterator(trackSeeds.begin()),
783 std::make_move_iterator(trackSeeds.end()));
784 return StatusCode::SUCCESS;
785 }
#define ATH_CHECK
Evaluate an expression and check for errors.
#define z
SG::ReadHandleKey< xAOD::MuonSegmentContainer > m_segmentKey
Declare the data dependency on the standard Mdt+Rpc+Tgc segment container & on the NSW segment contai...
MsTrackSeed::Location Location
Enum toggling whether the segment is in the endcap or barrel.
SearchTree_t constructTree(const Acts::GeometryContext &tgContext, const xAOD::MuonSegmentContainer &segments) const
Construct a complete search tree from a MuonSegment container.
MsTrackSeedContainer resolveOverlaps(MsTrackSeedContainer &&unresolved) const
Removes exact duplciates or partial subsets of the MsTrackSeeds.
DoubleProperty m_seedHalfLength
Maximum separation of point on the cylinder to be picked up onto a seed.
int r
Definition globals.cxx:22
float reducedChi2(const xAOD::MuonSegment &segment)
std::vector< MsTrackSeed > MsTrackSeedContainer
Definition MsTrackSeed.h:96
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
MuonSegmentContainer_v1 MuonSegmentContainer
Definition of the current "MuonSegment container version".

◆ forceIntegration()

Amg::Vector3D MuonR4::MsTrackSeederTool::forceIntegration ( const PosMomPair_t & point1,
const PosMomPair_t & point2,
const Amg::Vector3D & planeNorm,
MagField::AtlasFieldCache & fieldCache ) const
private

Compute the integral of magnetic force (v x B ) dS along a trajectory, given the initial and final positions and directions of the trajectory.

The trajectory is approximated as a straight line between the two positions, and the magnetic field is evaluated at several points along this line.

Parameters
point1Initial position and direction
point2Final position and direction
planeNormNormal vector of the bending plane used for the momentum estimation, used to extract the orthogonal component to the field
fieldCacheMagnetic field cache
Returns
: Integral of magnetic force

Definition at line 490 of file MsTrackSeederTool.cxx.

493 {
494 const auto& [pos1, dir1] = point1;
495 const auto& [pos2, dir2] = point2;
496 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Integrate field from "<<Amg::toString(pos1)
497 <<" to "<<Amg::toString(pos2)<<", direction changing from "
498 <<inDeg(dir1.theta())<<" / "<<inDeg(dir1.phi())<<" to "
499 <<inDeg(dir2.theta())<<" / "<<inDeg(dir2.phi()));
500
501 Amg::Vector3D locField{Amg::Vector3D::Zero()};
502 Amg::Vector3D accumForce{Amg::Vector3D::Zero()};
503 for (double fieldStep : m_fieldExtpSteps) {
504 const Amg::Vector3D extPos {(1. - fieldStep) * pos1 + fieldStep * pos2};
505 const Amg::Vector3D extDir {((1. - fieldStep) * dir1 + fieldStep * dir2).unit()};
506
507 fieldCache.getField(extPos.data(), locField.data());
508 const Amg::Vector3D locForce {locField.dot(planeNorm) * extDir.cross(planeNorm)};
509 accumForce += locForce;
510
511 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - step: "<<fieldStep
512 <<", pos: "<<Amg::toString(extPos)<<", dir: "<<inDeg(extDir.theta())
513 <<" / "<<inDeg(extDir.phi())<<" --> local |B|: "<<locField.mag()*1e3
514 <<" [T]"<<", |Bnorm|: "<<locField.dot(planeNorm)*Gaudi::Units::GeV
515 <<" [T], local |v x Bnorm|: "<<locForce.mag()*Gaudi::Units::GeV<<" [T].");
516 }
517 const double dS {(pos2 - pos1).mag() / static_cast<double>(m_fieldExtpSteps.size())};
518 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Integrated force: "<<Amg::toString(accumForce)<<", dS: "<<dS);
519 return accumForce * dS;
520 }
Scalar mag() const
mag method
void getField(const double *ATH_RESTRICT xyz, double *ATH_RESTRICT bxyz, double *ATH_RESTRICT deriv=nullptr)
get B field value at given position xyz[3] is in mm, bxyz[3] is in kT if deriv[9] is given,...
std::vector< double > m_fieldExtpSteps
The list of field steps in the force field integration.

◆ getPtimesQ()

double MuonR4::MsTrackSeederTool::getPtimesQ ( const Amg::Vector3D & forceIntegral,
const Amg::Vector3D & deltaDir ) const
private

Compute the charge times momentum from the integral of lorentz force and the total change in direction.

Parameters
forceIntegralCumulative lorentz force
deltaDirChange in direction
Returns
: Estimated Q*P value

Definition at line 521 of file MsTrackSeederTool.cxx.

522 {
523 const double PtimesQ {0.3 * Gaudi::Units::GeV * forceIntegral.mag2() / deltaDir.dot(forceIntegral)};
524
525 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - estimateQtimesP() force integral: "<<forceIntegral.mag()<<" [T*m], deltaDir: "
526 <<deltaDir.mag()<<", cos: "<<deltaDir.dot(forceIntegral)/ (deltaDir.mag() * forceIntegral.mag())
527 <<", PtimesQ: "<<PtimesQ/Gaudi::Units::GeV <<" [GeV].");
528 return PtimesQ;
529 }

◆ initialize()

StatusCode MuonR4::MsTrackSeederTool::initialize ( )
finaloverridevirtual


Initialize the field extraction steps

Definition at line 82 of file MsTrackSeederTool.cxx.

82 {
83 ATH_CHECK(m_ctxProvider.initialize());
84 ATH_CHECK(m_segSelector.retrieve());
86 ATH_CHECK(m_segmentKey.initialize(!m_segmentKey.empty()));
87 ATH_CHECK(detStore()->retrieve(m_detMgr));
88
89 if (m_nFieldSteps == 0) {
90 ATH_MSG_ERROR("The number of field steps must not be zero "<<m_nFieldSteps);
91 return StatusCode::FAILURE;
92 }
94 const double stepSize {1. / m_nFieldSteps};
95 for (std::size_t i = 0; i < m_nFieldSteps; ++i) {
96 m_fieldExtpSteps.push_back((static_cast<double>(i) + 0.5) * stepSize);
97 }
98 return StatusCode::SUCCESS;
99 }
#define ATH_MSG_ERROR(x,...)
UnsignedIntegerProperty m_nFieldSteps
number of steps between two segments to integrate the magnetic field

◆ resolveOverlaps()

MsTrackSeedContainer MuonR4::MsTrackSeederTool::resolveOverlaps ( MsTrackSeedContainer && unresolved) const
private

Removes exact duplciates or partial subsets of the MsTrackSeeds.

Parameters
unresolvedInput MsTrackSeedContainer with duplicates

Resort the seeds starting from the ones with the most segments to the lowest

Definition at line 787 of file MsTrackSeederTool.cxx.

787 {
788
790 std::ranges::sort(unresolved, [](const MsTrackSeed& a, const MsTrackSeed&b) {
791 return a.segments().size() > b.segments().size();
792 });
793 MsTrackSeedContainer outputSeeds{};
794 outputSeeds.reserve(unresolved.size());
795 std::ranges::copy_if(std::move(unresolved), std::back_inserter(outputSeeds),
796 [&outputSeeds](const MsTrackSeed& testMe) {
797 for (const MsTrackSeed& good : outputSeeds){
798 if (!testMe.sector().isNeighbour(good.sector())) {
799 continue;
800 }
801 const std::size_t sharedSegs = std::ranges::count_if(testMe.segments(),
802 [&good](const xAOD::MuonSegment* segInTest){
803 return Acts::rangeContainsValue(good.segments(), segInTest);
804 });
805 if (sharedSegs == testMe.segments().size()) {
806 return false;
807 }
808 }
809 return true;
810 });
811
812 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Found in total "<<outputSeeds.size()<<" after overlap removal");
813 return outputSeeds;
814 }
static Double_t a

◆ segPosOntoPhiPlane()

Amg::Vector3D MuonR4::MsTrackSeederTool::segPosOntoPhiPlane ( const Acts::GeometryContext & tgContext,
const Amg::Vector3D & planeNormal,
const xAOD::MuonSegment & segment ) const
private

Projects the segment position onto the plane with global phi = x The local coordinate system is arranged such that the x-axis is co-linear to the phi direction.

The segment is moved along the MDT's wire direction in that sector.

Parameters
planeNormNormal of the phi plane onto which the segment is projected
sectorSector of the segment to be projected, needed to find the wire direction
posToProjectPosition to project

Fall back function for the truth segment test

Definition at line 330 of file MsTrackSeederTool.cxx.

332 {
333 const std::size_t nMeas = nMeasurements(segment);
334 Amg::Vector3D wireDir{Amg::Vector3D::Zero()};
335
336 for (std::size_t meas = 0; meas < nMeas; ++ meas) {
337 if (isOutlierMeasurement(segment, meas)) {
338 continue;
339 }
340 const xAOD::UncalibratedMeasurement* measPtr = getMeasurement(segment, meas);
341 if (xAOD::isPrecisionHit(measPtr)) {
342 wireDir = m_trackingGeometrySvc->trackingGeometry()->findVolume(volumeId(xAOD::muonSurface(measPtr)))->
343 localToGlobalTransform(tgContext).linear().col(Amg::x);
344 break;
345 }
346 }
348 if (nMeas == 0ul) {
349 wireDir = envelope(segment)->surface().localToGlobalTransform(tgContext).linear().col(Amg::x);
350 }
351
352 return Acts::PlanarHelper::intersectPlane(segment.position(), wireDir,
353 planeNormal, Amg::Vector3D::Zero()).position();
354 }
const Acts::PlaneSurface & surface() const
Returns the associated surface.
const MuonGMR4::SpectrometerSector * envelope(const xAOD::MuonSegment &segment) const
Returns the spectrometer envelope associated to the segment (Coord system where the parameter are exp...
const xAOD::UncalibratedMeasurement * getMeasurement(const xAOD::MuonSegment &segment, const std::size_t n)
Returns the n-th uncalibrated measurement.
bool isOutlierMeasurement(const xAOD::MuonSegment &segment, const std::size_t n)
Returns whether the n-the uncalibrated measurement is an outlier.
bool isPrecisionHit(const UncalibratedMeasurement *meas)
Returns whether the measurement is a precision hit.

◆ withinBounds()

bool MuonR4::MsTrackSeederTool::withinBounds ( const Amg::Vector2D & projPos,
const Location loc ) const
finaloverridevirtual

Definition at line 387 of file MsTrackSeederTool.cxx.

388 {
389 using enum Location;
390 if (loc == Barrel && std::abs(projPos[1]) > std::min(m_endcapDiscZ, m_barrelLength)) {
391 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Position "<<Amg::toString(projPos)<<
392 " exceeds cylinder boundaries ("<<(1.*m_barrelRadius)<<", "
393 <<std::min(m_endcapDiscZ, m_barrelLength)<<")");
394 return false;
395 } else if (loc == Endcap && (0 > projPos[0] || projPos[0] > m_endcapDiscRadius)) {
396 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Position "<<Amg::toString(projPos)<<
397 " exceeds endcap boundaries ("<<(1.*m_endcapDiscRadius)<<", "<<Acts::copySign(1.*m_endcapDiscZ, projPos[1])<<")");
398 return false;
399 }
400 return true;
401 }
DoubleProperty m_endcapDiscRadius
Radius of the endcap discs.
DoubleProperty m_barrelLength
The maximum length of the barrel cylinder, if not capped by the placement of the endcap discs.

Member Data Documentation

◆ m_barrelLength

DoubleProperty MuonR4::MsTrackSeederTool::m_barrelLength {this, "BarrelLength", 25.*Gaudi::Units::m}
private

The maximum length of the barrel cylinder, if not capped by the placement of the endcap discs.

Definition at line 183 of file MsTrackSeederTool.h.

183{this, "BarrelLength", 25.*Gaudi::Units::m};

◆ m_barrelMomentumRes

DoubleProperty MuonR4::MsTrackSeederTool::m_barrelMomentumRes {this, "BarrelMomentumResolution", 0.05}
private

Momentum resolution in the barrel.

Definition at line 192 of file MsTrackSeederTool.h.

192{this, "BarrelMomentumResolution", 0.05};

◆ m_barrelRadius

DoubleProperty MuonR4::MsTrackSeederTool::m_barrelRadius {this, "BarrelRadius", 7.*Gaudi::Units::m}
private

The radius of he barrel cylinder.

Definition at line 180 of file MsTrackSeederTool.h.

180{this, "BarrelRadius", 7.*Gaudi::Units::m};

◆ m_ctxProvider

ActsTrk::ContextUtility MuonR4::MsTrackSeederTool::m_ctxProvider {this}
private

Utility to fetch the geometry, magnetic field and calibration context in the event.

Definition at line 204 of file MsTrackSeederTool.h.

204{this};

◆ m_detMgr

const MuonGMR4::MuonDetectorManager* MuonR4::MsTrackSeederTool::m_detMgr {nullptr}
private

Definition at line 209 of file MsTrackSeederTool.h.

209{nullptr};

◆ m_endcapDiscRadius

DoubleProperty MuonR4::MsTrackSeederTool::m_endcapDiscRadius {this, "EndcapRadius", 13.*Gaudi::Units::m}
private

Radius of the endcap discs.

Definition at line 187 of file MsTrackSeederTool.h.

187{this, "EndcapRadius", 13.*Gaudi::Units::m};

◆ m_endcapDiscZ

DoubleProperty MuonR4::MsTrackSeederTool::m_endcapDiscZ {this, "EndcapDiscZ", 15.*Gaudi::Units::m}
private

Position of the endcap discs.

Definition at line 185 of file MsTrackSeederTool.h.

185{this, "EndcapDiscZ", 15.*Gaudi::Units::m};

◆ m_endcapMomentumRes

DoubleProperty MuonR4::MsTrackSeederTool::m_endcapMomentumRes {this, "EndcapMomentumResolution", 0.1}
private

Momentum resolution in the endcap.

Definition at line 194 of file MsTrackSeederTool.h.

194{this, "EndcapMomentumResolution", 0.1};

◆ m_fieldExtpSteps

std::vector<double> MuonR4::MsTrackSeederTool::m_fieldExtpSteps {}
private

The list of field steps in the force field integration.

Definition at line 178 of file MsTrackSeederTool.h.

178{};

◆ m_nFieldSteps

UnsignedIntegerProperty MuonR4::MsTrackSeederTool::m_nFieldSteps {this, "nFieldSteps", 10}
private

number of steps between two segments to integrate the magnetic field

Definition at line 196 of file MsTrackSeederTool.h.

196{this, "nFieldSteps", 10};

◆ m_seedHalfLength

DoubleProperty MuonR4::MsTrackSeederTool::m_seedHalfLength {this, "SeedHalfLength", 25.*Gaudi::Units::cm}
private

Maximum separation of point on the cylinder to be picked up onto a seed.

Definition at line 190 of file MsTrackSeederTool.h.

190{this, "SeedHalfLength", 25.*Gaudi::Units::cm};

◆ m_segmentKey

SG::ReadHandleKey<xAOD::MuonSegmentContainer> MuonR4::MsTrackSeederTool::m_segmentKey {this, "SegmentContainer", "MuonSegmentsFromR4" }
private

Declare the data dependency on the standard Mdt+Rpc+Tgc segment container & on the NSW segment container.

Definition at line 207 of file MsTrackSeederTool.h.

207{this, "SegmentContainer", "MuonSegmentsFromR4" };

◆ m_segSelector

ToolHandle<ISegmentSelectionTool> MuonR4::MsTrackSeederTool::m_segSelector {this, "SegmentSelectionTool" , "" }
private

Pointer to the segement selection tool which compares two segments for their compatibilitiy.

Definition at line 199 of file MsTrackSeederTool.h.

199{this, "SegmentSelectionTool" , "" };

◆ m_trackingGeometrySvc

ServiceHandle<ActsTrk::ITrackingGeometrySvc> MuonR4::MsTrackSeederTool::m_trackingGeometrySvc {this, "TrackingGeometrySvc", "ActsTrackingGeometrySvc"}
private

Tracking geometry tool.

Definition at line 201 of file MsTrackSeederTool.h.

201{this, "TrackingGeometrySvc", "ActsTrackingGeometrySvc"};

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