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) {
21 constexpr int ringSector(
const int sector) {
23 return sector == 0 ? nSectors : (sector > nSectors ? 1 : sector);
26 constexpr int ringOverlap(
const int sector) {
28 return sector > nSectors ? 1 : sector;
35 *posSlot = 0.5 * (*posSlot + segPos);
40 const double circPhi) {
44 const double theta = std::atan2(Acts::fastHypot(seg.
px(), seg.
py()), seg.
pz());
45 return Acts::makeDirectionFromPhiTheta(circPhi,
theta);
48 return std::format(
"r: {:.2f}, z: {:.2f}, phi: {:.2f}, theta: {:.2f}",
49 v.perp(),
v.z(), inDeg((
v.phi())), inDeg(
v.theta()));
56 return std::format(
"{:}, nPrecHits: {:}, nPhiHits: {:}",
MuonR4::printID(seg),
69 return "overlap with left sector";
71 return "sector center";
73 return "overlap with right sector";
82 auto& v{
m_cfg.fieldExtpSteps};
83 v.insert(0.); v.insert(1.);
84 if (std::ranges::any_of(v, [](
const double x){
85 return x < 0. ||
x > 1.;
94 return sectorMap.sectorOverlapPhi(sector, ringSector(sector + Acts::toUnderlying(proj)));
106 int doubSector = 2 * seg.
sector();
108 if (refSeed.
sector() == nSectors && doubSector ==2) {
109 return SectorProjector::leftOverlap;
110 }
else if (refSeed.
sector() == 1 && doubSector == nSectors) {
111 return SectorProjector::rightOverlap;
126 <<
envelope(segment)->identString());
131 const double projectPhi)
const {
137 ATH_MSG_VERBOSE(__func__<<
"() "<<__LINE__<<
" - Project onto phi: "<<inDeg(projectPhi));
139 using namespace Acts::detail::LineHelper;
141 const auto isect = lineIntersect<3>(segPos3D.z()*Amg::Vector3D::UnitZ(), radialDir,
142 segPos3D, dirAlongTube);
148 return isect.position();
162 if (Location::Barrel == loc) {
164 m_cfg.barrelRadius).value_or(10. * Gaudi::Units::km);
167 Acts::copySign(
m_cfg.endcapDiscZ, projPos[1])).value_or(10. * Gaudi::Units::km);
169 return projPos + lambda * projDir;
174 if (loc ==
Barrel && std::abs(projPos[1]) > std::min(
m_cfg.endcapDiscZ,
m_cfg.barrelLength)) {
176 " exceeds cylinder boundaries ("<<
m_cfg.barrelRadius<<
", "
177 <<std::min(
m_cfg.endcapDiscZ,
m_cfg.barrelLength)<<
")");
179 }
else if (loc == Endcap && (0 > projPos[0] || projPos[0] >
m_cfg.endcapDiscRadius)) {
181 " exceeds endcap boundaries ("<<
m_cfg.endcapDiscRadius<<
", "<<Acts::copySign(
m_cfg.endcapDiscZ, projPos[1])<<
")");
188 if (!pI || !pM || !pO) {
193 ATH_MSG_VERBOSE(__func__<<
"() "<<__LINE__<<
" - Construct sagitta from "
194 <<
"\n --- Inner: "<<
print(*pI) <<
"\n --- Middle: "<<
print(*pM)
195 <<
"\n --- Outer: "<<
print(*pO));
196 if (std::abs(planeNorm.dot(leverL)) > Acts::s_onSurfaceTolerance ||
197 std::abs(planeNorm.dot( (*pM) - (*pI))) > Acts::s_onSurfaceTolerance) {
201 const Amg::Vector3D sagittaDir = leverL.cross(planeNorm).unit();
202 std::optional<double> sagitta =
Amg::intersect<3>(*pI, leverL.unit(), *pM, sagittaDir);
203 ATH_MSG_VERBOSE(__func__<<
"() "<<__LINE__<<
" - Estimated sagitta: "<<(sagitta ?
204 std::to_string(sagitta.value_or(0.)) :
"---")<<
", lever arm: "
205 <<(leverL.mag() / Gaudi::Units::m)<<
" [m]" );
217 return leverL.dot(leverL) / (8. * (*sagitta));
228 unsigned nSegsWithPhi{0};
230 if (segment->nPhiLayers() > 0) {
231 circPhi += std::atan2(segment->y(), segment->x());
239 circPhi /=nSegsWithPhi;
241 auto layerPos{Acts::filledArray<VecOpt_t, 6>(std::nullopt)};
242 double avgBField{0.}, avgTheta{0.};
244 const Amg::Vector3D planeNorm = Acts::makeDirectionFromPhiTheta(circPhi+ 90._degree, 90._degree);
248 Amg::Vector3D projDir = dirForBField(*seed.segments()[0], circPhi);
250 unsigned nBFieldPoints{0};
251 const unsigned nSeedSeg = seed.segments().size();
253 for (
unsigned int s = 0; s < nSeedSeg; ++s) {
254 avgTheta += projDir.theta();
264 average(projPos, layerPos[0]);
267 average(projPos, layerPos[1]);
270 average(projPos, layerPos[2]);
274 average(projPos, layerPos[3]);
277 average(projPos, layerPos[4]);
280 average(projPos, layerPos[5]);
286 if (s + 1 == nSeedSeg) {
290 Amg::Vector3D nextDir = dirForBField(*seed.segments()[s+1], circPhi);
293 for (
double fieldStep :
m_cfg.fieldExtpSteps) {
295 if (fieldStep == 0. && s > 0) {
298 const Amg::Vector3D extPos = (1. -fieldStep) * projPos + fieldStep * nextPos;
299 const Amg::Vector3D extDir = ((1. -fieldStep) * projDir + fieldStep * nextDir).
unit();
301 fieldCache.
getField(extPos.data(), locField.data());
302 const double localB = extDir.cross(locField).mag();
304 ATH_MSG_VERBOSE(__func__<<
"() "<<__LINE__<<
" - Segment "<<s<<
", step: "<<fieldStep
306 <<
" --> localB: "<<(localB * 1000.)<<
" T."
313 projPos = std::move(nextPos);
314 projDir = std::move(nextDir);
316 avgBField /= std::max(nBFieldPoints, 1u);
317 ATH_MSG_DEBUG(__func__<<
"() "<<__LINE__<<
" - averaged field: "<<(avgBField * 1000.)
318 <<
" T, number of points: "<<nBFieldPoints<<
".");
320 avgTheta /= seed.segments().size();
322 std::optional<double> barrelR =
calculateRadius(std::move(layerPos[0]),
323 std::move(layerPos[1]),
324 std::move(layerPos[2]), planeNorm);
326 std::optional<double> endcapR =
calculateRadius(std::move(layerPos[3]),
327 std::move(layerPos[4]),
328 std::move(layerPos[5]), planeNorm);
330 if (!barrelR && !endcapR) {
331 return 5.*Gaudi::Units::TeV;
333 const double r = 0.5* ((barrelR ? *barrelR : *endcapR) +
334 (endcapR ? *endcapR : *barrelR));
336 const double P = 0.3* Gaudi::Units::GeV* avgBField *
r / std::abs(std::sin(avgTheta));
338 ATH_MSG_DEBUG(__func__<<
"() "<<__LINE__<<
" - Estimated radius "<<
r / Gaudi::Units::m<<
" [m] --> P: "<<
339 (
P / Gaudi::Units::GeV)<<
" [GeV]");
341 if (truthMuon && Acts::copySign(1.f,
P) != truthMuon->charge() && truthMuon->abseta() < 2.5 &&
342 (truthMuon->abseta() < 1.3 || truthMuon->abseta() > 1.4) ) {
343 ATH_MSG_WARNING(
"Invalid charge, pT: "<<(truthMuon->pt() / Gaudi::Units::GeV)<<
" [GeV], eta: "
344 <<truthMuon->eta()<<
", phi: "<<(truthMuon->phi() / 1._degree)<<
", q: "<<truthMuon->charge());
353 const int segSector = segment->
sector();
354 for (
const auto proj : {SectorProjector::leftOverlap, SectorProjector::center, SectorProjector::rightOverlap}) {
356 const int projSector = ringSector(segSector + Acts::toUnderlying(proj));
357 if (segment->
nPhiLayers() > 0 && proj != SectorProjector::center &&
358 !sectorMap.insideSector(projSector, segment->
position().phi())) {
360 <<
" is not in sector "<<projSector<<
" which is "<<
to_string(proj) <<
" to "<<segment->
sector());
368 std::array<double, 3> coords{};
372 const int treeSector = 2*segSector + Acts::toUnderlying(proj);
373 coords[Acts::toUnderlying(
eSector)] = ringOverlap(treeSector);
376 coords[Acts::toUnderlying(
eDetSection)] = Acts::copySign(Acts::toUnderlying(loc), refPoint[1]);
378 coords[Acts::toUnderlying(
ePosOnCylinder)] = refPoint[Location::Barrel == loc];
381 <<
" with "<<coords<<
" to the search tree");
382 outContainer.emplace_back(std::move(coords), segment);
388 rawData.reserve(3*segments.
size());
393 ATH_MSG_VERBOSE(
"Create a new tree with "<<rawData.size()<<
" entries. ");
402 for (
const auto& [coords, seedCandidate] : orderedSegs) {
406 if (!
m_cfg.selector->passSeedingQuality(ctx, *recoSeedCandidate)){
410 SearchTree_t::range_t selectRange{};
419 selectRange[Acts::toUnderlying(
eSector)].shrink(coords[Acts::toUnderlying(
eSector)] -0.25,
420 coords[Acts::toUnderlying(
eSector)] +0.25);
423 static_cast<int>(coords[Acts::toUnderlying(
eSector)])};
426 orderedSegs.rangeSearchMapDiscard(selectRange, [&](
427 const SearchTree_t::coordinate_t& ,
431 if (!
m_cfg.selector->compatibleForTrack(ctx, *recoSeedCandidate, *extendCandidate)) {
436 return extendWithMe->chamberIndex() == onSeed->chamberIndex();
438 if (itr == newSeed.
segments().end()){
441 }
else if (reducedChi2(**itr) > reducedChi2(*extendWithMe) &&
442 (*itr)->nPhiLayers() <= extendWithMe->
nPhiLayers()) {
452 const double r = newSeed.
location() == Location::Barrel ?
m_cfg.barrelRadius
456 const int secCoord = coords[Acts::toUnderlying(
eSector)];
458 const double phi = sectorMap.sectorOverlapPhi( (secCoord - secCoord % 2) / 2, (secCoord + secCoord % 2) / 2 );
460 +
z * Amg::Vector3D::UnitZ();
464 trackSeeds.emplace_back(std::move(newSeed));
466 ATH_MSG_VERBOSE(
"Found in total "<<trackSeeds.size()<<
" before overlap removal");
469 std::unique_ptr<MsTrackSeedContainer>
474 return a.segments().size() > b.segments().size();
476 auto outputSeeds = std::make_unique<MsTrackSeedContainer>();
477 outputSeeds->reserve(unresolved.size());
478 std::ranges::copy_if(std::move(unresolved), std::back_inserter(*outputSeeds),
480 MsTrackSeedContainer::iterator test_itr =
481 std::ranges::find_if(*outputSeeds, [&testMe](
const MsTrackSeed& good) {
482 if (ringOverlap(good.sector() - testMe.
sector()) > 1) {
485 return std::ranges::find_if(testMe.
segments(),
487 return std::ranges::find(good.segments(), segInTest) != good.segments().end();
491 if (test_itr == outputSeeds->end()) {
496 if ( (*test_itr).segments().size() < testMe.
segments().size()){
497 (*test_itr) = testMe;
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 & localToGlobalTrans(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.
SearchTree_t constructTree(const ActsTrk::GeometryContext &gctx, const xAOD::MuonSegmentContainer &segments) const
Construct a complete search tree from a MuonSegment container.
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.
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.
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
std::string to_string(const SectorProjector proj)
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)