9#include "Acts/Utilities/Helpers.hpp"
10#include "Acts/Surfaces/detail/LineHelper.hpp"
11#include "Acts/Definitions/Units.hpp"
12#include "GaudiKernel/PhysicalConstants.h"
14 using namespace Acts::UnitLiterals;
15 constexpr double inDeg(
const double a) {
23 *posSlot = 0.5 * (*posSlot + segPos);
28 const double circPhi) {
32 const double theta = std::atan2(Acts::fastHypot(seg.
px(), seg.
py()), seg.
pz());
33 return Acts::makeDirectionFromPhiTheta(circPhi,
theta);
36 return std::format(
"r: {:.2f}, z: {:.2f}, phi: {:.2f}, theta: {:.2f}",
37 v.perp(),
v.z(), inDeg((
v.phi())), inDeg(
v.theta()));
44 return std::format(
"{:}, nPrecHits: {:}, nPhiHits: {:}",
MuonR4::printID(seg),
57 return "overlap with left sector";
59 return "sector center";
61 return "overlap with right sector";
68 return sector == 0 ? nSectors : (sector > nSectors ? 1 : sector);
72 return sector > nSectors ? 1 : sector;
78 auto& v{
m_cfg.fieldExtpSteps};
79 v.insert(0.); v.insert(1.);
80 if (std::ranges::any_of(v, [](
const double x){
81 return x < 0. ||
x > 1.;
90 return sectorMap.sectorOverlapPhi(sector,
ringSector(sector + Acts::toUnderlying(proj)));
102 int doubSector = 2 * seg.
sector();
104 if (refSeed.
sector() == nSectors && doubSector ==2) {
105 return SectorProjector::leftOverlap;
106 }
else if (refSeed.
sector() == 1 && doubSector == nSectors) {
107 return SectorProjector::rightOverlap;
122 <<
envelope(segment)->identString());
127 const double projectPhi)
const {
133 ATH_MSG_VERBOSE(__func__<<
"() "<<__LINE__<<
" - Project onto phi: "<<inDeg(projectPhi));
135 using namespace Acts::detail::LineHelper;
137 const auto isect = lineIntersect<3>(segPos3D.z()*Amg::Vector3D::UnitZ(), radialDir,
138 segPos3D, dirAlongTube);
144 return isect.position();
159 <<
", direction: "<<
Amg::toString(dir)<<
" sector projector: " << Acts::toUnderlying(proj) <<
" location: " << Acts::toUnderlying(loc));
164 if (Location::Barrel == loc) {
166 m_cfg.barrelRadius).value_or(10. * Gaudi::Units::km);
170 Acts::copySign(
m_cfg.endcapDiscZ, projPos[1])).value_or(10. * Gaudi::Units::km);
171 ATH_MSG_VERBOSE(__func__<<
"() "<<__LINE__<<
" - Intersect with endcap at z: "<<Acts::copySign(
m_cfg.endcapDiscZ, projPos[1])<<
" --> "<<
Amg::toString(projPos + lambda * projDir));
173 return projPos + lambda * projDir;
178 if (loc ==
Barrel && std::abs(projPos[1]) > std::min(
m_cfg.endcapDiscZ,
m_cfg.barrelLength)) {
180 " exceeds cylinder boundaries ("<<
m_cfg.barrelRadius<<
", "
181 <<std::min(
m_cfg.endcapDiscZ,
m_cfg.barrelLength)<<
")");
183 }
else if (loc == Endcap && (0 > projPos[0] || projPos[0] >
m_cfg.endcapDiscRadius)) {
185 " exceeds endcap boundaries ("<<
m_cfg.endcapDiscRadius<<
", "<<Acts::copySign(
m_cfg.endcapDiscZ, projPos[1])<<
")");
192 if (!pI || !pM || !pO) {
197 ATH_MSG_VERBOSE(__func__<<
"() "<<__LINE__<<
" - Construct sagitta from "
198 <<
"\n --- Inner: "<<
print(*pI) <<
"\n --- Middle: "<<
print(*pM)
199 <<
"\n --- Outer: "<<
print(*pO));
200 if (std::abs(planeNorm.dot(leverL)) > Acts::s_onSurfaceTolerance ||
201 std::abs(planeNorm.dot( (*pM) - (*pI))) > Acts::s_onSurfaceTolerance) {
205 const Amg::Vector3D sagittaDir = leverL.cross(planeNorm).unit();
206 std::optional<double> sagitta =
Amg::intersect<3>(*pI, leverL.unit(), *pM, sagittaDir);
207 ATH_MSG_VERBOSE(__func__<<
"() "<<__LINE__<<
" - Estimated sagitta: "<<(sagitta ?
208 std::to_string(sagitta.value_or(0.)) :
"---")<<
", lever arm: "
209 <<(leverL.mag() / Gaudi::Units::m)<<
" [m]" );
221 return leverL.dot(leverL) / (8. * (*sagitta));
232 unsigned nSegsWithPhi{0};
234 if (segment->nPhiLayers() > 0) {
235 circPhi += std::atan2(segment->y(), segment->x());
243 circPhi /=nSegsWithPhi;
245 auto layerPos{Acts::filledArray<VecOpt_t, 6>(std::nullopt)};
246 double avgBField{0.}, avgTheta{0.};
248 const Amg::Vector3D planeNorm = Acts::makeDirectionFromPhiTheta(circPhi+ 90._degree, 90._degree);
252 Amg::Vector3D projDir = dirForBField(*seed.segments()[0], circPhi);
254 unsigned nBFieldPoints{0};
255 const unsigned nSeedSeg = seed.segments().size();
257 for (
unsigned int s = 0; s < nSeedSeg; ++s) {
258 avgTheta += projDir.theta();
268 average(projPos, layerPos[0]);
271 average(projPos, layerPos[1]);
274 average(projPos, layerPos[2]);
278 average(projPos, layerPos[3]);
281 average(projPos, layerPos[4]);
284 average(projPos, layerPos[5]);
290 if (s + 1 == nSeedSeg) {
294 Amg::Vector3D nextDir = dirForBField(*seed.segments()[s+1], circPhi);
297 for (
double fieldStep :
m_cfg.fieldExtpSteps) {
299 if (fieldStep == 0. && s > 0) {
302 const Amg::Vector3D extPos = (1. -fieldStep) * projPos + fieldStep * nextPos;
303 const Amg::Vector3D extDir = ((1. -fieldStep) * projDir + fieldStep * nextDir).
unit();
305 fieldCache.
getField(extPos.data(), locField.data());
306 const double localB = extDir.cross(locField).mag();
308 ATH_MSG_VERBOSE(__func__<<
"() "<<__LINE__<<
" - Segment "<<s<<
", step: "<<fieldStep
310 <<
" --> localB: "<<(localB * 1000.)<<
" T."
317 projPos = std::move(nextPos);
318 projDir = std::move(nextDir);
320 avgBField /= std::max(nBFieldPoints, 1u);
321 ATH_MSG_DEBUG(__func__<<
"() "<<__LINE__<<
" - averaged field: "<<(avgBField * 1000.)
322 <<
" T, number of points: "<<nBFieldPoints<<
".");
324 avgTheta /= seed.segments().size();
326 std::optional<double> barrelR =
calculateRadius(std::move(layerPos[0]),
327 std::move(layerPos[1]),
328 std::move(layerPos[2]), planeNorm);
330 std::optional<double> endcapR =
calculateRadius(std::move(layerPos[3]),
331 std::move(layerPos[4]),
332 std::move(layerPos[5]), planeNorm);
334 if (!barrelR && !endcapR) {
335 return 5.*Gaudi::Units::TeV;
337 const double r = 0.5* ((barrelR ? *barrelR : *endcapR) +
338 (endcapR ? *endcapR : *barrelR));
340 const double P = 0.3* Gaudi::Units::GeV* avgBField *
r / std::abs(std::sin(avgTheta));
342 ATH_MSG_DEBUG(__func__<<
"() "<<__LINE__<<
" - Estimated radius "<<
r / Gaudi::Units::m<<
" [m] --> P: "<<
343 (
P / Gaudi::Units::GeV)<<
" [GeV]");
345 if (truthMuon && Acts::copySign(1.f,
P) != truthMuon->charge() && truthMuon->abseta() < 2.5 &&
346 (truthMuon->abseta() < 1.3 || truthMuon->abseta() > 1.4) ) {
347 ATH_MSG_WARNING(
"Invalid charge, pT: "<<(truthMuon->pt() / Gaudi::Units::GeV)<<
" [GeV], eta: "
348 <<truthMuon->eta()<<
", phi: "<<(truthMuon->phi() / 1._degree)<<
", q: "<<truthMuon->charge());
357 const int segSector = segment->
sector();
358 for (
const auto proj : {SectorProjector::leftOverlap, SectorProjector::center, SectorProjector::rightOverlap}) {
360 const int projSector =
ringSector(segSector + Acts::toUnderlying(proj));
361 if (segment->
nPhiLayers() > 0 && proj != SectorProjector::center &&
362 !sectorMap.insideSector(projSector, segment->
position().phi())) {
364 <<
" is not in sector "<<projSector<<
" which is "<<
to_string(proj) <<
" to "<<segment->
sector());
372 std::array<double, 3> coords{};
376 const int treeSector = 2*segSector + Acts::toUnderlying(proj);
380 coords[Acts::toUnderlying(
eDetSection)] = Acts::copySign(Acts::toUnderlying(loc), refPoint[1]);
382 coords[Acts::toUnderlying(
ePosOnCylinder)] = refPoint[Location::Barrel == loc];
385 <<
" with "<<coords<<
" to the search tree");
386 outContainer.emplace_back(std::move(coords), segment);
392 rawData.reserve(3*segments.
size());
397 ATH_MSG_VERBOSE(
"Create a new tree with "<<rawData.size()<<
" entries. ");
406 for (
const auto& [coords, seedCandidate] : orderedSegs) {
410 if (!
m_cfg.selector->passSeedingQuality(ctx, *recoSeedCandidate)){
415 SearchTree_t::range_t selectRange{};
424 selectRange[Acts::toUnderlying(
eSector)].shrink(coords[Acts::toUnderlying(
eSector)] -0.25,
425 coords[Acts::toUnderlying(
eSector)] +0.25);
428 static_cast<int>(coords[Acts::toUnderlying(
eSector)])};
431 orderedSegs.rangeSearchMapDiscard(selectRange, [&](
432 const SearchTree_t::coordinate_t& ,
436 if (!
m_cfg.selector->compatibleForTrack(ctx, *recoSeedCandidate, *extendCandidate)) {
441 return extendWithMe->chamberIndex() == onSeed->chamberIndex();
443 if (itr == newSeed.
segments().end()){
447 else if (reducedChi2(**itr) > reducedChi2(*extendWithMe) &&
448 (*itr)->nPhiLayers() <= extendWithMe->
nPhiLayers()) {
451 <<
" on seed due to better chi2.");
464 const double r = newSeed.
location() == Location::Barrel ?
m_cfg.barrelRadius
468 const int secCoord = coords[Acts::toUnderlying(
eSector)];
470 const double phi = sectorMap.sectorOverlapPhi( (secCoord - secCoord % 2) / 2, (secCoord + secCoord % 2) / 2 );
472 +
z * Amg::Vector3D::UnitZ();
476 trackSeeds.emplace_back(std::move(newSeed));
478 ATH_MSG_VERBOSE(
"Found in total "<<trackSeeds.size()<<
" before overlap removal");
481 std::unique_ptr<MsTrackSeedContainer>
486 return a.segments().size() > b.segments().size();
488 auto outputSeeds = std::make_unique<MsTrackSeedContainer>();
489 outputSeeds->reserve(unresolved.size());
490 std::ranges::copy_if(std::move(unresolved), std::back_inserter(*outputSeeds),
492 MsTrackSeedContainer::iterator test_itr =
493 std::ranges::find_if(*outputSeeds, [&testMe](
const MsTrackSeed& good) {
497 return std::ranges::find_if(testMe.
segments(),
499 return std::ranges::find(good.segments(), segInTest) != good.segments().end();
503 if (test_itr == outputSeeds->end()) {
508 if ( (*test_itr).segments().size() < testMe.
segments().size()){
509 (*test_itr) = testMe;
514 ATH_MSG_VERBOSE(
"Found in total "<<outputSeeds->size()<<
" after overlap removal");
Scalar phi() const
phi method
Scalar theta() const
theta method
const PlainObject unit() const
This is a plugin that makes Eigen look like CLHEP & defines some convenience methods.
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
void print(char *figname, TCanvas *c1)
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,...
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.
int sector() const
Returns the seed's sector.
void addSegment(const xAOD::MuonSegment *seg)
Append a segment to the seed.
Location location() const
Returns the location of the seed.
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
std::optional< double > calculateRadius(VecOpt_t &&pI, VecOpt_t &&pM, VecOpt_t &&pO, const Amg::Vector3D &planeNorm) const
Calculates the radius of the bending circle from three points using the sagitta.
bool withinBounds(const Amg::Vector2D &projPos, const Location loc) const
Returns whether the expression on the cylinder is within the surface bounds.
static std::string to_string(const SectorProjector proj)
Print the sector projector.
SearchTree_t constructTree(const ActsTrk::GeometryContext &gctx, const xAOD::MuonSegmentContainer &segments) const
Construct a complete search tree from a MuonSegment container.
static int ringSector(const int sector)
Ensure that the parsed sector number is following the MS sector schema 0 is mapped to 16 and 17 is ma...
Amg::Vector3D projectOntoSector(const ActsTrk::GeometryContext &gctx, const xAOD::MuonSegment &segment, const SectorProjector proj) const
Projects the segment's position onto the sector centre or onto the overlap point with one of the neig...
SearchTree_t::vector_t TreeRawVec_t
Abbrivation of the KDTree raw data vector.
Amg::Vector2D expressOnCylinder(const ActsTrk::GeometryContext &gctx, const xAOD::MuonSegment &segment, const Location loc, const SectorProjector proj) const
Expresses the segment on the cylinder surface.
std::unique_ptr< MsTrackSeedContainer > resolveOverlaps(MsTrackSeedContainer &&unresolved) const
Removes exact duplciates or partial subsets of the MsTrackSeeds.
static int ringOverlap(const int sector)
Maps the sector 33 -> 0 to close the extended MS symmetry ring.
void appendSegment(const ActsTrk::GeometryContext &gctx, const xAOD::MuonSegment *segment, const Location loc, TreeRawVec_t &outContainer) const
Append the to the raw data container.
Acts::KDTree< 3, const xAOD::MuonSegment *, double, std::array, 6 > SearchTree_t
Definition of the search tree class.
const MuonGMR4::SpectrometerSector * envelope(const xAOD::MuonSegment &segment) const
Returns the spectrometer envelope associated to the segment (Coord system where the parameter are exp...
static double projectedPhi(const int sector, const SectorProjector proj)
Returns the projected phi for a given sector and projector.
MsTrackSeeder(const std::string &msgName, Config &&cfg)
Standard constructor.
MsTrackSeed::Location Location
Enum toggling whether the segment is in the endcap or barrel.
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.
double estimateQtimesP(const ActsTrk::GeometryContext &gctx, const AtlasFieldCacheCondObj &magField, const MsTrackSeed &seed) const
Estimate the q /p of the seed candidate from the contained segments.
std::unique_ptr< MsTrackSeedContainer > findTrackSeeds(const EventContext &ctx, const ActsTrk::GeometryContext &gctx, const xAOD::MuonSegmentContainer &segments) const
Constructs the MS track seeds from the segment container.
Amg::Vector3D projectOntoPhiPlane(const ActsTrk::GeometryContext &gctx, const xAOD::MuonSegment &segment, const double projectPhi) const
static SectorProjector projectorFromSeed(const xAOD::MuonSegment &seg, const MsTrackSeed &refSeed)
Returns the Sector projector within the context of a MsTrackSeed.
std::optional< Amg::Vector3D > VecOpt_t
SectorProjector
Enumeration to select the sector projection.
@ rightOverlap
Project the segment onto the sector centre.
@ center
Project the segment onto the overlap with the previous sector.
Placeholder for what will later be the muon segment EDM representation.
float pz() const
Returns the pz.
float numberDoF() const
Returns the numberDoF.
int nPrecisionHits() const
Amg::Vector3D direction() const
Returns the direction as Amg::Vector.
float py() const
Returns the py.
::Muon::MuonStationIndex::ChIndex chamberIndex() const
Returns the chamber index.
Amg::Vector3D position() const
Returns the position as Amg::Vector.
int nPhiLayers() const
Returns the number of phi layers.
int etaIndex() const
Returns the eta index, which corresponds to stationEta in the offline identifiers (and the ).
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::Transform3D getRotateZ3D(double angle)
get a rotation transformation around Z-axis
Eigen::Matrix< double, 2, 1 > Vector2D
Eigen::Matrix< double, 3, 1 > Vector3D
This header ties the generic definitions in this package.
const xAOD::TruthParticle * getTruthMatchedParticle(const xAOD::MuonSegment &segment)
Returns the particle truth-matched to the segment.
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
MsTrackSeeder::SectorProjector SectorProjector
const Segment * detailedSegment(const xAOD::MuonSegment &seg)
Helper function to navigate from the xAOD::MuonSegment to the MuonR4::Segment.
StIndex
enum to classify the different station layers in the muon spectrometer
StIndex toStationIndex(ChIndex index)
convert ChIndex into StIndex
constexpr unsigned numberOfSectors()
return total number of sectors
MuonSegmentContainer_v1 MuonSegmentContainer
Definition of the current "MuonSegment container version".
TruthParticle_v1 TruthParticle
Typedef to implementation.
MuonSegment_v1 MuonSegment
Reference the current persistent version:
#define THROW_EXCEPTION(MESSAGE)