Project
Loading...
Searching...
No Matches
TrackGeometry.h
Go to the documentation of this file.
1// Copyright 2026-2026 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
12#ifndef ALICEO2_TRACKTOOLS_TRACKGEOMETRY_H_
13#define ALICEO2_TRACKTOOLS_TRACKGEOMETRY_H_
14
71#include <algorithm>
72#include <array>
73#include <cmath>
74#include <cstdint>
75#include <limits>
76#include <cstddef>
77#include <vector>
78#include <utility>
79
81#include "MathUtils/Utils.h"
83#include "TPCBase/Mapper.h"
85
87{
88
90enum class TrackParametersStatus : int32_t {
91 OK = 0,
92 InvalidInput = 1,
97};
98
104 float gx = std::numeric_limits<float>::quiet_NaN();
105 float gy = std::numeric_limits<float>::quiet_NaN();
106 float gz = std::numeric_limits<float>::quiet_NaN();
107 float px = std::numeric_limits<float>::quiet_NaN();
108 float py = std::numeric_limits<float>::quiet_NaN();
109 float pz = std::numeric_limits<float>::quiet_NaN();
111
112 bool valid() const { return status == TrackParametersStatus::OK; }
113};
114
117{
118 return state.valid() ? std::atan2(state.gy, state.gx) : std::numeric_limits<float>::quiet_NaN();
119}
120
123{
124 return state.valid() ? std::atan2(state.py, state.px) : std::numeric_limits<float>::quiet_NaN();
125}
126
129{
130 if (!state.valid()) {
131 return std::numeric_limits<float>::quiet_NaN();
132 }
133 const float pt = std::hypot(state.px, state.py);
134 return pt > 0.f ? state.pz / pt : std::numeric_limits<float>::quiet_NaN();
135}
136
146inline float dSector(float phi, int nSectors = 18, float sectorPhaseRad = 0.f)
147{
148 if (!std::isfinite(phi) || !std::isfinite(sectorPhaseRad) || nSectors <= 0) {
149 return std::numeric_limits<float>::quiet_NaN();
150 }
151 const float x = 0.5f * nSectors * (phi - sectorPhaseRad) / o2::constants::math::PI;
152 const float phase = x - std::floor(x);
153 return phase < 1.f ? phase : 0.f;
154}
155
157inline float stateDSector(const TrackParametersAtRadius& state, int nSectors = 18, float sectorPhaseRad = 0.f)
158{
159 return dSector(statePhiPos(state), nSectors, sectorPhaseRad);
160}
161
171inline o2::track::TrackPar makeTrackParFromPrimaryApproximation(float phi, float tgl, float qpt,
172 float vx = 0.f, float vy = 0.f, float vz = 0.f)
173{
174 const float pt = 1.f / std::abs(qpt);
175 const int charge = qpt >= 0.f ? 1 : -1;
176 o2::track::TrackPar::dim3_t xyz{{vx, vy, vz}};
177 o2::track::TrackPar::dim3_t pxyz{{pt * std::cos(phi), pt * std::sin(phi), pt * tgl}};
178 return o2::track::TrackPar(xyz, pxyz, charge, false);
179}
180
182inline o2::track::TrackPar makePrimaryTrackPar(float phi, float tgl, float qpt, float vz = 0.f)
183{
184 return makeTrackParFromPrimaryApproximation(phi, tgl, qpt, 0.f, 0.f, vz);
185}
186
191{
192 track.getXYZGlo(xyz);
193 if (!track.getPxPyPzGlo(pxyz)) {
194 return false;
195 }
196 for (int i = 0; i < 3; ++i) {
197 if (!std::isfinite(xyz[i]) || !std::isfinite(pxyz[i])) {
198 return false;
199 }
200 }
201 return true;
202}
203
205inline bool directionMatches(o2::track::DirType dir, float drds)
206{
207 constexpr float eps = 1.e-6f;
209 return drds >= -eps;
210 }
212 return drds <= eps;
213 }
214 return true;
215}
216
231{
233 if (!(radius > 0.f) || !std::isfinite(radius) || !std::isfinite(bzKG) || !track.isValid()) {
235 return out;
236 }
237
238 o2::track::TrackPar::dim3_t xyz{}, pxyz{};
239 if (!extractGlobalState(track, xyz, pxyz)) {
241 return out;
242 }
243
244 // Use double precision for the analytic construction even though TrackPar and
245 // the public result are float-based. For high-pT tracks the circle centre is
246 // O(1/curvature) away while the requested detector radius is O(10^2 cm).
247 // Performing the circle/circle intersection in float loses several microrad
248 // through cancellation. K0 is therefore the numerical oracle for the final
249 // float state, while H keeps the fast analytic transport.
250 const double x0 = static_cast<double>(xyz[0]);
251 const double y0 = static_cast<double>(xyz[1]);
252 const double z0 = static_cast<double>(xyz[2]);
253 const double px0 = static_cast<double>(pxyz[0]);
254 const double py0 = static_cast<double>(pxyz[1]);
255 const double pz0 = static_cast<double>(pxyz[2]);
256 const double targetR = static_cast<double>(radius);
257 const double pt = std::hypot(px0, py0);
258 if (!(pt > 0.) || !std::isfinite(pt)) {
260 return out;
261 }
262
263 const double phi0 = std::atan2(py0, px0);
264 const double tgl = pz0 / pt;
265 const double curv = static_cast<double>(track.getQ2Pt()) *
266 static_cast<double>(bzKG) *
267 static_cast<double>(o2::constants::math::B2C);
268 constexpr double eps = 1.e-10;
269 constexpr double curvEps = 1.e-12;
270 const double twoPi = 2.0 * static_cast<double>(o2::constants::math::PI);
271
272 bool foundAny = false;
273 bool foundDirection = false;
274 double bestSxy = std::numeric_limits<double>::infinity();
275 double bestX = 0., bestY = 0., bestPhiMom = 0.;
276
277 auto directionMatchesD = [&](double drds) {
278 constexpr double dirEps = 1.e-10;
280 return drds >= -dirEps;
281 }
283 return drds <= dirEps;
284 }
285 return true;
286 };
287
288 auto consider = [&](double x, double y, double phiMom, double sxy) {
289 if (!std::isfinite(sxy) || sxy < -eps) {
290 return;
291 }
292 foundAny = true;
293 sxy = std::max(0.0, sxy);
294 const double drds = (x * std::cos(phiMom) + y * std::sin(phiMom)) / targetR;
295 if (!directionMatchesD(drds)) {
296 return;
297 }
298 foundDirection = true;
299 if (sxy < bestSxy) {
300 bestSxy = sxy;
301 bestX = x;
302 bestY = y;
303 bestPhiMom = phiMom;
304 }
305 };
306
307 if (std::abs(curv) < curvEps) {
308 const double ux = px0 / pt, uy = py0 / pt;
309 const double b = x0 * ux + y0 * uy;
310 const double c = x0 * x0 + y0 * y0 - targetR * targetR;
311 double disc = b * b - c;
312 if (disc < -eps) {
314 return out;
315 }
316 disc = std::max(0.0, disc);
317 const double root = std::sqrt(disc);
318 for (const double sxy : {-b - root, -b + root}) {
319 consider(x0 + sxy * ux, y0 + sxy * uy, phi0, sxy);
320 }
321 } else {
322 const double xc = x0 - std::sin(phi0) / curv;
323 const double yc = y0 + std::cos(phi0) / curv;
324 const double rho = 1.0 / std::abs(curv);
325 const double d = std::hypot(xc, yc);
326 if (!(d > eps) || d > targetR + rho + eps || d < std::abs(targetR - rho) - eps) {
328 return out;
329 }
330
331 const double a = (targetR * targetR - rho * rho + d * d) / (2.0 * d);
332 double h2 = targetR * targetR - a * a;
333 if (h2 < -eps) {
335 return out;
336 }
337 h2 = std::max(0.0, h2);
338 const double h = std::sqrt(h2);
339 const double bx = a * xc / d, by = a * yc / d;
340 const double ox = -yc * h / d, oy = xc * h / d;
341
342 const std::array<std::array<double, 2>, 2> intersections{{{{bx + ox, by + oy}},
343 {{bx - ox, by - oy}}}};
344 for (const auto& xy : intersections) {
345 const double x = xy[0], y = xy[1];
346 double sinPhi = curv * (x - xc);
347 double cosPhi = -curv * (y - yc);
348 const double norm = std::hypot(sinPhi, cosPhi);
349 if (!(norm > 0.) || !std::isfinite(norm)) {
350 continue;
351 }
352 sinPhi /= norm;
353 cosPhi /= norm;
354 const double phiMom = std::atan2(sinPhi, cosPhi);
355 double delta = phiMom - phi0;
356 if (curv > 0.) {
357 while (delta < -eps) {
358 delta += twoPi;
359 }
360 } else {
361 while (delta > eps) {
362 delta -= twoPi;
363 }
364 }
365 consider(x, y, phiMom, delta / curv);
366 }
367 }
368
369 if (!foundDirection || !std::isfinite(bestSxy)) {
371 return out;
372 }
373
374 out.gx = static_cast<float>(bestX);
375 out.gy = static_cast<float>(bestY);
376 out.gz = static_cast<float>(z0 + bestSxy * tgl);
377 out.px = static_cast<float>(pt * std::cos(bestPhiMom));
378 out.py = static_cast<float>(pt * std::sin(bestPhiMom));
379 out.pz = static_cast<float>(pz0);
381 return out;
382}
383
399{
401 if (!(radius > 0.f) || !std::isfinite(radius) || !std::isfinite(bzKG) || !track.isValid()) {
403 return out;
404 }
405
406 float xAtR = 0.f;
407 if (!track.getXatLabR(radius, xAtR, bzKG, dir)) {
409 return out;
410 }
411
412 auto propagated = track;
413 if (!propagated.propagateParamTo(xAtR, bzKG)) {
415 return out;
416 }
417
418 o2::track::TrackPar::dim3_t xyz{}, pxyz{};
419 if (!extractGlobalState(propagated, xyz, pxyz)) {
421 return out;
422 }
423
424 // A successful internal TrackPar update is not sufficient: the returned
425 // global state must actually lie on the requested cylinder (Architect A5).
426 constexpr float radiusToleranceCm = 2.e-3f;
427 const float resultRadius = std::hypot(xyz[0], xyz[1]);
428 if (!std::isfinite(resultRadius) || std::abs(resultRadius - radius) > radiusToleranceCm) {
430 return out;
431 }
432
433 out.gx = xyz[0];
434 out.gy = xyz[1];
435 out.gz = xyz[2];
436 out.px = pxyz[0];
437 out.py = pxyz[1];
438 out.pz = pxyz[2];
440 return out;
441}
442
444inline float phiAtRadius(const o2::track::TrackPar& track, float radius, float bzKG,
446{
447 return statePhiPos(getParametersAtRadiusK0(track, radius, bzKG, dir));
448}
449
451inline float dSectorAtRadius(const o2::track::TrackPar& track, float radius, float bzKG, int nSectors = 18,
452 o2::track::DirType dir = o2::track::DirType::DirOutward, float sectorPhaseRad = 0.f)
453{
454 return stateDSector(getParametersAtRadiusK0(track, radius, bzKG, dir), nSectors, sectorPhaseRad);
455}
456
458inline float primaryPhiAtRadius(float phi, float tgl, float qpt, float radius, float bzKG, float vz = 0.f)
459{
460 if (!std::isfinite(phi) || !std::isfinite(tgl) || !std::isfinite(qpt) || std::abs(qpt) < 1.e-12f) {
461 return std::numeric_limits<float>::quiet_NaN();
462 }
463 return phiAtRadius(makePrimaryTrackPar(phi, tgl, qpt, vz), radius, bzKG, o2::track::DirType::DirOutward);
464}
465
467inline float primaryDSectorAtRadius(float phi, float tgl, float qpt, float radius, float bzKG,
468 int nSectors = 18, float vz = 0.f, float sectorPhaseRad = 0.f)
469{
470 const float phiR = primaryPhiAtRadius(phi, tgl, qpt, radius, bzKG, vz);
471 return dSector(phiR, nSectors, sectorPhaseRad);
472}
473
480 std::vector<float> gx, gy, gz, px, py, pz;
481 std::vector<float> phiPos, phiMom, tgl, dSector;
482 std::vector<int32_t> status;
483
484 void resize(std::size_t n)
485 {
486 gx.resize(n);
487 gy.resize(n);
488 gz.resize(n);
489 px.resize(n);
490 py.resize(n);
491 pz.resize(n);
492 phiPos.resize(n);
493 phiMom.resize(n);
494 tgl.resize(n);
495 dSector.resize(n);
496 status.resize(n);
497 }
498
499 void set(std::size_t i, const TrackParametersAtRadius& state, int nSectors, float sectorPhaseRad)
500 {
501 gx[i] = state.gx;
502 gy[i] = state.gy;
503 gz[i] = state.gz;
504 px[i] = state.px;
505 py[i] = state.py;
506 pz[i] = state.pz;
509 tgl[i] = stateTgl(state);
510 dSector[i] = stateDSector(state, nSectors, sectorPhaseRad);
511 status[i] = static_cast<int32_t>(state.status);
512 }
513};
514
515template <typename Evaluator>
517 const float* phi, const float* tgl, const float* qpt,
518 const float* vx, const float* vy, const float* vz,
519 std::size_t n, float radius, float bzKG, int nSectors, float sectorPhaseRad, Evaluator&& evaluate)
520{
522 out.resize(n);
523 for (std::size_t i = 0; i < n; ++i) {
524 if (!phi || !tgl || !qpt || !std::isfinite(qpt[i]) || std::abs(qpt[i]) < 1.e-12f) {
527 out.set(i, invalid, nSectors, sectorPhaseRad);
528 continue;
529 }
530 const auto track = makeTrackParFromPrimaryApproximation(phi[i], tgl[i], qpt[i],
531 vx ? vx[i] : 0.f,
532 vy ? vy[i] : 0.f,
533 vz ? vz[i] : 0.f);
534 out.set(i, evaluate(track, radius, bzKG), nSectors, sectorPhaseRad);
535 }
536 return out;
537}
538
541 const float* phi, const float* tgl, const float* qpt,
542 const float* vx, const float* vy, const float* vz,
543 std::size_t n, float radius, float bzKG, int nSectors = 18, float sectorPhaseRad = 0.f)
544{
546 phi, tgl, qpt, vx, vy, vz, n, radius, bzKG, nSectors, sectorPhaseRad,
547 [](const o2::track::TrackPar& track, float r, float bz) {
549 });
550}
551
554 const float* phi, const float* tgl, const float* qpt,
555 const float* vx, const float* vy, const float* vz,
556 std::size_t n, float radius, float bzKG, int nSectors = 18, float sectorPhaseRad = 0.f)
557{
559 phi, tgl, qpt, vx, vy, vz, n, radius, bzKG, nSectors, sectorPhaseRad,
560 [](const o2::track::TrackPar& track, float r, float bz) {
562 });
563}
564
567 float snp, float tgl, float qpt, int absCharge = 1)
568{
569 o2::track::TrackPar::params_t par{{y, z, snp, tgl, qpt}};
570 return o2::track::TrackPar(x, alpha, par, std::max(1, std::abs(absCharge)));
571}
572
573template <typename Evaluator>
575 const float* x, const float* alpha, const float* y, const float* z,
576 const float* snp, const float* tgl, const float* qpt,
577 std::size_t n, float radius, float bzKG, int nSectors, float sectorPhaseRad, Evaluator&& evaluate)
578{
580 out.resize(n);
581 for (std::size_t i = 0; i < n; ++i) {
582 if (!x || !alpha || !y || !z || !snp || !tgl || !qpt) {
585 out.set(i, invalid, nSectors, sectorPhaseRad);
586 continue;
587 }
588 const auto track = makeTrackParFromLocalParameters(x[i], alpha[i], y[i], z[i], snp[i], tgl[i], qpt[i]);
589 out.set(i, evaluate(track, radius, bzKG), nSectors, sectorPhaseRad);
590 }
591 return out;
592}
593
596 const float* x, const float* alpha, const float* y, const float* z,
597 const float* snp, const float* tgl, const float* qpt,
598 std::size_t n, float radius, float bzKG, int nSectors = 18, float sectorPhaseRad = 0.f)
599{
601 x, alpha, y, z, snp, tgl, qpt, n, radius, bzKG, nSectors, sectorPhaseRad,
602 [](const o2::track::TrackPar& track, float r, float bz) {
604 });
605}
606
609 const float* x, const float* alpha, const float* y, const float* z,
610 const float* snp, const float* tgl, const float* qpt,
611 std::size_t n, float radius, float bzKG, int nSectors = 18, float sectorPhaseRad = 0.f)
612{
614 x, alpha, y, z, snp, tgl, qpt, n, radius, bzKG, nSectors, sectorPhaseRad,
615 [](const o2::track::TrackPar& track, float r, float bz) {
617 });
618}
619
621inline std::vector<float> primaryPhiAtRadiusBatch(const float* phi, const float* tgl, const float* qpt, const float* vz,
622 std::size_t n, float radius, float bzKG)
623{
624 auto batch = primaryTrackParametersAtRadiusK0Batch(phi, tgl, qpt, nullptr, nullptr, vz, n, radius, bzKG);
625 return std::move(batch.phiPos);
626}
627
628inline std::vector<float> primaryDSectorAtRadiusBatch(const float* phi, const float* tgl, const float* qpt, const float* vz,
629 std::size_t n, float radius, float bzKG, int nSectors = 18,
630 float sectorPhaseRad = 0.f)
631{
632 auto batch = primaryTrackParametersAtRadiusK0Batch(phi, tgl, qpt, nullptr, nullptr, vz, n, radius, bzKG,
633 nSectors, sectorPhaseRad);
634 return std::move(batch.dSector);
635}
636
639 std::vector<float> phi;
640 std::vector<float> dSector;
641};
642
644 const float* phi, const float* tgl, const float* qpt, const float* vz,
645 std::size_t n, float radius, float bzKG, int nSectors = 18, float sectorPhaseRad = 0.f)
646{
647 auto batch = primaryTrackParametersAtRadiusK0Batch(phi, tgl, qpt, nullptr, nullptr, vz, n, radius, bzKG,
648 nSectors, sectorPhaseRad);
649 return {std::move(batch.phiPos), std::move(batch.dSector)};
650}
651
652} // namespace o2::recohelpers
653
655{
656
664
666enum class SectorEdgeStatus : int32_t {
667 OK = 0,
668 InvalidInput = 1,
669 NoRowsCrossed = 2,
672};
673
679 float phi = 0.f; // momentum azimuth at the primary vertex [rad]
680 float tgl = 0.f; // tan(lambda)
681 float qpt = 0.f; // signed q/pT [(GeV/c)^-1]
682 float vz = 0.f; // primary vertex z [cm]
683 float bzKG = std::numeric_limits<float>::quiet_NaN(); // signed O2 field convention [kG]; must be supplied
684};
685
710 float guardLowerCm = 0.f; // active-side exclusion adjacent to negative local-y edge
711 float guardUpperCm = 0.f; // active-side exclusion adjacent to positive local-y edge
712 float zMaxCm = 250.f; // symmetric TPC longitudinal acceptance |z| <= zMaxCm
713 float maxSnp = 0.9f; // radial-frame |sin(phiMom-phiPos)| < maxSnp; not O2 TrackPar::getSnp()
714};
715
728 float lengthCrossedCm = 0.f;
729 float lengthDeadCm = 0.f;
730 float lengthExcludedCm = 0.f;
731 float lengthAvailableCm = 0.f;
732 float maxRadiusCm = std::numeric_limits<float>::quiet_NaN(); // last accepted row radius [cm], not turning radius
733
734 uint16_t nRowsCrossed = 0;
735 uint16_t nRowsDead = 0;
736 uint16_t nRowsExcluded = 0;
737 uint16_t nRowsAvailable = 0;
738
739 std::array<uint32_t, 5> crossedMask{};
740 std::array<uint32_t, 5> deadMask{};
741 std::array<uint32_t, 5> excludedMask{};
742 std::array<uint32_t, 5> availableMask{};
743
744 int16_t firstDeadRow = -1;
745 int16_t lastDeadRow = -1;
747};
748
754 std::vector<float> lengthCrossedCm;
755 std::vector<float> lengthDeadCm;
756 std::vector<float> lengthExcludedCm;
757 std::vector<float> lengthAvailableCm;
758 std::vector<float> maxRadiusCm;
759
760 std::vector<int16_t> nRowsCrossed;
761 std::vector<int16_t> nRowsDead;
762 std::vector<int16_t> nRowsExcluded;
763 std::vector<int16_t> nRowsAvailable;
764
765 std::vector<int32_t> status;
766
767 void resize(std::size_t n)
768 {
769 lengthCrossedCm.resize(n);
770 lengthDeadCm.resize(n);
771 lengthExcludedCm.resize(n);
772 lengthAvailableCm.resize(n);
773 maxRadiusCm.resize(n);
774 nRowsCrossed.resize(n);
775 nRowsDead.resize(n);
776 nRowsExcluded.resize(n);
777 nRowsAvailable.resize(n);
778 status.resize(n);
779 }
780
781 void set(std::size_t i, const TPCSectorEdgeResult& r)
782 {
783 lengthCrossedCm[i] = r.lengthCrossedCm;
784 lengthDeadCm[i] = r.lengthDeadCm;
785 lengthExcludedCm[i] = r.lengthExcludedCm;
786 lengthAvailableCm[i] = r.lengthAvailableCm;
787 maxRadiusCm[i] = r.maxRadiusCm;
788 nRowsCrossed[i] = static_cast<int16_t>(r.nRowsCrossed);
789 nRowsDead[i] = static_cast<int16_t>(r.nRowsDead);
790 nRowsExcluded[i] = static_cast<int16_t>(r.nRowsExcluded);
791 nRowsAvailable[i] = static_cast<int16_t>(r.nRowsAvailable);
792 status[i] = static_cast<int32_t>(r.status);
793 }
794};
795
798 float x = 0.f;
799 float y = 0.f;
800 float z = 0.f;
801 float phiPos = 0.f;
802 float phiMom = 0.f;
803 float dsDr = 0.f;
804 bool valid = false;
805};
806
810inline void appendTrajectoryState(std::vector<o2::recohelpers::TrackParametersAtRadius>& trajectory,
811 const TrajectoryPoint& p, float qpt, float tgl)
812{
813 const float pt = 1.f / std::abs(qpt);
814 trajectory.push_back({p.x, p.y, p.z, pt * std::cos(p.phiMom), pt * std::sin(p.phiMom),
816}
817
819inline void setMaskBit(std::array<uint32_t, 5>& mask, int row)
820{
821 mask[row >> 5] |= uint32_t(1) << (row & 31);
822}
823
825inline int regionForRow(const o2::tpc::Mapper& mapper, int row)
826{
827 for (int i = 0; i < mapper.getNumberOfPadRegions(); ++i) {
828 const auto& r = mapper.getPadRegionInfo(i);
829 const int first = r.getGlobalRowOffset(), last = first + r.getNumberOfPadRows();
830 if (row >= first && row < last) {
831 return i;
832 }
833 }
834 return -1;
835}
836
838inline float rowRadius(const o2::tpc::Mapper& mapper, int row)
839{
840 const int ireg = regionForRow(mapper, row);
841 if (ireg < 0) {
842 return std::numeric_limits<float>::quiet_NaN();
843 }
844 const auto& r = mapper.getPadRegionInfo(ireg);
845 return r.getRadiusFirstRow() + (row - r.getGlobalRowOffset()) * r.getPadHeight();
846}
847
849inline float rowLengthRadial(const o2::tpc::Mapper& mapper, int row)
850{
851 const int ireg = regionForRow(mapper, row);
852 return ireg < 0 ? 0.f : mapper.getPadRegionInfo(ireg).getPadHeight();
853}
854
856inline float rowActiveHalfWidth(const o2::tpc::Mapper& mapper, int row)
857{
858 const int ireg = regionForRow(mapper, row);
859 if (ireg < 0) {
860 return 0.f;
861 }
862 const auto& r = mapper.getPadRegionInfo(ireg);
863 return 0.5f * mapper.getNumberOfPadsInRowSector(row) * r.getPadWidth();
864}
865
872{
873 if (!p.valid || !std::isfinite(p.x) || !std::isfinite(p.y) || !std::isfinite(p.phiMom)) {
874 return std::numeric_limits<float>::quiet_NaN();
875 }
876 return std::sin(p.phiMom - std::atan2(p.y, p.x));
877}
878
880inline float trajectoryLocalSnp(const TrajectoryPoint& p) { return trajectoryRadialSnp(p); }
881
884{
885 const float snp = trajectoryRadialSnp(p);
886 return p.valid && std::isfinite(p.z) && std::abs(p.z) <= cfg.zMaxCm &&
887 std::isfinite(snp) && std::abs(snp) < cfg.maxSnp;
888}
889
891inline bool reachedMaxSnp(const TrajectoryPoint& p, const TPCSectorEdgeConfig& cfg)
892{
893 const float snp = trajectoryRadialSnp(p);
894 return std::isfinite(snp) && std::abs(snp) >= cfg.maxSnp;
895}
896
900inline bool trackOutwardReference(const o2::track::TrackPar& track, float& radius)
901{
902 o2::track::TrackPar::dim3_t xyz{}, pxyz{};
903 if (!track.isValid() || !o2::recohelpers::extractGlobalState(track, xyz, pxyz)) {
904 return false;
905 }
906 radius = std::hypot(xyz[0], xyz[1]);
907 if (!std::isfinite(radius) || !std::isfinite(track.getQ2Pt()) ||
908 std::abs(track.getQ2Pt()) < 1.e-12f) {
909 return false;
910 }
911 // Negative radial motion at the supplied reference is not the first outward branch.
912 return radius < 1.e-5f || xyz[0] * pxyz[0] + xyz[1] * pxyz[1] >= 0.f;
913}
914
916inline TrajectoryPoint helixAtRadius(const o2::track::TrackPar& track, float radius, float bzKG)
917{
919 if (!track.isValid() || !std::isfinite(bzKG) || !(radius > 0.f)) {
920 return p;
921 }
924 if (!state.valid()) {
925 return p;
926 }
927 p.x = state.gx;
928 p.y = state.gy;
929 p.z = state.gz;
932 const float c = std::abs(std::cos(p.phiMom - p.phiPos));
933 const float tgl = o2::recohelpers::stateTgl(state);
934 p.dsDr = std::sqrt(1.f + tgl * tgl) / std::max(c, 1.e-6f);
935 p.valid = std::isfinite(p.dsDr);
936 return p;
937}
938
941{
942 if (!std::isfinite(seed.phi) || !std::isfinite(seed.tgl) || !std::isfinite(seed.qpt) ||
943 !std::isfinite(seed.bzKG) || std::abs(seed.qpt) < 1.e-12f) {
944 return {};
945 }
947 seed.phi, seed.tgl, seed.qpt, 0.f, 0.f, seed.vz),
948 radius, seed.bzKG);
949}
950
959inline void accumulateRow(const o2::tpc::Mapper& mapper, int row, const TrajectoryPoint& p, float rowLength,
961{
962 if (!rowWithinAcceptance(p, cfg) || !std::isfinite(rowLength) || rowLength <= 0.f) {
963 return;
964 }
965
966 const float phi = std::atan2(p.y, p.x);
967 const int sector = o2::math_utils::angle2Sector(phi);
968 const float alpha = o2::math_utils::sector2Angle(sector);
969 const float localY = -p.x * std::sin(alpha) + p.y * std::cos(alpha);
970 const float halfWidth = rowActiveHalfWidth(mapper, row);
971 const float edgeDistance = halfWidth - std::abs(localY);
972 const float guard = localY < 0.f ? cfg.guardLowerCm : cfg.guardUpperCm;
973
974 ++out.nRowsCrossed;
975 out.maxRadiusCm = rowRadius(mapper, row);
976 out.lengthCrossedCm += rowLength;
978
979 if (edgeDistance < 0.f) {
980 ++out.nRowsDead;
981 out.lengthDeadCm += rowLength;
982 setMaskBit(out.deadMask, row);
983 if (out.firstDeadRow < 0) {
984 out.firstDeadRow = row;
985 }
986 out.lastDeadRow = row;
987 } else if (edgeDistance < guard) {
988 ++out.nRowsExcluded;
989 out.lengthExcludedCm += rowLength;
991 } else {
992 ++out.nRowsAvailable;
993 out.lengthAvailableCm += rowLength;
995 }
996}
997
1004 std::vector<o2::recohelpers::TrackParametersAtRadius>* trajectory = nullptr)
1005{
1006 TPCSectorEdgeResult out;
1007 if (trajectory) {
1008 trajectory->clear();
1009 }
1010 float startRadius = 0.f;
1011 if (!std::isfinite(bzKG) || !trackOutwardReference(track, startRadius)) {
1013 return out;
1014 }
1015 const auto& mapper = o2::tpc::Mapper::instance();
1016
1017 for (int row = 0; row < mapper.getNumberOfRows(); ++row) {
1018 const float r = rowRadius(mapper, row);
1019 if (r + 1.e-4f < startRadius) {
1020 if (trajectory) {
1021 trajectory->emplace_back(); // Preserve trajectory[row] indexing for skipped rows.
1022 }
1023 continue; // A full track can start inside the TPC; do not count earlier rows.
1024 }
1025 const auto p = helixAtRadius(track, r, bzKG);
1026 if (!p.valid) {
1027 break; // a larger radius cannot re-enter the first outward primary-helix branch
1028 }
1029 if (trajectory) {
1030 appendTrajectoryState(*trajectory, p, track.getQ2Pt(), track.getTgl());
1031 }
1032 if (reachedMaxSnp(p, cfg)) {
1033 break;
1034 }
1035 accumulateRow(mapper, row, p, rowLengthRadial(mapper, row) * p.dsDr, cfg, out);
1036 }
1037
1038 if (!out.nRowsCrossed) {
1040 }
1041 return out;
1042}
1043
1046 std::vector<o2::recohelpers::TrackParametersAtRadius>* trajectory = nullptr)
1047{
1048 if (!std::isfinite(seed.phi) || !std::isfinite(seed.tgl) || !std::isfinite(seed.qpt) ||
1049 !std::isfinite(seed.bzKG) || std::abs(seed.qpt) < 1.e-12f) {
1050 TPCSectorEdgeResult r;
1052 return r;
1053 }
1055 seed.phi, seed.tgl, seed.qpt, 0.f, 0.f, seed.vz),
1056 seed.bzKG, cfg, trajectory);
1057}
1058
1065inline bool plausibleFirstOutwardStep(float dr, float ds, float tgl, float maxSnp)
1066{
1067 if (!(dr > 0.f) || !(ds > 0.f) || !std::isfinite(ds) || !std::isfinite(tgl) ||
1068 !(maxSnp > 0.f && maxSnp < 1.f)) {
1069 return false;
1070 }
1071 const float minRadialCos = std::sqrt(1.f - maxSnp * maxSnp);
1072 return ds <= 10.f * dr * std::sqrt(1.f + tgl * tgl) / minRadialCos;
1073}
1074
1087 const TPCSectorEdgeConfig& cfg = {},
1088 std::vector<o2::recohelpers::TrackParametersAtRadius>* trajectory = nullptr)
1089{
1090 TPCSectorEdgeResult out;
1091 if (trajectory) {
1092 trajectory->clear();
1093 }
1094 const auto& mapper = o2::tpc::Mapper::instance();
1095 auto* prop = o2::base::Propagator::Instance();
1096 if (!prop->hasMagFieldSet()) {
1098 return out;
1099 }
1100 float startRadius = 0.f;
1101 if (!std::isfinite(bzKG) || !trackOutwardReference(inputTrack, startRadius) ||
1102 !std::isfinite(cfg.maxSnp) || !(cfg.maxSnp > 0.f && cfg.maxSnp < 1.f)) {
1103 out.status = SectorEdgeStatus::InvalidInput;
1104 return out;
1105 }
1106
1107 int firstRow = 0;
1108 while (firstRow < mapper.getNumberOfRows() &&
1109 rowRadius(mapper, firstRow) + 1.e-4f < startRadius) {
1110 ++firstRow;
1111 }
1112 if (firstRow == mapper.getNumberOfRows()) {
1114 return out;
1115 }
1116 const float r0 = rowRadius(mapper, firstRow);
1117 if (trajectory) {
1118 trajectory->resize(firstRow); // Keep diagnostic trace indexed by global TPC row.
1119 }
1120 // Preserve the reviewed K first-row seeding convention, but use the complete
1121 // original track rather than reconstructing a primary vertex. K field-map
1122 // transport from the original reference remains a separate physics-validation item.
1123 const auto p0 = helixAtRadius(inputTrack, r0, bzKG);
1124 if (!p0.valid) {
1126 return out;
1127 }
1128
1129 const float qpt = inputTrack.getQ2Pt();
1130 const float pt = 1.f / std::abs(qpt);
1131 const int charge = qpt >= 0.f ? 1 : -1;
1132 o2::track::TrackPar::dim3_t xyz{{p0.x, p0.y, p0.z}};
1133 o2::track::TrackPar::dim3_t pxyz{{pt * std::cos(p0.phiMom), pt * std::sin(p0.phiMom), pt * inputTrack.getTgl()}};
1134 o2::track::TrackPar track(xyz, pxyz, charge, true);
1136 path.clear();
1137 path.setTimeNotNeeded();
1138 float previousPathCm = 0.f;
1139 float previousRadiusCm = r0;
1140
1141 for (int row = firstRow; row < mapper.getNumberOfRows(); ++row) {
1142 const float radius = rowRadius(mapper, row);
1143 if (row > firstRow) {
1144 // Record accumulated transported path length. A later outward *orbit* can have positive dr/ds
1145 // and lie on the requested cylinder; radialCos > 0 alone would accept that wrong branch.
1146 // With radial |snp| < maxSnp, a first-branch step should be short relative to its radial
1147 // increment. This deliberately generous guard only rejects gross discontinuities; numerical
1148 // H/K row-mask agreement is checked separately by the physics oracle.
1149 if (!prop->propagateToR(track, radius, false, o2::base::Propagator::MAX_SIN_PHI,
1150 o2::base::Propagator::MAX_STEP,
1151 o2::base::Propagator::MatCorrType::USEMatCorrNONE, &path)) {
1153 return out;
1154 }
1155 const float deltaPathCm = path.getL() - previousPathCm;
1156 const float deltaRadiusCm = radius - previousRadiusCm;
1157 if (!plausibleFirstOutwardStep(deltaRadiusCm, deltaPathCm, track.getTgl(), cfg.maxSnp)) {
1159 return out;
1160 }
1161 previousPathCm = path.getL();
1162 previousRadiusCm = radius;
1163 }
1164
1165 const auto xyzg = track.getXYZGlo();
1166 TrajectoryPoint p;
1167 p.x = xyzg.X();
1168 p.y = xyzg.Y();
1169 p.z = xyzg.Z();
1170 p.phiPos = track.getPhiPos();
1171 p.phiMom = track.getPhi();
1172 const float dphi = p.phiMom - p.phiPos;
1173 const float radialCos = std::cos(dphi);
1174 // propagateToR() may return a state on a different branch or slightly off the target cylinder.
1175 // Neither may contribute a crossed row. The radial-frame snp limit is checked independently below.
1176 constexpr float radiusToleranceCm = 2.e-3f;
1177 const float actualRadius = std::hypot(p.x, p.y);
1178 if (!std::isfinite(actualRadius) || std::abs(actualRadius - radius) > radiusToleranceCm ||
1179 !std::isfinite(radialCos)) {
1181 return out;
1182 }
1183 if (radialCos <= 0.f) {
1184 break; // reached the transverse turning point or a returning arc
1185 }
1186 p.dsDr = std::sqrt(1.f + track.getTgl() * track.getTgl()) / radialCos;
1187 p.valid = true;
1188 if (trajectory) {
1189 appendTrajectoryState(*trajectory, p, track.getQ2Pt(), track.getTgl());
1190 }
1191 if (reachedMaxSnp(p, cfg)) {
1192 break;
1193 }
1194 accumulateRow(mapper, row, p, rowLengthRadial(mapper, row) * p.dsDr, cfg, out);
1195 }
1196
1197 if (!out.nRowsCrossed && out.status == SectorEdgeStatus::OK) {
1199 }
1200 return out;
1201}
1202
1205 const TPCSectorEdgeConfig& cfg = {},
1206 std::vector<o2::recohelpers::TrackParametersAtRadius>* trajectory = nullptr)
1207{
1208 if (!std::isfinite(seed.phi) || !std::isfinite(seed.tgl) || !std::isfinite(seed.qpt) ||
1209 !std::isfinite(seed.bzKG) || std::abs(seed.qpt) < 1.e-12f) {
1210 TPCSectorEdgeResult r;
1212 return r;
1213 }
1215 seed.phi, seed.tgl, seed.qpt, 0.f, 0.f, seed.vz),
1216 seed.bzKG, cfg, trajectory);
1217}
1218
1227 std::vector<o2::recohelpers::TrackParametersAtRadius>* trajectory = nullptr)
1228{
1229 TPCSectorEdgeResult out;
1230 if (trajectory) {
1231 trajectory->clear();
1232 }
1233 float startRadius = 0.f;
1234 if (!std::isfinite(bzKG) || !trackOutwardReference(track, startRadius)) {
1236 return out;
1237 }
1238 const auto& mapper = o2::tpc::Mapper::instance();
1239
1240 int first = 0;
1241 while (first < mapper.getNumberOfRows()) {
1242 const float h = rowLengthRadial(mapper, first);
1243 int last = first;
1244 while (last + 1 < mapper.getNumberOfRows() && std::abs(rowLengthRadial(mapper, last + 1) - h) < 1.e-6f) {
1245 ++last;
1246 }
1247
1248 if (rowRadius(mapper, first) + 1.e-4f < startRadius) {
1249 if (trajectory) {
1250 trajectory->emplace_back(); // Preserve global row indexing.
1251 }
1252 ++first; // Ignore rows inside the complete track's reference radius.
1253 continue;
1254 }
1255 const float r0 = rowRadius(mapper, first);
1256 const auto p0 = helixAtRadius(track, r0, bzKG);
1257 if (!p0.valid || reachedMaxSnp(p0, cfg)) {
1258 break;
1259 }
1260
1261 // The frozen Linear contract keeps a reachable partial stack. If the nominal
1262 // outer endpoint is unreachable or already beyond maxSnp, walk inward to the
1263 // last reachable row below maxSnp and use that row as the interpolation endpoint.
1264 int endpoint = last;
1265 auto p1 = helixAtRadius(track, rowRadius(mapper, endpoint), bzKG);
1266 bool stopAfterStack = false;
1267 while (endpoint > first && (!p1.valid || reachedMaxSnp(p1, cfg))) {
1268 --endpoint;
1269 p1 = helixAtRadius(track, rowRadius(mapper, endpoint), bzKG);
1270 stopAfterStack = true;
1271 }
1272 if (!p1.valid || reachedMaxSnp(p1, cfg)) {
1273 break;
1274 }
1275 stopAfterStack = stopAfterStack || endpoint < last;
1276
1277 const float r1 = rowRadius(mapper, endpoint);
1278 float dphi = p1.phiPos - p0.phiPos;
1279 o2::math_utils::detail::bringToPMPi(dphi);
1280 const float dr = std::max(r1 - r0, 1.e-6f);
1281 const float dphiDr = dphi / dr, dzDr = (p1.z - p0.z) / dr;
1282
1283 for (int row = first; row <= endpoint; ++row) {
1284 const float r = rowRadius(mapper, row), f = (r - r0) / dr;
1285 const float phi = p0.phiPos + f * dphi;
1286 TrajectoryPoint p;
1287 p.x = r * std::cos(phi);
1288 p.y = r * std::sin(phi);
1289 p.z = p0.z + f * (p1.z - p0.z);
1290 p.phiPos = phi;
1291 p.phiMom = phi + std::atan(r * dphiDr);
1292 p.dsDr = std::sqrt(1.f + (r * dphiDr) * (r * dphiDr) + dzDr * dzDr);
1293 // When the limit leaves a single reachable row, finite-difference derivatives are undefined.
1294 // Reuse the actual helix tangent and path length at that row rather than setting the slopes to zero.
1295 if (endpoint == first) {
1296 p.phiMom = p0.phiMom;
1297 p.dsDr = p0.dsDr;
1298 }
1299 p.valid = true;
1300 if (trajectory) {
1301 appendTrajectoryState(*trajectory, p, track.getQ2Pt(), track.getTgl());
1302 }
1303 if (reachedMaxSnp(p, cfg)) {
1304 stopAfterStack = true;
1305 break;
1306 }
1307 accumulateRow(mapper, row, p, rowLengthRadial(mapper, row) * p.dsDr, cfg, out);
1308 }
1309
1310 if (stopAfterStack) {
1311 break;
1312 }
1313 first = last + 1;
1314 }
1315
1316 if (!out.nRowsCrossed) {
1318 }
1319 return out;
1320}
1321
1324 std::vector<o2::recohelpers::TrackParametersAtRadius>* trajectory = nullptr)
1325{
1326 if (!std::isfinite(seed.phi) || !std::isfinite(seed.tgl) || !std::isfinite(seed.qpt) ||
1327 !std::isfinite(seed.bzKG) || std::abs(seed.qpt) < 1.e-12f) {
1328 TPCSectorEdgeResult r;
1330 return r;
1331 }
1333 seed.phi, seed.tgl, seed.qpt, 0.f, 0.f, seed.vz),
1334 seed.bzKG, cfg, trajectory);
1335}
1336
1337template <typename Evaluator>
1338inline TPCSectorEdgeBatchResult sectorEdgeBatch(const float* phi, const float* tgl, const float* qpt, const float* vz,
1339 std::size_t n, float bzKG, const TPCSectorEdgeConfig& cfg, Evaluator&& evaluate)
1340{
1342 out.resize(n);
1343
1344 if (!phi || !tgl || !qpt) {
1345 for (std::size_t i = 0; i < n; ++i) {
1348 out.set(i, r);
1349 }
1350 return out;
1351 }
1352
1353 for (std::size_t i = 0; i < n; ++i) {
1355 seed.phi = phi[i];
1356 seed.tgl = tgl[i];
1357 seed.qpt = qpt[i];
1358 seed.vz = vz ? vz[i] : 0.f;
1359 seed.bzKG = bzKG;
1360 out.set(i, evaluate(seed, cfg));
1361 }
1362 return out;
1363}
1364
1366inline TPCSectorEdgeBatchResult sectorEdgeHelixRowsBatch(const float* phi, const float* tgl, const float* qpt, const float* vz,
1367 std::size_t n, float bzKG, const TPCSectorEdgeConfig& cfg = {})
1368{
1369 return sectorEdgeBatch(phi, tgl, qpt, vz, n, bzKG, cfg,
1370 [](const TPCPrimaryTrackSeed& seed, const TPCSectorEdgeConfig& c) { return sectorEdgeHelixRows(seed, c); });
1371}
1372
1374inline TPCSectorEdgeBatchResult sectorEdgeStackLinearBatch(const float* phi, const float* tgl, const float* qpt, const float* vz,
1375 std::size_t n, float bzKG, const TPCSectorEdgeConfig& cfg = {})
1376{
1377 return sectorEdgeBatch(phi, tgl, qpt, vz, n, bzKG, cfg,
1378 [](const TPCPrimaryTrackSeed& seed, const TPCSectorEdgeConfig& c) { return sectorEdgeStackLinear(seed, c); });
1379}
1380
1382inline TPCSectorEdgeBatchResult sectorEdgeO2PropagatorRowsBatch(const float* phi, const float* tgl, const float* qpt, const float* vz,
1383 std::size_t n, float bzKG, const TPCSectorEdgeConfig& cfg = {})
1384{
1385 return sectorEdgeBatch(phi, tgl, qpt, vz, n, bzKG, cfg,
1386 [](const TPCPrimaryTrackSeed& seed, const TPCSectorEdgeConfig& c) {
1387 return sectorEdgeO2PropagatorRows(seed, c);
1388 });
1389}
1390
1396template <typename Evaluator>
1398 const float* x, const float* alpha, const float* y, const float* z,
1399 const float* snp, const float* tgl, const float* qpt,
1400 std::size_t n, float bzKG, const TPCSectorEdgeConfig& cfg, Evaluator&& evaluate)
1401{
1403 out.resize(n);
1404 for (std::size_t i = 0; i < n; ++i) {
1405 if (!x || !alpha || !y || !z || !snp || !tgl || !qpt ||
1406 !std::isfinite(x[i]) || !std::isfinite(alpha[i]) || !std::isfinite(y[i]) ||
1407 !std::isfinite(z[i]) || !std::isfinite(snp[i]) || !std::isfinite(tgl[i]) ||
1408 !std::isfinite(qpt[i]) || std::abs(qpt[i]) < 1.e-12f || std::abs(snp[i]) >= 1.f) {
1411 out.set(i, r);
1412 continue;
1413 }
1415 x[i], alpha[i], y[i], z[i], snp[i], tgl[i], qpt[i]);
1416 out.set(i, evaluate(track, bzKG, cfg));
1417 }
1418 return out;
1419}
1420
1422 const float* x, const float* alpha, const float* y, const float* z,
1423 const float* snp, const float* tgl, const float* qpt,
1424 std::size_t n, float bzKG, const TPCSectorEdgeConfig& cfg = {})
1425{
1426 return localSectorEdgeBatch(x, alpha, y, z, snp, tgl, qpt, n, bzKG, cfg,
1427 [](const o2::track::TrackPar& t, float bz, const TPCSectorEdgeConfig& c) {
1428 return sectorEdgeHelixRows(t, bz, c);
1429 });
1430}
1431
1433 const float* x, const float* alpha, const float* y, const float* z,
1434 const float* snp, const float* tgl, const float* qpt,
1435 std::size_t n, float bzKG, const TPCSectorEdgeConfig& cfg = {})
1436{
1437 return localSectorEdgeBatch(x, alpha, y, z, snp, tgl, qpt, n, bzKG, cfg,
1438 [](const o2::track::TrackPar& t, float bz, const TPCSectorEdgeConfig& c) {
1439 return sectorEdgeStackLinear(t, bz, c);
1440 });
1441}
1442
1444 const float* x, const float* alpha, const float* y, const float* z,
1445 const float* snp, const float* tgl, const float* qpt,
1446 std::size_t n, float bzKG, const TPCSectorEdgeConfig& cfg = {})
1447{
1448 return localSectorEdgeBatch(x, alpha, y, z, snp, tgl, qpt, n, bzKG, cfg,
1449 [](const o2::track::TrackPar& t, float bz, const TPCSectorEdgeConfig& c) {
1450 return sectorEdgeO2PropagatorRows(t, bz, c);
1451 });
1452}
1453
1455
1456} // namespace o2::tpc::recohelpers
1457
1458#endif // ALICEO2_TRACKTOOLS_TRACKGEOMETRY_H_
General auxilliary methods.
Base track model for the Barrel, params only, w/o covariance.
int16_t charge
Definition RawEventData.h:5
uint64_t phase
Definition RawEventData.h:7
int32_t i
constexpr int p1()
constexpr to accelerate the coordinates changing
SurfaceTrackState state
useful math constants
uint8_t endpoint
Definition RawData.h:0
uint32_t c
Definition RawData.h:2
double radius
Class for time synchronization of RawReader instances.
GPUd() value_type estimateLTFast(o2 static GPUd() float estimateLTIncrement(const o2 PropagatorImpl * Instance(bool uninitialized=false)
Definition Propagator.h:180
int getNumberOfPadsInRowSector(int row) const
Definition Mapper.h:340
static Mapper & instance(const std::string mappingDir="")
Definition Mapper.h:44
const PadRegionInfo & getPadRegionInfo(const unsigned char region) const
Definition Mapper.h:385
int getNumberOfPadRegions() const
Definition Mapper.h:387
float getRadiusFirstRow() const
unsigned char getGlobalRowOffset() const
float getPadHeight() const
std::array< value_t, kNParams > params_t
GLdouble n
Definition glcorearb.h:1982
GLfloat GLfloat GLfloat alpha
Definition glcorearb.h:279
GLint GLenum GLint x
Definition glcorearb.h:403
GLint first
Definition glcorearb.h:399
GLdouble f
Definition glcorearb.h:310
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLint y
Definition glcorearb.h:270
GLuint GLfloat x0
Definition glcorearb.h:5034
GLsizei const GLchar *const * path
Definition glcorearb.h:3591
GLboolean r
Definition glcorearb.h:1233
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLint GLuint mask
Definition glcorearb.h:291
GLuint GLfloat GLfloat y0
Definition glcorearb.h:5034
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
const bool const int TrackITSInternal< NLayers > & track
int angle2Sector(float phi)
Definition Utils.h:183
float sector2Angle(int sect)
Definition Utils.h:193
std::array< int, 24 > p0
TrackParametersAtRadiusBatchResult localTrackParametersAtRadiusK0Batch(const float *x, const float *alpha, const float *y, const float *z, const float *snp, const float *tgl, const float *qpt, std::size_t n, float radius, float bzKG, int nSectors=18, float sectorPhaseRad=0.f)
Batch K0 transport for tracks supplied in local O2 parameterization.
float stateTgl(const TrackParametersAtRadius &state)
tan(lambda)=pz/pT of the propagated state, or NaN for an invalid state.
float phiAtRadius(const o2::track::TrackPar &track, float radius, float bzKG, o2::track::DirType dir=o2::track::DirType::DirOutward)
Backward-compatible scalar wrapper returning only the propagated position azimuth using K0.
TrackParametersAtRadiusBatchResult primaryTrackParametersAtRadiusBatch(const float *phi, const float *tgl, const float *qpt, const float *vx, const float *vy, const float *vz, std::size_t n, float radius, float bzKG, int nSectors, float sectorPhaseRad, Evaluator &&evaluate)
TrackParametersAtRadius getParametersAtRadiusK0(const o2::track::TrackPar &track, float radius, float bzKG, o2::track::DirType dir=o2::track::DirType::DirOutward)
K0 transport: O2 TrackPar propagation to a cylindrical radius in Bz, without material correction.
TrackParametersAtRadiusBatchResult localTrackParametersAtRadiusBatch(const float *x, const float *alpha, const float *y, const float *z, const float *snp, const float *tgl, const float *qpt, std::size_t n, float radius, float bzKG, int nSectors, float sectorPhaseRad, Evaluator &&evaluate)
bool extractGlobalState(const o2::track::TrackPar &track, o2::track::TrackPar::dim3_t &xyz, o2::track::TrackPar::dim3_t &pxyz)
Extract finite global position and momentum from an O2 TrackPar.
float primaryDSectorAtRadius(float phi, float tgl, float qpt, float radius, float bzKG, int nSectors=18, float vz=0.f, float sectorPhaseRad=0.f)
Fractional sector coordinate at radius for the reduced primary-like parameterization,...
float statePhiMom(const TrackParametersAtRadius &state)
Global azimuth of the propagated momentum, atan2(py,px), or NaN for an invalid state.
TrackParametersStatus
Status returned by the cylindrical-radius track-parameter evaluators.
float stateDSector(const TrackParametersAtRadius &state, int nSectors=18, float sectorPhaseRad=0.f)
dSector() evaluated from the propagated global position azimuth.
TrackParametersAtRadius getParametersAtRadiusH(const o2::track::TrackPar &track, float radius, float bzKG, o2::track::DirType dir=o2::track::DirType::DirOutward)
H transport: analytic helix intersection with a cylindrical radius in uniform Bz.
float statePhiPos(const TrackParametersAtRadius &state)
Global azimuth of the propagated position, atan2(y,x), or NaN for an invalid state.
std::vector< float > primaryDSectorAtRadiusBatch(const float *phi, const float *tgl, const float *qpt, const float *vz, std::size_t n, float radius, float bzKG, int nSectors=18, float sectorPhaseRad=0.f)
TrackParametersAtRadiusBatchResult primaryTrackParametersAtRadiusK0Batch(const float *phi, const float *tgl, const float *qpt, const float *vx, const float *vy, const float *vz, std::size_t n, float radius, float bzKG, int nSectors=18, float sectorPhaseRad=0.f)
Batch K0 transport for reduced primary-like inputs.
o2::track::TrackPar makeTrackParFromPrimaryApproximation(float phi, float tgl, float qpt, float vx=0.f, float vy=0.f, float vz=0.f)
Build an O2 TrackPar from a reduced primary-like state.
std::vector< float > primaryPhiAtRadiusBatch(const float *phi, const float *tgl, const float *qpt, const float *vz, std::size_t n, float radius, float bzKG)
Backward-compatible batch wrappers retained for existing commissioning notebooks/tests.
TrackParametersAtRadiusBatchResult primaryTrackParametersAtRadiusHBatch(const float *phi, const float *tgl, const float *qpt, const float *vx, const float *vy, const float *vz, std::size_t n, float radius, float bzKG, int nSectors=18, float sectorPhaseRad=0.f)
Batch H transport for reduced primary-like inputs.
o2::track::TrackPar makeTrackParFromLocalParameters(float x, float alpha, float y, float z, float snp, float tgl, float qpt, int absCharge=1)
Build an O2 TrackPar from the standard local O2 track parameterization.
TrackParametersAtRadiusBatchResult localTrackParametersAtRadiusHBatch(const float *x, const float *alpha, const float *y, const float *z, const float *snp, const float *tgl, const float *qpt, std::size_t n, float radius, float bzKG, int nSectors=18, float sectorPhaseRad=0.f)
Batch H transport for tracks supplied in local O2 parameterization.
float dSector(float phi, int nSectors=18, float sectorPhaseRad=0.f)
Fractional azimuthal position inside a periodic sector pattern.
bool directionMatches(o2::track::DirType dir, float drds)
Check whether the radial derivative is compatible with the requested propagation direction.
o2::track::TrackPar makePrimaryTrackPar(float phi, float tgl, float qpt, float vz=0.f)
Backward-compatible reduced-primary constructor used by existing commissioning code.
float primaryPhiAtRadius(float phi, float tgl, float qpt, float radius, float bzKG, float vz=0.f)
Position azimuth at radius for the reduced primary-like parameterization, evaluated with K0.
PrimaryTrackGeometryBatchResult primaryTrackGeometryAtRadiusBatch(const float *phi, const float *tgl, const float *qpt, const float *vz, std::size_t n, float radius, float bzKG, int nSectors=18, float sectorPhaseRad=0.f)
float dSectorAtRadius(const o2::track::TrackPar &track, float radius, float bzKG, int nSectors=18, o2::track::DirType dir=o2::track::DirType::DirOutward, float sectorPhaseRad=0.f)
Backward-compatible scalar wrapper returning only the propagated fractional sector coordinate using K...
TPCSectorEdgeResult sectorEdgeO2PropagatorRows(const o2::track::TrackPar &inputTrack, float bzKG, const TPCSectorEdgeConfig &cfg={}, std::vector< o2::recohelpers::TrackParametersAtRadius > *trajectory=nullptr)
K row evaluator: O2 Propagator traversal of TPC row radii without material correction.
float trajectoryRadialSnp(const TrajectoryPoint &p)
Radial-frame sin(phiMom-phiPos); independent of the O2 TrackPar alpha rotation.
TPCSectorEdgeBatchResult sectorEdgeStackLinearBatch(const float *phi, const float *tgl, const float *qpt, const float *vz, std::size_t n, float bzKG, const TPCSectorEdgeConfig &cfg={})
Batch wrapper for the stack-linear L TPC row evaluator.
TPCSectorEdgeBatchResult localSectorEdgeO2PropagatorRowsBatch(const float *x, const float *alpha, const float *y, const float *z, const float *snp, const float *tgl, const float *qpt, std::size_t n, float bzKG, const TPCSectorEdgeConfig &cfg={})
TPCSectorEdgeBatchResult sectorEdgeBatch(const float *phi, const float *tgl, const float *qpt, const float *vz, std::size_t n, float bzKG, const TPCSectorEdgeConfig &cfg, Evaluator &&evaluate)
TPCSectorEdgeResult sectorEdgeHelixRows(const o2::track::TrackPar &track, float bzKG, const TPCSectorEdgeConfig &cfg={}, std::vector< o2::recohelpers::TrackParametersAtRadius > *trajectory=nullptr)
H row evaluator: analytic helix sampled at the current Mapper row radii.
bool rowWithinAcceptance(const TrajectoryPoint &p, const TPCSectorEdgeConfig &cfg)
True when a valid row-centre trajectory point satisfies the configured z and snp acceptance.
void setMaskBit(std::array< uint32_t, 5 > &mask, int row)
Set one of the 152 TPC row bits in the five-word row mask.
float trajectoryLocalSnp(const TrajectoryPoint &p)
Backward-compatible internal spelling; use trajectoryRadialSnp for new code.
TPCSectorEdgeBatchResult localSectorEdgeStackLinearBatch(const float *x, const float *alpha, const float *y, const float *z, const float *snp, const float *tgl, const float *qpt, std::size_t n, float bzKG, const TPCSectorEdgeConfig &cfg={})
bool plausibleFirstOutwardStep(float dr, float ds, float tgl, float maxSnp)
Conservative path-continuity guard against a later outward orbit.
TPCSectorEdgeBatchResult sectorEdgeO2PropagatorRowsBatch(const float *phi, const float *tgl, const float *qpt, const float *vz, std::size_t n, float bzKG, const TPCSectorEdgeConfig &cfg={})
Batch wrapper for the O2 Propagator K TPC row evaluator, without material correction.
TPCSectorEdgeResult sectorEdgeStackLinear(const o2::track::TrackPar &track, float bzKG, const TPCSectorEdgeConfig &cfg={}, std::vector< o2::recohelpers::TrackParametersAtRadius > *trajectory=nullptr)
L row evaluator: stack-local linear approximation to the analytic helix.
float rowLengthRadial(const o2::tpc::Mapper &mapper, int row)
Radial row thickness, taken from the Mapper pad height [cm].
SectorEdgeStatus
Status returned by the TPC row/sector-edge evaluators.
float rowActiveHalfWidth(const o2::tpc::Mapper &mapper, int row)
Active half-width of one TPC row in local-y from Mapper pad count and pad width [cm].
TPCSectorEdgeBatchResult localSectorEdgeBatch(const float *x, const float *alpha, const float *y, const float *z, const float *snp, const float *tgl, const float *qpt, std::size_t n, float bzKG, const TPCSectorEdgeConfig &cfg, Evaluator &&evaluate)
Evaluate row acceptance for a batch of complete O2 local 5D track states. Input arrays are x,...
bool reachedMaxSnp(const TrajectoryPoint &p, const TPCSectorEdgeConfig &cfg)
True when the radial-frame inclination reaches the configured stopping boundary.
TPCSectorEdgeBatchResult sectorEdgeHelixRowsBatch(const float *phi, const float *tgl, const float *qpt, const float *vz, std::size_t n, float bzKG, const TPCSectorEdgeConfig &cfg={})
Batch wrapper for the analytic H TPC row evaluator.
TrajectoryPoint primaryHelixAtRadius(const TPCPrimaryTrackSeed &seed, float radius)
Legacy reduced-primary adapter to the same complete-track H implementation.
bool trackOutwardReference(const o2::track::TrackPar &track, float &radius)
Validate the complete input state and report its global reference radius. An outward-only row query d...
TrajectoryPoint helixAtRadius(const o2::track::TrackPar &track, float radius, float bzKG)
One TPC row trajectory sample from a complete O2 local five-parameter track.
TPCSectorEdgeBatchResult localSectorEdgeHelixRowsBatch(const float *x, const float *alpha, const float *y, const float *z, const float *snp, const float *tgl, const float *qpt, std::size_t n, float bzKG, const TPCSectorEdgeConfig &cfg={})
void appendTrajectoryState(std::vector< o2::recohelpers::TrackParametersAtRadius > &trajectory, const TrajectoryPoint &p, float qpt, float tgl)
Debug-only row state; trajectory[row] is the state at that sampled TPC row. Append even the first sta...
float rowRadius(const o2::tpc::Mapper &mapper, int row)
Current sampled TPC row radius [cm]; F6 physical pad-centre correction remains pending.
int regionForRow(const o2::tpc::Mapper &mapper, int row)
Return the Mapper pad-region index containing a global TPC row, or -1 if none.
void accumulateRow(const o2::tpc::Mapper &mapper, int row, const TrajectoryPoint &p, float rowLength, const TPCSectorEdgeConfig &cfg, TPCSectorEdgeResult &out)
Apply acceptance and classify one crossed row as Dead, Excluded, or Available.
TrackParF TrackPar
Definition Track.h:29
Legacy compact batch result containing only position azimuth and fractional sector coordinate.
Structure-of-arrays result for batch cylindrical-radius propagation.
void set(std::size_t i, const TrackParametersAtRadius &state, int nSectors, float sectorPhaseRad)
Global track state evaluated at a requested cylindrical radius.
Reduced primary-track seed used by the TPC row evaluators.
Structure-of-arrays batch result for the TPC row/sector-edge evaluators.
void set(std::size_t i, const TPCSectorEdgeResult &r)
Configuration for TPC row acceptance and sector-edge classification.
Result of one TPC row/sector-edge evaluation.
std::array< uint32_t, 5 > availableMask
Internal global trajectory sample at one TPC row-centre radius.
o2::mch::DsIndex ds
std::vector< int > row