8#include "Acts/Utilities/Helpers.hpp"
9#include "Acts/Definitions/Tolerance.hpp"
10#include "Acts/Definitions/Units.hpp"
14 using namespace Acts::UnitLiterals;
16 constexpr bool chargeAgree(
double PtimesQ1,
double PtimesQ2) {
17 return PtimesQ1 * PtimesQ2 > 0;
20 double momentumDev(
double PtimesQ1,
double PtimesQ2) {
21 const double denom {std::max(std::abs(PtimesQ1) + std::abs(PtimesQ2), Acts::s_epsilon)};
22 return std::abs(PtimesQ1 - PtimesQ2) /
denom;
28 return std::format(
"{:}, nPrecHits: {:}, nPhiHits: {:}",
MuonR4::printID(seg),
40 const double stepSize {1. /
static_cast<double>(
m_cfg.nFieldSteps)};
41 for (std::size_t i = 0; i <
m_cfg.nFieldSteps; ++i) {
52 return Acts::PlanarHelper::intersectPlane(
53 posToProject, projDir, planeNorm, Amg::Vector3D::Zero()).position();
57 return (dirToProject - dirToProject.dot(planeNorm) * planeNorm).unit();
72 <<
", direction: "<<
Amg::toString(dir)<<
" sector projector: " << sector
73 <<
" location: " << Acts::toUnderlying(loc));
78 if (Location::Barrel == loc) {
80 m_cfg.barrelRadius).value_or(10. * Gaudi::Units::km);
84 Acts::copySign(
m_cfg.endcapDiscZ, projPos[1])).value_or(10. * Gaudi::Units::km);
85 ATH_MSG_VERBOSE(__func__<<
"() "<<__LINE__<<
" - Intersect with endcap at z: "<<Acts::copySign(
m_cfg.endcapDiscZ, projPos[1])<<
" --> "<<
Amg::toString(projPos + lambda * projDir));
87 return projPos + lambda * projDir;
92 if (loc ==
Barrel && std::abs(projPos[1]) > std::min(
m_cfg.endcapDiscZ,
m_cfg.barrelLength)) {
94 " exceeds cylinder boundaries ("<<
m_cfg.barrelRadius<<
", "
95 <<std::min(
m_cfg.endcapDiscZ,
m_cfg.barrelLength)<<
")");
97 }
else if (loc == Endcap && (0 > projPos[0] || projPos[0] >
m_cfg.endcapDiscRadius)) {
99 " exceeds endcap boundaries ("<<
m_cfg.endcapDiscRadius<<
", "<<Acts::copySign(
m_cfg.endcapDiscZ, projPos[1])<<
")");
127 std::vector<Estimate> estimates{};
128 const double weightNorm {force12.mag() + force23.mag()};
130 estimates.emplace_back(
getPtimesQ(force12, p2.second - p1.second), force12.mag()/weightNorm, 0.);
131 estimates.emplace_back(
getPtimesQ(force23, p3.second - p2.second), force23.mag()/weightNorm, 0.);
133 const double weight13 {force13.mag()/weightNorm};
134 estimates.emplace_back(
getPtimesQ(force13, p3.second - p1.second), weight13, 0.);
137 estimates.emplace_back(
getPtimesQ(force13, t23 - t12), weight13, 0.);
139 for (std::size_t i {0}; i < estimates.size(); ++i) {
140 Estimate& est1 {estimates[i]};
141 for (std::size_t j {i+1}; j < estimates.size(); ++j) {
142 Estimate& est2 {estimates[j]};
144 double w {std::min(est1.weight, est2.weight)};
145 double chargeScore {chargeAgree(est1.PtimesQ, est2.PtimesQ) ? 1. : -1.};
146 double pDevPenalty {momentumDev(est1.PtimesQ, est2.PtimesQ)};
148 est1.score += w * (chargeScore - pDevPenalty);
149 est2.score += w * (chargeScore - pDevPenalty);
152 if (
msgLvl(MSG::VERBOSE)) {
153 std::vector<std::string> names {
"Pair01",
"Pair12",
"Pair02Seg",
"Pair02Pos"};
154 for (
const auto& [i, est] : Acts::enumerate(estimates)) {
155 ATH_MSG_VERBOSE(__func__<<
"() Estimate "<<names[i]<<
": PtimesQ: "<<est.PtimesQ*1e-3
156 <<
", weight: "<<est.weight<<
", score: "<<est.score);
161 const Estimate& bestEstimate {*std::ranges::max_element(estimates,
162 std::ranges::less{}, &Estimate::score)};
163 const double charge {std::copysign(1., bestEstimate.PtimesQ)};
165 double totalSum {0.}, totalWeight {0.};
166 for (
const Estimate& est : estimates) {
167 if (chargeAgree(est.PtimesQ,
charge)) {
168 totalSum += est.PtimesQ * est.weight;
169 totalWeight += est.weight;
172 assert(totalWeight > Acts::s_epsilon);
173 return totalSum / totalWeight;
183 p2.second - p1.second);
189 const auto& [pos1, dir1] = point1;
190 const auto& [pos2, dir2] = point2;
195 const Amg::Vector3D extPos {(1. - fieldStep) * pos1 + fieldStep * pos2};
196 const Amg::Vector3D extDir {((1. - fieldStep) * dir1 + fieldStep * dir2).
unit()};
198 fieldCache.
getField(extPos.data(), locField.data());
199 const Amg::Vector3D locForce {locField.dot(planeNorm) * extDir.cross(planeNorm)};
200 accumForce += locForce;
204 <<
" --> local |B|: "<<locField.mag()*1e3 <<
" [T]"<<
", |Bnorm|: "<<locField.dot(planeNorm)*1e3
205 <<
" [T], local |v x Bnorm|: "<<locForce.mag()*1e3<<
" [T].");
208 return accumForce * dS;
212 const double PtimesQ {0.3 * Gaudi::Units::GeV * forceIntegral.mag2() / deltaDir.dot(forceIntegral)};
214 ATH_MSG_VERBOSE(
"estimateQtimesP() force integral: "<<forceIntegral.mag()<<
" [T*m], deltaDir: "
215 <<deltaDir.mag()<<
", cos: "<<deltaDir.dot(forceIntegral)/ (deltaDir.mag() * forceIntegral.mag())
216 <<
", PtimesQ: "<<PtimesQ/Gaudi::Units::GeV <<
" [GeV].");
226 double deltaPhiAcc {0.};
227 std::optional<double> centralPhi {};
228 unsigned nSegsWithPhi{0};
230 if (segment->nPhiLayers() > 0) {
231 if (!centralPhi) centralPhi = segment->position().phi();
236 const double circPhi {nSegsWithPhi > 0
238 : seed.sector().phi()};
240 std::array<const xAOD::MuonSegment*, 3> segmentsToUse{};
248 segmentsToUse[0] = segment;
253 segmentsToUse[1] = segment;
258 segmentsToUse[2] = segment;
265 unsigned nSegments = std::ranges::count_if(segmentsToUse,
270 auto missingSeg = std::ranges::find(segmentsToUse,
nullptr);
276 assert(missingSeg != segmentsToUse.end());
277 *missingSeg = segment;
279 if (nSegments == 3) {
282 missingSeg = std::ranges::find(segmentsToUse,
nullptr);
285 if (!seg1 || !seg2) {
286 return seg1 !=
nullptr;
291 const Amg::Vector3D planeNorm {Acts::makeDirectionFromPhiTheta(circPhi + 90._degree, 90._degree)};
296 return nSegments == 3
297 ?
estimateQtimesP(magField, planeNorm, point(segmentsToUse[0]), point(segmentsToUse[1]), point(segmentsToUse[2]))
298 :
estimateQtimesP(magField, planeNorm, point(segmentsToUse[0]), point(segmentsToUse[1]));
304 const unsigned segSector = segment->
sector();
305 for (
const auto proj : {SectorProjector::leftOverlap, SectorProjector::center, SectorProjector::rightOverlap}) {
310 <<
" is not in sector "<<projSector);
318 std::array<double, 3> coords{};
325 coords[Acts::toUnderlying(
eDetSection)] = Acts::copySign(Acts::toUnderlying(loc), refPoint[1]);
327 coords[Acts::toUnderlying(
ePosOnCylinder)] = refPoint[Location::Barrel == loc];
330 <<
" with "<<coords<<
" to the search tree");
331 outContainer.emplace_back(std::move(coords), segment);
336 rawData.reserve(3*segments.
size());
341 ATH_MSG_VERBOSE(
"Create a new tree with "<<rawData.size()<<
" entries. ");
349 for (
const auto& [coords, seedCandidate] : orderedSegs) {
353 if (!
m_cfg.selector->passSeedingQuality(ctx, *recoSeedCandidate)){
358 SearchTree_t::range_t selectRange{};
367 selectRange[Acts::toUnderlying(
eSector)].shrink(coords[Acts::toUnderlying(
eSector)] -0.25,
368 coords[Acts::toUnderlying(
eSector)] +0.25);
374 orderedSegs.rangeSearchMapDiscard(selectRange, [&](
375 const SearchTree_t::coordinate_t& ,
379 if (!
m_cfg.selector->compatibleForTrack(ctx, *recoSeedCandidate, *extendCandidate)) {
384 return extendWithMe->chamberIndex() == onSeed->chamberIndex();
386 if (itr == newSeed.
segments().end()){
390 else if (reducedChi2(**itr) > reducedChi2(*extendWithMe) &&
391 (*itr)->nPhiLayers() <= extendWithMe->
nPhiLayers()) {
394 <<
" on seed due to better chi2.");
415 const double r = newSeed.
location() == Location::Barrel ?
m_cfg.barrelRadius
421 +
z * Amg::Vector3D::UnitZ();
425 trackSeeds.emplace_back(std::move(newSeed));
427 ATH_MSG_VERBOSE(
"Found in total "<<trackSeeds.size()<<
" before overlap removal");
430 std::unique_ptr<MsTrackSeedContainer>
435 return a.segments().size() > b.segments().size();
437 auto outputSeeds = std::make_unique<MsTrackSeedContainer>();
438 outputSeeds->reserve(unresolved.size());
439 std::ranges::copy_if(std::move(unresolved), std::back_inserter(*outputSeeds),
445 const std::size_t sharedSegs = std::ranges::count_if(testMe.
segments(),
447 return std::ranges::find(good.segments(), segInTest) !=
448 good.segments().end();
450 if (sharedSegs == testMe.
segments().size()) {
457 ATH_MSG_VERBOSE(
"Found in total "<<outputSeeds->size()<<
" after overlap removal");
Scalar mag() const
mag method
const PlainObject unit() const
This is a plugin that makes Eigen look like CLHEP & defines some convenience methods.
#define ATH_MSG_VERBOSE(x)
double charge(const T &p)
void print(char *figname, TCanvas *c1)
bool msgLvl(const MSG::Level lvl) const
Test the output level.
AthMessaging(IMessageSvc *msgSvc, const std::string &name)
Constructor.
void getInitializedCache(MagField::AtlasFieldCache &cache) const
get B field cache for evaluation as a function of 2-d or 3-d position.
size_type size() const noexcept
Returns the number of elements in the collection.
Local cache for magnetic field (based on MagFieldServices/AtlasFieldSvcTLS.h).
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,...
Amg::Vector3D normalDir() const
Returns the vector that is normal to the plane spanned by the expanded sector.
@ center
Project the segment onto the overlap with the previous sector.
std::int8_t sector() const
Returns the expanded sector number.
bool isNeighbour(const ExpandedSector &other) const
Amg::Vector3D radialDir() const
Returns the vector pointing radially along the sector plane.
void addSegment(const xAOD::MuonSegment *seg)
Append a segment to the seed.
Location location() const
Returns the location of the seed.
ExpandedSector sector() const
Returns the seed's sector.
const std::vector< const xAOD::MuonSegment * > & segments() const
Returns the vector of associated segments.
void replaceSegment(const xAOD::MuonSegment *exist, const xAOD::MuonSegment *updated)
Replaces an already added segment in the seed with a better suited one.
void setPosition(Amg::Vector3D &&pos)
set the seed's position
bool withinBounds(const Amg::Vector2D &projPos, const Location loc) const
Returns whether the expression on the cylinder is within the surface bounds.
double estimateQtimesP(const AtlasFieldCacheCondObj &magField, const Amg::Vector3D &planeNorm, const PosMomPair_t &p1, const PosMomPair_t &p2, const PosMomPair_t &p3) const
Estimate the charge times momentum of a muon candidate when three points are available.
SearchTree_t::vector_t TreeRawVec_t
Abbrivation of the KDTree raw data vector.
std::unique_ptr< MsTrackSeedContainer > resolveOverlaps(MsTrackSeedContainer &&unresolved) const
Removes exact duplciates or partial subsets of the MsTrackSeeds.
std::pair< Amg::Vector3D, Amg::Vector3D > PosMomPair_t
static Amg::Vector3D segPosOntoPhiPlane(const Amg::Vector3D &planeNorm, const int Sector, const Amg::Vector3D &posToProject)
Projects the segment position onto the plane with global phi = x The local coordinate system is arran...
Acts::KDTree< 3, const xAOD::MuonSegment *, double, std::array, 6 > SearchTree_t
Definition of the search tree class.
static Amg::Vector3D segDirOntoPhiPlane(const Amg::Vector3D &planeNorm, const Amg::Vector3D &dirToProject)
Projects the segment direction onto the plane with global phi = x by removing the component orthogona...
void appendSegment(const xAOD::MuonSegment *segment, const Location loc, TreeRawVec_t &outContainer) const
Append the to the raw data container.
MsTrackSeeder(const std::string &msgName, Config &&cfg)
Standard constructor.
Amg::Vector2D expressOnCylinder(const xAOD::MuonSegment &segment, const Location loc, const ExpandedSector sector) const
Expresses the segment on the cylinder surface.
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...
MsTrackSeed::Location Location
Enum toggling whether the segment is in the endcap or barrel.
std::set< double > m_fieldExtpSteps
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.
SearchTree_t constructTree(const xAOD::MuonSegmentContainer &segments) const
Construct a complete search tree from a MuonSegment container.
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...
std::unique_ptr< MsTrackSeedContainer > findTrackSeeds(const EventContext &ctx, const xAOD::MuonSegmentContainer &segments) const
Constructs the MS track seeds from the segment container.
Placeholder for what will later be the muon segment EDM representation.
float numberDoF() const
Returns the numberDoF.
int nPrecisionHits() const
Amg::Vector3D direction() const
Returns the direction as Amg::Vector.
Amg::Vector3D position() const
Returns the position as Amg::Vector.
int nPhiLayers() const
Returns the number of phi layers.
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.
Eigen::Matrix< double, 2, 1 > Vector2D
Eigen::Matrix< double, 3, 1 > Vector3D
This header ties the generic definitions in this package.
std::vector< MsTrackSeed > MsTrackSeedContainer
std::string printID(const xAOD::MuonSegment &seg)
Print the chamber ID of a segment, e.g.
MsTrackSeeder::SearchTree_t SearchTree_t
std::string print(const cont_t &container)
Print a space point container to string.
const Segment * detailedSegment(const xAOD::MuonSegment &seg)
Helper function to navigate from the xAOD::MuonSegment to the MuonR4::Segment.
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[
MuonSegmentContainer_v1 MuonSegmentContainer
Definition of the current "MuonSegment container version".
MuonSegment_v1 MuonSegment
Reference the current persistent version: