File indexing completed on 2026-07-26 08:19:00
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Seeding/TripletSeedFinder.hpp"
0010
0011 #include "Acts/EventData/SpacePointContainer.hpp"
0012 #include "Acts/EventData/StripSpacePointCalibrationDetails.hpp"
0013 #include "Acts/SpacePointFormation/detail/StripSpacePointCalibrationImpl.hpp"
0014 #include "Acts/Utilities/MathHelpers.hpp"
0015 #include "Acts/Utilities/Zip.hpp"
0016
0017 #include <ranges>
0018
0019 #include <Eigen/Dense>
0020 #include <boost/mp11.hpp>
0021 #include <boost/mp11/algorithm.hpp>
0022
0023 namespace Acts {
0024
0025 namespace {
0026
0027 template <bool useStripInfo, bool sortedByCotTheta>
0028 class Impl final : public TripletSeedFinder {
0029 public:
0030 explicit Impl(const DerivedConfig& config) : m_cfg(config) {}
0031
0032 const DerivedConfig& config() const override { return m_cfg; }
0033
0034 template <typename TopDoublets>
0035 void createPixelTripletTopCandidates(
0036 const ConstSpacePointProxy& spM,
0037 const DoubletsForMiddleSp::Proxy& bottomDoublet, TopDoublets& topDoublets,
0038 TripletTopCandidates& tripletTopCandidates) const {
0039 const float rM = spM.zr()[1];
0040 const float varianceZM = spM.varianceZ();
0041 const float varianceRM = spM.varianceR();
0042
0043
0044 tripletTopCandidates.reserve(tripletTopCandidates.size() +
0045 topDoublets.size());
0046
0047 const float cotThetaB = bottomDoublet.cotTheta();
0048 const float erB = bottomDoublet.er();
0049 const float iDeltaRB = bottomDoublet.iDeltaR();
0050 const float Ub = bottomDoublet.u();
0051 const float Vb = bottomDoublet.v();
0052
0053
0054 const float iSinTheta2 = 1 + cotThetaB * cotThetaB;
0055 const float sigmaSquaredPtDependent = iSinTheta2 * m_cfg.sigmapT2perRadius;
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065 const float scatteringInRegion2 = m_cfg.multipleScattering2 * iSinTheta2;
0066
0067 std::size_t topDoubletOffset = 0;
0068 for (auto [topDoublet, topDoubletIndex] :
0069 zip(topDoublets, std::ranges::iota_view<std::size_t, std::size_t>(
0070 0, topDoublets.size()))) {
0071 const SpacePointIndex spT = topDoublet.spacePointIndex();
0072 const float cotThetaT = topDoublet.cotTheta();
0073
0074
0075 const float cotThetaAvg2 = cotThetaB * cotThetaT;
0076
0077
0078
0079 const float error2 = topDoublet.er() + erB +
0080 2 * (cotThetaAvg2 * varianceRM + varianceZM) *
0081 iDeltaRB * topDoublet.iDeltaR();
0082
0083 const float deltaCotTheta = cotThetaB - cotThetaT;
0084 const float deltaCotTheta2 = deltaCotTheta * deltaCotTheta;
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096 if (deltaCotTheta2 > error2 + scatteringInRegion2) {
0097 if constexpr (sortedByCotTheta) {
0098
0099
0100
0101 if (cotThetaB < cotThetaT) {
0102 break;
0103 }
0104 topDoubletOffset = topDoubletIndex + 1;
0105 }
0106 continue;
0107 }
0108
0109 const float dU = topDoublet.u() - Ub;
0110
0111 if (dU == 0) {
0112 continue;
0113 }
0114
0115
0116 const float A = (topDoublet.v() - Vb) / dU;
0117 const float S2 = 1 + A * A;
0118 const float B = Vb - A * Ub;
0119 const float B2 = B * B;
0120
0121
0122
0123 if (S2 < B2 * m_cfg.minHelixDiameter2) {
0124 continue;
0125 }
0126
0127
0128
0129
0130 const float iHelixDiameter2 = B2 / S2;
0131
0132
0133 const float p2scatterSigma = iHelixDiameter2 * sigmaSquaredPtDependent;
0134
0135 if (deltaCotTheta2 > error2 + p2scatterSigma) {
0136 if constexpr (sortedByCotTheta) {
0137 if (cotThetaB < cotThetaT) {
0138 break;
0139 }
0140 topDoubletOffset = topDoubletIndex;
0141 }
0142 continue;
0143 }
0144
0145
0146
0147
0148 const float im = std::abs((A - B * rM) * rM);
0149 if (im > m_cfg.impactMax) {
0150 continue;
0151 }
0152
0153
0154
0155 tripletTopCandidates.emplace_back(spT, B / std::sqrt(S2), im);
0156 }
0157
0158 if constexpr (sortedByCotTheta) {
0159
0160 topDoublets = topDoublets.subrange(topDoubletOffset);
0161 }
0162 }
0163
0164 template <typename TopDoublets>
0165 void createStripTripletTopCandidates(
0166 const SpacePointContainer& spacePoints, const ConstSpacePointProxy& spM,
0167 const DoubletsForMiddleSp::Proxy& bottomDoublet, TopDoublets& topDoublets,
0168 TripletTopCandidates& tripletTopCandidates) const {
0169 const float rM = spM.zr()[1];
0170 const float cosPhiM = spM.xy()[0] / rM;
0171 const float sinPhiM = spM.xy()[1] / rM;
0172 const float varianceZM = spM.varianceZ();
0173 const float varianceRM = spM.varianceR();
0174
0175
0176 tripletTopCandidates.reserve(tripletTopCandidates.size() +
0177 topDoublets.size());
0178
0179 const float cotThetaB0 = bottomDoublet.cotTheta();
0180 const float erB = bottomDoublet.er();
0181 const float iDeltaRB = bottomDoublet.iDeltaR();
0182 const float Ub0 = bottomDoublet.u();
0183 const float Vb0 = bottomDoublet.v();
0184
0185
0186 const float iSinTheta2 = 1 + cotThetaB0 * cotThetaB0;
0187 const float sigmaSquaredPtDependent = iSinTheta2 * m_cfg.sigmapT2perRadius;
0188
0189
0190
0191
0192
0193
0194
0195
0196
0197 const float scatteringInRegion2 = m_cfg.multipleScattering2 * iSinTheta2;
0198
0199 const float sinTheta = 1 / std::sqrt(iSinTheta2);
0200 const float cosTheta = cotThetaB0 * sinTheta;
0201
0202
0203
0204 const std::array<float, 2> rotationTermsUVtoXY = {cosPhiM * sinTheta,
0205 sinPhiM * sinTheta};
0206
0207
0208 const OuterStripSpacePointCalibrationDetailsDerived calM =
0209 detail::deriveOuterStripSpacePointCalibrationDetails(
0210 spM.outerStripCalibrationDetails());
0211 const ConstSpacePointProxy spB =
0212 spacePoints[bottomDoublet.spacePointIndex()];
0213 const OuterStripSpacePointCalibrationDetailsDerived calB =
0214 detail::deriveOuterStripSpacePointCalibrationDetails(
0215 spB.outerStripCalibrationDetails());
0216
0217 std::size_t topDoubletOffset = 0;
0218 for (auto [topDoublet, topDoubletIndex] :
0219 zip(topDoublets, std::ranges::iota_view<std::size_t, std::size_t>(
0220 0, topDoublets.size()))) {
0221
0222
0223
0224
0225
0226
0227
0228
0229
0230
0231
0232 if constexpr (sortedByCotTheta) {
0233 const float cotThetaT = topDoublet.cotTheta();
0234 const float deltaCotTheta = cotThetaB0 - cotThetaT;
0235 const float cotThetaDiffMax2 =
0236 m_cfg.cotThetaDiffMax * m_cfg.cotThetaDiffMax;
0237 if (deltaCotTheta * deltaCotTheta > cotThetaDiffMax2) {
0238 if (cotThetaB0 < cotThetaT) {
0239 break;
0240 }
0241 topDoubletOffset = topDoubletIndex + 1;
0242 continue;
0243 }
0244 }
0245
0246
0247 const float dU0 = topDoublet.u() - Ub0;
0248 if (dU0 == 0) {
0249 continue;
0250 }
0251
0252
0253 const float A0 = (topDoublet.v() - Vb0) / dU0;
0254
0255
0256
0257
0258
0259 const std::array<float, 3> directionMiddle = {
0260 rotationTermsUVtoXY[0] - rotationTermsUVtoXY[1] * A0,
0261 rotationTermsUVtoXY[0] * A0 + rotationTermsUVtoXY[1], cosTheta};
0262
0263 std::array<float, 3> rMTransf{};
0264 if (!detail::calibrateOuterStripSpacePoint(
0265 directionMiddle, calM, rMTransf, m_cfg.toleranceParam)) {
0266 continue;
0267 }
0268
0269
0270 const float zDirectionMiddle = cosTheta * std::sqrt(1 + A0 * A0);
0271
0272
0273 const float B0 = 2 * (Vb0 - A0 * Ub0);
0274 const float Cb = 1 - B0 * bottomDoublet.y();
0275 const float Sb = A0 + B0 * bottomDoublet.x();
0276 const std::array<float, 3> directionBottom = {
0277 rotationTermsUVtoXY[0] * Cb - rotationTermsUVtoXY[1] * Sb,
0278 rotationTermsUVtoXY[0] * Sb + rotationTermsUVtoXY[1] * Cb,
0279 zDirectionMiddle};
0280
0281 std::array<float, 3> rBTransf{};
0282 if (!detail::calibrateOuterStripSpacePoint(
0283 directionBottom, calB, rBTransf, m_cfg.toleranceParam)) {
0284 continue;
0285 }
0286
0287
0288 const float Ct = 1 - B0 * topDoublet.y();
0289 const float St = A0 + B0 * topDoublet.x();
0290 const std::array<float, 3> directionTop = {
0291 rotationTermsUVtoXY[0] * Ct - rotationTermsUVtoXY[1] * St,
0292 rotationTermsUVtoXY[0] * St + rotationTermsUVtoXY[1] * Ct,
0293 zDirectionMiddle};
0294
0295 const ConstSpacePointProxy spT =
0296 spacePoints[topDoublet.spacePointIndex()];
0297 const OuterStripSpacePointCalibrationDetailsDerived calT =
0298 detail::deriveOuterStripSpacePointCalibrationDetails(
0299 spT.outerStripCalibrationDetails());
0300 std::array<float, 3> rTTransf{};
0301 if (!detail::calibrateOuterStripSpacePoint(directionTop, calT, rTTransf,
0302 m_cfg.toleranceParam)) {
0303 continue;
0304 }
0305
0306
0307 const float xB = rBTransf[0] - rMTransf[0];
0308 const float yB = rBTransf[1] - rMTransf[1];
0309 const float zB = rBTransf[2] - rMTransf[2];
0310 const float xT = rTTransf[0] - rMTransf[0];
0311 const float yT = rTTransf[1] - rMTransf[1];
0312 const float zT = rTTransf[2] - rMTransf[2];
0313
0314 const float iDeltaRB2 = 1 / (xB * xB + yB * yB);
0315 const float iDeltaRT2 = 1 / (xT * xT + yT * yT);
0316
0317 const float cotThetaB = -zB * std::sqrt(iDeltaRB2);
0318 const float cotThetaT = zT * std::sqrt(iDeltaRT2);
0319
0320
0321 const float averageCotTheta = 0.5f * (cotThetaB + cotThetaT);
0322 const float cotThetaAvg2 = averageCotTheta * averageCotTheta;
0323
0324
0325
0326 const float error2 = topDoublet.er() + erB +
0327 2 * (cotThetaAvg2 * varianceRM + varianceZM) *
0328 iDeltaRB * topDoublet.iDeltaR();
0329
0330 const float deltaCotTheta = cotThetaB - cotThetaT;
0331 const float deltaCotTheta2 = deltaCotTheta * deltaCotTheta;
0332
0333
0334
0335
0336
0337
0338
0339
0340
0341
0342
0343 if (deltaCotTheta2 > error2 + scatteringInRegion2) {
0344
0345 continue;
0346 }
0347
0348 const float rMxy =
0349 std::sqrt(rMTransf[0] * rMTransf[0] + rMTransf[1] * rMTransf[1]);
0350 const float irMxy = 1 / rMxy;
0351 const float Ax = rMTransf[0] * irMxy;
0352 const float Ay = rMTransf[1] * irMxy;
0353
0354 const float Ub = (xB * Ax + yB * Ay) * iDeltaRB2;
0355 const float Vb = (yB * Ax - xB * Ay) * iDeltaRB2;
0356 const float Ut = (xT * Ax + yT * Ay) * iDeltaRT2;
0357 const float Vt = (yT * Ax - xT * Ay) * iDeltaRT2;
0358
0359 const float dU = Ut - Ub;
0360
0361 if (dU == 0) {
0362 continue;
0363 }
0364 const float A = (Vt - Vb) / dU;
0365 const float S2 = 1 + A * A;
0366 const float B = Vb - A * Ub;
0367 const float B2 = B * B;
0368
0369
0370
0371 if (S2 < B2 * m_cfg.minHelixDiameter2) {
0372 continue;
0373 }
0374
0375
0376
0377
0378 const float iHelixDiameter2 = B2 / S2;
0379
0380
0381 const float p2scatterSigma = iHelixDiameter2 * sigmaSquaredPtDependent;
0382
0383 if (deltaCotTheta2 > error2 + p2scatterSigma) {
0384 continue;
0385 }
0386
0387
0388
0389
0390 const float im = std::abs((A - B * rMxy) * rMxy);
0391 if (im > m_cfg.impactMax) {
0392 continue;
0393 }
0394
0395
0396
0397 tripletTopCandidates.emplace_back(topDoublet.spacePointIndex(),
0398 B / std::sqrt(S2), im);
0399 }
0400
0401 if constexpr (sortedByCotTheta) {
0402
0403
0404 topDoublets = topDoublets.subrange(topDoubletOffset);
0405 }
0406 }
0407
0408 void createTripletTopCandidates(
0409 const SpacePointContainer& spacePoints, const ConstSpacePointProxy& spM,
0410 const DoubletsForMiddleSp::Proxy& bottomDoublet,
0411 DoubletsForMiddleSp::Range& topDoublets,
0412 TripletTopCandidates& tripletTopCandidates) const override {
0413 if constexpr (useStripInfo) {
0414 createStripTripletTopCandidates(spacePoints, spM, bottomDoublet,
0415 topDoublets, tripletTopCandidates);
0416 } else {
0417 createPixelTripletTopCandidates(spM, bottomDoublet, topDoublets,
0418 tripletTopCandidates);
0419 }
0420 }
0421
0422 void createTripletTopCandidates(
0423 const SpacePointContainer& spacePoints, const ConstSpacePointProxy& spM,
0424 const DoubletsForMiddleSp::Proxy& bottomDoublet,
0425 DoubletsForMiddleSp::Subset& topDoublets,
0426 TripletTopCandidates& tripletTopCandidates) const override {
0427 if constexpr (useStripInfo) {
0428 createStripTripletTopCandidates(spacePoints, spM, bottomDoublet,
0429 topDoublets, tripletTopCandidates);
0430 } else {
0431 createPixelTripletTopCandidates(spM, bottomDoublet, topDoublets,
0432 tripletTopCandidates);
0433 }
0434 }
0435
0436 void createTripletTopCandidates(
0437 const SpacePointContainer& spacePoints, const ConstSpacePointProxy& spM,
0438 const DoubletsForMiddleSp::Proxy& bottomDoublet,
0439 DoubletsForMiddleSp::Subset2& topDoublets,
0440 TripletTopCandidates& tripletTopCandidates) const override {
0441 if constexpr (useStripInfo) {
0442 createStripTripletTopCandidates(spacePoints, spM, bottomDoublet,
0443 topDoublets, tripletTopCandidates);
0444 } else {
0445 createPixelTripletTopCandidates(spM, bottomDoublet, topDoublets,
0446 tripletTopCandidates);
0447 }
0448 }
0449
0450 private:
0451 DerivedConfig m_cfg;
0452 };
0453
0454 }
0455
0456 TripletSeedFinder::DerivedConfig::DerivedConfig(const Config& config,
0457 float bFieldInZ_)
0458 : Config(config), bFieldInZ(bFieldInZ_) {
0459
0460 {
0461 using namespace Acts::UnitLiterals;
0462 const double xOverX0 = radLengthPerSeed;
0463 const double q2OverBeta2 = 1;
0464
0465 const double t = std::sqrt(xOverX0 * q2OverBeta2);
0466
0467
0468 highland =
0469 static_cast<float>(13.6_MeV * t * (1.0 + 0.038 * 2 * std::log(t)));
0470 }
0471
0472 const float maxScatteringAngle = highland / minPt;
0473 const float maxScatteringAngle2 = maxScatteringAngle * maxScatteringAngle;
0474
0475
0476 pTPerHelixRadius = bFieldInZ;
0477 minHelixDiameter2 = square(minPt * 2 / pTPerHelixRadius) * helixCutTolerance;
0478 const float pT2perRadius = square(highland / pTPerHelixRadius);
0479 sigmapT2perRadius = pT2perRadius * square(2 * sigmaScattering);
0480 multipleScattering2 = maxScatteringAngle2 * square(sigmaScattering);
0481 }
0482
0483 std::unique_ptr<TripletSeedFinder> TripletSeedFinder::create(
0484 const DerivedConfig& config) {
0485 using BooleanOptions =
0486 boost::mp11::mp_list<std::bool_constant<false>, std::bool_constant<true>>;
0487
0488 using UseStripInfoOptions = BooleanOptions;
0489 using SortedByCotThetaOptions = BooleanOptions;
0490
0491 using TripletOptions =
0492 boost::mp11::mp_product<boost::mp11::mp_list, UseStripInfoOptions,
0493 SortedByCotThetaOptions>;
0494
0495 std::unique_ptr<TripletSeedFinder> result;
0496 boost::mp11::mp_for_each<TripletOptions>([&](auto option) {
0497 using OptionType = decltype(option);
0498
0499 using UseStripInfo = boost::mp11::mp_at_c<OptionType, 0>;
0500 using SortedByCotTheta = boost::mp11::mp_at_c<OptionType, 1>;
0501
0502 if (config.useStripInfo != UseStripInfo::value ||
0503 config.sortedByCotTheta != SortedByCotTheta::value) {
0504 return;
0505 }
0506
0507
0508 if (result != nullptr) {
0509 throw std::runtime_error(
0510 "TripletSeedFinder: Multiple implementations found for one "
0511 "configuration");
0512 }
0513
0514
0515 result =
0516 std::make_unique<Impl<UseStripInfo::value, SortedByCotTheta::value>>(
0517 config);
0518 });
0519 if (result == nullptr) {
0520 throw std::runtime_error(
0521 "TripletSeedFinder: No implementation found for the given "
0522 "configuration");
0523 }
0524 return result;
0525 }
0526
0527 }