10 #include "Acts/Utilities/Helpers.hpp"
11 #include "Acts/Surfaces/detail/LineHelper.hpp"
12 #include "Acts/Definitions/Units.hpp"
13 #include "GaudiKernel/PhysicalConstants.h"
15 using namespace Acts::UnitLiterals;
16 constexpr
double inDeg(
const double a) {
22 constexpr
int ringSector(
const int sector) {
24 return sector == 0 ? nSectors : (sector > nSectors ? 1 : sector);
27 constexpr
int ringOverlap(
const int sector) {
29 return sector > nSectors ? 1 : sector;
36 posSlot = 0.5 * ((*posSlot) + segPos);
41 const double circPhi) {
45 const double theta = std::atan2(Acts::fastHypot(seg.
px(), seg.
py()), seg.
pz());
46 return Acts::makeDirectionFromPhiTheta(circPhi,
theta);
49 return std::format(
"r: {:.2f}, z: {:.2f}, phi: {:.2f}, theta: {:.2f}",
50 v.perp(),
v.z(), inDeg((
v.phi())), inDeg(
v.theta()));
70 return "overlap with left sector";
72 return "sector center";
74 return "overlap with right sector";
82 m_cfg{std::move(
cfg)}{
83 auto&
v{m_cfg.fieldExtpSteps};
84 v.insert(0.);
v.insert(1.);
85 if (std::ranges::any_of(
v, [](
const double x){
86 return x < 0. ||
x > 1.;
95 return sectorMap.sectorOverlapPhi(sector, ringSector(sector + Acts::toUnderlying(
proj)));
107 int doubSector = 2 * seg.
sector();
109 if (refSeed.
sector() == nSectors && doubSector ==2) {
111 }
else if (refSeed.
sector() == 1 && doubSector == nSectors) {
127 <<
envelope(segment)->identString());
132 const double projectPhi)
const {
138 ATH_MSG_VERBOSE(__func__<<
"() "<<__LINE__<<
" - Project onto phi: "<<inDeg(projectPhi));
140 using namespace Acts::detail::LineHelper;
142 const auto isect = lineIntersect<3>(segPos3D.z()*Amg::Vector3D::UnitZ(), radialDir,
143 segPos3D, dirAlongTube);
149 return isect.position();
164 lambda = Amg::intersect<2>(projPos, projDir, Amg::Vector2D::UnitX(),
167 lambda = Amg::intersect<2>(projPos, projDir, Amg::Vector2D::UnitY(),
170 return projPos + lambda * projDir;
189 if (!pI || !pM || !pO) {
194 ATH_MSG_VERBOSE(__func__<<
"() "<<__LINE__<<
" - Construct sagitta from "
195 <<
"\n --- Inner: "<<
print(*pI) <<
"\n --- Middle: "<<
print(*pM)
196 <<
"\n --- Outer: "<<
print(*pO));
197 if (std::abs(planeNorm.dot(leverL)) > Acts::s_onSurfaceTolerance ||
198 std::abs(planeNorm.dot( (*pM) - (*pI))) > Acts::s_onSurfaceTolerance) {
202 const Amg::Vector3D sagittaDir = leverL.cross(planeNorm).unit();
203 std::optional<double> sagitta = Amg::intersect<3>(*pI, leverL.unit(), *pM, sagittaDir);
204 ATH_MSG_VERBOSE(__func__<<
"() "<<__LINE__<<
" - Estimated sagitta: "<<(sagitta ?
218 return leverL.dot(leverL) / (8. * (*sagitta));
229 unsigned nSegsWithPhi{0};
231 if (segment->nPhiLayers() > 0) {
232 circPhi += std::atan2(segment->y(), segment->x());
240 circPhi /=nSegsWithPhi;
242 auto layerPos{Acts::filledArray<VecOpt_t, 6>(std::nullopt)};
243 double avgBField{0.}, avgTheta{0.};
245 const Amg::Vector3D planeNorm = Acts::makeDirectionFromPhiTheta(circPhi+ 90._degree, 90._degree);
249 Amg::Vector3D projDir = dirForBField(*seed.segments()[0], circPhi);
251 unsigned nBFieldPoints{0};
252 const unsigned nSeedSeg = seed.segments().size();
254 for (
unsigned int s = 0;
s < nSeedSeg; ++
s) {
255 avgTheta += projDir.theta();
287 if (
s + 1 == nSeedSeg) {
291 Amg::Vector3D nextDir = dirForBField(*seed.segments()[
s+1], circPhi);
296 if (fieldStep == 0. &&
s > 0) {
299 const Amg::Vector3D extPos = (1. -fieldStep) * projPos + fieldStep * nextPos;
300 const Amg::Vector3D extDir = ((1. -fieldStep) * projDir + fieldStep * nextDir).unit();
302 fieldCache.getField(extPos.data(), locField.data());
303 const double localB = extDir.cross(locField).mag();
305 ATH_MSG_VERBOSE(__func__<<
"() "<<__LINE__<<
" - Segment "<<
s<<
", step: "<<fieldStep
307 <<
" --> localB: "<<(localB * 1000.)<<
" T."
314 projPos = std::move(nextPos);
315 projDir = std::move(nextDir);
317 avgBField /=
std::max(nBFieldPoints, 1
u);
318 ATH_MSG_DEBUG(__func__<<
"() "<<__LINE__<<
" - averaged field: "<<(avgBField * 1000.)
319 <<
" T, number of points: "<<nBFieldPoints<<
".");
321 avgTheta /= seed.segments().size();
323 std::optional<double> barrelR =
calculateRadius(std::move(layerPos[0]),
324 std::move(layerPos[1]),
325 std::move(layerPos[2]), planeNorm);
327 std::optional<double> endcapR =
calculateRadius(std::move(layerPos[3]),
328 std::move(layerPos[4]),
329 std::move(layerPos[5]), planeNorm);
331 if (!barrelR && !endcapR) {
334 const double r = 0.5* (barrelR.value_or(*endcapR) +
335 endcapR.value_or(*barrelR));
354 const int segSector = segment->
sector();
357 const int projSector = ringSector(segSector + Acts::toUnderlying(
proj));
359 !sectorMap.insideSector(projSector, segment->
position().phi())) {
361 <<
" is not in sector "<<projSector<<
" which is "<<
to_string(
proj) <<
" to "<<segment->
sector());
369 std::array<double, 3> coords{};
373 const int treeSector = 2*segSector + Acts::toUnderlying(
proj);
374 coords[Acts::toUnderlying(eSector)] = ringOverlap(treeSector);
377 coords[Acts::toUnderlying(eDetSection)] = Acts::toUnderlying(loc) *
sign(refPoint[1]);
379 coords[Acts::toUnderlying(ePosOnCylinder)] = refPoint[
Location::Barrel == loc];
382 <<
" with "<<coords<<
" to the search tree");
383 outContainer.emplace_back(std::move(coords), segment);
389 rawData.reserve(3*segments.
size());
394 ATH_MSG_VERBOSE(
"Create a new tree with "<<rawData.size()<<
" entries. ");
403 for (
const auto& [coords, seedCandidate] : orderedSegs) {
411 SearchTree_t::range_t selectRange{};
414 selectRange[Acts::toUnderlying(eDetSection)].shrink(coords[Acts::toUnderlying(eDetSection)] - 0.1,
415 coords[Acts::toUnderlying(eDetSection)] + 0.1);
417 selectRange[Acts::toUnderlying(ePosOnCylinder)].shrink(coords[Acts::toUnderlying(ePosOnCylinder)] -
m_cfg.
seedHalfLength,
420 selectRange[Acts::toUnderlying(eSector)].shrink(coords[Acts::toUnderlying(eSector)] -0.25,
421 coords[Acts::toUnderlying(eSector)] +0.25);
423 MsTrackSeed newSeed{
static_cast<Location>(std::abs(coords[Acts::toUnderlying(eDetSection)])),
424 static_cast<int>(coords[Acts::toUnderlying(eSector)])};
427 orderedSegs.rangeSearchMapDiscard(selectRange, [&](
428 const SearchTree_t::coordinate_t& ,
436 auto itr = std::ranges::find_if(newSeed.segments(), [extendWithMe](
const xAOD::MuonSegment* onSeed){
437 return extendWithMe->chamberIndex() == onSeed->chamberIndex();
439 if (itr == newSeed.segments().end()){
441 newSeed.addSegment(extendWithMe);
442 }
else if (reducedChi2(**itr) > reducedChi2(*extendWithMe) &&
443 (*itr)->nPhiLayers() <= extendWithMe->
nPhiLayers()) {
444 newSeed.replaceSegment(*itr, extendWithMe);
448 if (newSeed.segments().empty()) {
451 newSeed.addSegment(seedCandidate);
454 : coords[Acts::toUnderlying(ePosOnCylinder)];
455 const double z = newSeed.location() ==
Location::Barrel ? coords[Acts::toUnderlying(ePosOnCylinder)]
457 const int secCoord = coords[Acts::toUnderlying(eSector)];
459 const double phi = sectorMap.sectorOverlapPhi( (secCoord - secCoord % 2) / 2, (secCoord + secCoord % 2) / 2 );
461 +
z * Amg::Vector3D::UnitZ();
463 newSeed.setPosition(std::move(
pos));
465 trackSeeds.emplace_back(std::move(newSeed));
467 ATH_MSG_VERBOSE(
"Found in total "<<trackSeeds.size()<<
" before overlap removal");
470 std::unique_ptr<MsTrackSeedContainer>
475 return a.segments().
size() >
b.segments().size();
477 auto outputSeeds = std::make_unique<MsTrackSeedContainer>();
478 outputSeeds->reserve(unresolved.size());
482 std::ranges::find_if(*outputSeeds, [&testMe](
const MsTrackSeed&
good) {
483 if (ringOverlap(
good.sector() - testMe.
sector()) > 1) {
486 return std::ranges::find_if(testMe.
segments(),
488 return std::ranges::find(good.segments(), segInTest) != good.segments().end();
492 if (test_itr == outputSeeds->end()) {
497 if ( (*test_itr).segments().size() < testMe.
segments().size()){
498 (*test_itr) = testMe;