File indexing completed on 2025-12-14 09:23:07
0001
0002
0003
0004
0005
0006
0007
0008 #pragma once
0009 #include "Acts/Definitions/Units.hpp"
0010 #include "Acts/Seeding/CompositeSpacePointLineFitter.hpp"
0011 #include "Acts/Seeding/CompositeSpacePointLineSeeder.hpp"
0012 #include "Acts/Utilities/Enumerate.hpp"
0013 #include "Acts/Utilities/Logger.hpp"
0014 #include "Acts/Utilities/StringHelpers.hpp"
0015 #include "Acts/Utilities/UnitVectors.hpp"
0016 #include "Acts/Utilities/VectorHelpers.hpp"
0017 #include "Acts/Utilities/detail/Polynomials.hpp"
0018
0019 #include <random>
0020 #include <ranges>
0021
0022 using namespace Acts;
0023 using namespace Acts::UnitLiterals;
0024 using namespace Acts::VectorHelpers;
0025 using namespace Acts::Experimental::detail;
0026 using namespace Acts::Experimental;
0027 using namespace Acts::detail::LineHelper;
0028 using namespace Acts::PlanarHelper;
0029
0030 using RandomEngine = std::mt19937;
0031
0032 using ResidualIdx = CompSpacePointAuxiliaries::ResidualIdx;
0033 using Line_t = CompSpacePointAuxiliaries::Line_t;
0034 using normal_t = std::normal_distribution<double>;
0035 using uniform = std::uniform_real_distribution<double>;
0036 using FitParIndex = CompSpacePointAuxiliaries::FitParIndex;
0037 using ParamVec_t = CompositeSpacePointLineFitter::ParamVec_t;
0038
0039 namespace ActsTests {
0040
0041 class FitTestSpacePoint {
0042 public:
0043
0044
0045
0046
0047
0048 FitTestSpacePoint(const Vector3& pos, const double driftR,
0049 const double driftRUncert,
0050 const std::optional<double> twinUncert = std::nullopt)
0051 : m_position{pos},
0052 m_driftR{driftR},
0053 m_measLoc0{twinUncert != std::nullopt} {
0054 using enum ResidualIdx;
0055 m_covariance[toUnderlying(bending)] = Acts::square(driftRUncert);
0056 m_covariance[toUnderlying(nonBending)] =
0057 Acts::square(twinUncert.value_or(0.));
0058 }
0059
0060
0061
0062
0063
0064
0065 FitTestSpacePoint(const Vector3& pos, const Vector3& wire,
0066 const double driftR, const double driftRUncert,
0067 const std::optional<double> twinUncert = std::nullopt)
0068 : m_position{pos},
0069 m_sensorDir{wire},
0070 m_driftR{driftR},
0071 m_measLoc0{twinUncert != std::nullopt} {
0072 using enum ResidualIdx;
0073 m_covariance[toUnderlying(bending)] = Acts::square(driftRUncert);
0074 m_covariance[toUnderlying(nonBending)] =
0075 Acts::square(twinUncert.value_or(0.));
0076 }
0077
0078
0079
0080
0081
0082
0083
0084 FitTestSpacePoint(const Vector3& stripPos, const Vector3& stripDir,
0085 const Vector3& toNext, const double uncertLoc0,
0086 const double uncertLoc1)
0087 : m_position{stripPos},
0088 m_sensorDir{stripDir},
0089 m_toNextSen{toNext},
0090 m_measLoc0{uncertLoc0 > 0.},
0091 m_measLoc1{uncertLoc1 > 0.} {
0092 using enum ResidualIdx;
0093 m_covariance[toUnderlying(nonBending)] = Acts::square(uncertLoc0);
0094 m_covariance[toUnderlying(bending)] = Acts::square(uncertLoc1);
0095 }
0096
0097 FitTestSpacePoint(const Vector3& stripPos, const Vector3& stripDir,
0098 const Vector3& toNext, const double time,
0099 const std::array<double, 3>& cov)
0100 : m_position{stripPos},
0101 m_sensorDir{stripDir},
0102 m_toNextSen{toNext},
0103 m_time{time},
0104 m_covariance{cov} {
0105 m_measLoc0 = m_covariance[toUnderlying(ResidualIdx::nonBending)] > 0.;
0106 m_measLoc1 = m_covariance[toUnderlying(ResidualIdx::bending)] > 0.;
0107 }
0108
0109
0110 const Vector3& localPosition() const { return m_position; }
0111
0112 const Vector3& sensorDirection() const { return m_sensorDir; }
0113
0114 const Vector3& toNextSensor() const { return m_toNextSen; }
0115
0116 const Vector3& planeNormal() const { return m_planeNorm; }
0117
0118 double driftRadius() const { return m_driftR.value_or(0.); }
0119
0120 const std::array<double, 3>& covariance() const { return m_covariance; }
0121
0122 double time() const { return m_time.value_or(0.); }
0123
0124 bool isStraw() const { return m_driftR.has_value(); }
0125
0126 bool hasTime() const { return m_time.has_value(); }
0127
0128 bool measuresLoc0() const { return m_measLoc0; }
0129
0130 bool measuresLoc1() const { return m_measLoc1 || isStraw(); }
0131
0132 void updateDriftR(const double updatedR) { m_driftR = updatedR; }
0133
0134 void updatePosition(const Vector3& newPos) { m_position = newPos; }
0135
0136 void updateTime(const double newTime) { m_time = newTime; }
0137
0138 void updateStatus(const bool newStatus) { m_isGood = newStatus; }
0139
0140 bool isGood() const { return m_isGood; }
0141
0142 std::size_t layer() const { return m_layer.value_or(0ul); }
0143
0144 void setLayer(const std::size_t lay) {
0145 if (!m_layer) {
0146 m_layer = lay;
0147 }
0148 }
0149
0150 private:
0151 Vector3 m_position{Vector3::Zero()};
0152 Vector3 m_sensorDir{Vector3::UnitX()};
0153 Vector3 m_toNextSen{Vector3::UnitY()};
0154 Vector3 m_planeNorm{m_sensorDir.cross(m_toNextSen).normalized()};
0155 std::optional<double> m_driftR{std::nullopt};
0156 std::optional<double> m_time{std::nullopt};
0157 std::array<double, 3> m_covariance{filledArray<double, 3>(0)};
0158 bool m_measLoc0{false};
0159 bool m_measLoc1{false};
0160 bool m_isGood{true};
0161 std::optional<std::size_t> m_layer{};
0162 };
0163
0164 static_assert(CompositeSpacePoint<FitTestSpacePoint>);
0165
0166 using Container_t = std::vector<std::shared_ptr<FitTestSpacePoint>>;
0167 static_assert(CompositeSpacePointContainer<Container_t>);
0168
0169 class SpCalibrator {
0170 public:
0171 SpCalibrator() = default;
0172
0173 explicit SpCalibrator(const Acts::Transform3& localToGlobal)
0174 : m_localToGlobal{localToGlobal} {}
0175
0176
0177 static double normalize(const double value, const double min,
0178 const double max) {
0179 return 2. * (std::clamp(value, min, max) - min) / (max - min) - 1.;
0180 }
0181
0182 static constexpr double s_minDriftTime = 0._ns;
0183 static constexpr double s_maxDriftTime = 741.324 * 1._ns;
0184
0185 static double normDriftTime(const double t) {
0186 return normalize(t, s_minDriftTime, s_maxDriftTime);
0187 }
0188
0189 static constexpr double s_timeNormFactor =
0190 2.0 / (s_maxDriftTime - s_minDriftTime);
0191
0192 static constexpr std::array<double, 9> s_TtoRcoeffs = {
0193 9.0102, 6.7419, -1.5829, 0.56475, -0.19245,
0194 0.019055, 0.030006, -0.034591, 0.023517};
0195
0196 static double driftRadius(const double t) {
0197 const double x = normDriftTime(t);
0198 double r{0.0};
0199 for (std::size_t n = 0; n < s_TtoRcoeffs.size(); ++n) {
0200 r += s_TtoRcoeffs[n] * Acts::detail::chebychevPolyTn(x, n);
0201 }
0202 return r;
0203 }
0204
0205 static double driftVelocity(const double t) {
0206 const double x = normDriftTime(t);
0207 double v{0.0};
0208 for (std::size_t n = 1; n < s_TtoRcoeffs.size(); ++n) {
0209 v += s_TtoRcoeffs[n] * Acts::detail::chebychevPolyUn(x, n, 1u);
0210 }
0211 return v * s_timeNormFactor;
0212 }
0213
0214 static double driftAcceleration(const double t) {
0215 const double x = normDriftTime(t);
0216 double a{0.0};
0217 for (std::size_t n = 2; n < s_TtoRcoeffs.size(); ++n) {
0218 a += s_TtoRcoeffs[n] * Acts::detail::chebychevPolyUn(x, n, 2u);
0219 }
0220 return a * Acts::square(s_timeNormFactor);
0221 }
0222
0223 static constexpr double s_minDriftRadius = 0.064502;
0224 static constexpr double s_maxDriftRadius = 14.566;
0225
0226 static double normDriftRadius(const double r) {
0227 return normalize(r, s_minDriftRadius, s_maxDriftRadius);
0228 }
0229
0230 static constexpr std::array<double, 5> s_RtoTcoeffs = {
0231 256.476, 349.056, 118.349, 18.748, -6.4142};
0232
0233 static double driftTime(const double r) {
0234 const double x = normDriftRadius(r);
0235 double t{0.0};
0236 for (std::size_t n = 0; n < s_RtoTcoeffs.size(); ++n) {
0237 t += s_RtoTcoeffs[n] * Acts::detail::legendrePoly(x, n);
0238 }
0239 return t * 1._ns;
0240 }
0241
0242 static constexpr std::array<double, 4> s_driftRUncertCoeffs{
0243 0.10826, -0.07182, 0.037597, -0.011712};
0244
0245 static double driftUncert(const double r) {
0246 const double x = normDriftRadius(r);
0247 double s{0.0};
0248 for (std::size_t n = 0; n < s_driftRUncertCoeffs.size(); ++n) {
0249 s += s_driftRUncertCoeffs[n] * Acts::detail::chebychevPolyTn(x, n);
0250 }
0251 return s;
0252 }
0253
0254
0255
0256
0257
0258 static double driftRadius(const Acts::CalibrationContext& ,
0259 const FitTestSpacePoint& measurement,
0260 const double timeOffSet) {
0261 if (!measurement.isStraw() || !measurement.isGood()) {
0262 return 0.;
0263 }
0264 return driftRadius(Acts::abs(measurement.time() - timeOffSet));
0265 }
0266
0267
0268
0269
0270
0271 static double driftVelocity(const Acts::CalibrationContext& ,
0272 const FitTestSpacePoint& measurement,
0273 const double timeOffSet) {
0274 if (!measurement.isStraw() || !measurement.isGood()) {
0275 return 0.;
0276 }
0277 return driftVelocity(Acts::abs(measurement.time() - timeOffSet));
0278 }
0279
0280
0281
0282
0283
0284 static double driftAcceleration(const Acts::CalibrationContext& ,
0285 const FitTestSpacePoint& measurement,
0286 const double timeOffSet) {
0287 if (!measurement.isStraw() || !measurement.isGood()) {
0288 return 0.;
0289 }
0290 return driftAcceleration(Acts::abs(measurement.time() - timeOffSet));
0291 }
0292
0293
0294
0295
0296
0297 double closestApproachDist(const Vector3& trackPos, const Vector3& trackDir,
0298 const FitTestSpacePoint& measurement) const {
0299 const Vector3 closePointOnLine =
0300 lineIntersect(measurement.localPosition(),
0301 measurement.sensorDirection(), trackPos, trackDir)
0302 .position();
0303 return (m_localToGlobal * closePointOnLine).norm();
0304 }
0305 std::shared_ptr<FitTestSpacePoint> calibrate(
0306 const Acts::CalibrationContext& , const Vector3& trackPos,
0307 const Vector3& trackDir, const double timeOffSet,
0308 const FitTestSpacePoint& sp) const {
0309 auto copySp = std::make_shared<FitTestSpacePoint>(sp);
0310 if (!copySp->measuresLoc0() || !copySp->measuresLoc1()) {
0311
0312 auto bestPos = lineIntersect(trackPos, trackDir, copySp->localPosition(),
0313 copySp->sensorDirection());
0314 copySp->updatePosition(bestPos.position());
0315 }
0316 if (copySp->isStraw()) {
0317 const double driftTime{copySp->time() - timeOffSet -
0318 closestApproachDist(trackPos, trackDir, sp) /
0319 PhysicalConstants::c};
0320 if (driftTime < 0) {
0321 copySp->updateStatus(false);
0322 copySp->updateDriftR(0.);
0323 } else {
0324 copySp->updateStatus(true);
0325 copySp->updateDriftR(Acts::abs(driftRadius(driftTime)));
0326 }
0327 }
0328 return copySp;
0329 }
0330
0331
0332
0333
0334
0335
0336 Container_t calibrate(const Acts::CalibrationContext& ctx,
0337 const Vector3& trackPos, const Vector3& trackDir,
0338 const double timeOffSet,
0339 const Container_t& uncalibCont) const {
0340 Container_t calibMeas{};
0341 std::ranges::transform(
0342 uncalibCont, std::back_inserter(calibMeas), [&](const auto& calibMe) {
0343 return calibrate(ctx, trackPos, trackDir, timeOffSet, *calibMe);
0344 });
0345 return uncalibCont;
0346 }
0347
0348
0349 void updateSigns(const Vector3& trackPos, const Vector3& trackDir,
0350 Container_t& measurements) const {
0351 auto signs =
0352 CompSpacePointAuxiliaries::strawSigns(trackPos, trackDir, measurements);
0353
0354 for (std::size_t s = 0; s < signs.size(); ++s) {
0355
0356 if (measurements[s]->isStraw()) {
0357 measurements[s]->updateDriftR(
0358 Acts::abs(measurements[s]->driftRadius()) * signs[s]);
0359 }
0360 }
0361 }
0362
0363
0364
0365 static double driftVelocity(const Acts::CalibrationContext& ,
0366 const FitTestSpacePoint& measurement) {
0367 if (!measurement.isStraw() || !measurement.isGood()) {
0368 return 0.;
0369 }
0370 return driftVelocity(driftTime(Acts::abs(measurement.driftRadius())));
0371 }
0372
0373
0374
0375 static double driftAcceleration(const Acts::CalibrationContext& ,
0376 const FitTestSpacePoint& measurement) {
0377 if (!measurement.isStraw() || !measurement.isGood()) {
0378 return 0.;
0379 }
0380 return driftAcceleration(driftTime(Acts::abs(measurement.driftRadius())));
0381 }
0382
0383 private:
0384 const Acts::Transform3 m_localToGlobal{Acts::Transform3::Identity()};
0385 };
0386
0387 static_assert(
0388 CompositeSpacePointCalibrator<SpCalibrator, Container_t, Container_t>);
0389 static_assert(
0390 CompositeSpacePointFastCalibrator<SpCalibrator, FitTestSpacePoint>);
0391
0392
0393 class SpSorter {
0394 public:
0395 SpSorter(const Container_t& hits, const SpCalibrator* calibrator)
0396 : m_calibrator{calibrator} {
0397 for (const auto& spPtr : hits) {
0398 auto& pushMe{spPtr->isStraw() ? m_straws : m_strips};
0399 if (spPtr->layer() >= pushMe.size()) {
0400 pushMe.resize(spPtr->layer() + 1);
0401 }
0402 pushMe[spPtr->layer()].push_back(spPtr);
0403 }
0404 }
0405 const std::vector<Container_t>& strawHits() const { return m_straws; }
0406 const std::vector<Container_t>& stripHits() const { return m_strips; }
0407
0408 bool goodCandidate(const FitTestSpacePoint& testPoint) const {
0409 return testPoint.isGood();
0410 }
0411
0412
0413
0414
0415
0416
0417
0418
0419
0420 double candidatePull(const CalibrationContext& , const Vector3& pos,
0421 const Vector3& dir, const double ,
0422 const FitTestSpacePoint& testSp) const {
0423 return CompSpacePointAuxiliaries::chi2Term(pos, dir, testSp);
0424 }
0425
0426 Container_t newContainer(const CalibrationContext& ) const {
0427 return Container_t{};
0428 }
0429
0430
0431
0432 void append(const CalibrationContext& cctx, const Vector3& pos,
0433 const Vector3& dir, const double t0,
0434 const FitTestSpacePoint& testSp,
0435 Container_t& outContainer) const {
0436 outContainer.push_back(m_calibrator->calibrate(cctx, pos, dir, t0, testSp));
0437 }
0438
0439 bool stopSeeding(const std::size_t lowerLayer,
0440 const std::size_t upperLayer) const {
0441 return lowerLayer >= upperLayer;
0442 }
0443
0444 private:
0445 const SpCalibrator* m_calibrator{nullptr};
0446 std::vector<Container_t> m_straws{};
0447 std::vector<Container_t> m_strips{};
0448 };
0449
0450 static_assert(CompositeSpacePointSorter<SpSorter, Container_t>);
0451
0452
0453
0454
0455 Line_t generateLine(RandomEngine& engine, const Logger& logger) {
0456 using ParIndex = Line_t::ParIndex;
0457 Line_t::ParamVector linePars{};
0458 linePars[toUnderlying(ParIndex::phi)] =
0459 uniform{-120_degree, 120_degree}(engine);
0460 linePars[toUnderlying(ParIndex::x0)] = uniform{-500., 500.}(engine);
0461 linePars[toUnderlying(ParIndex::y0)] = uniform{-500., 500.}(engine);
0462 linePars[toUnderlying(ParIndex::theta)] =
0463 uniform{5_degree, 175_degree}(engine);
0464 if (Acts::abs(linePars[toUnderlying(ParIndex::theta)] - 90._degree) <
0465 3_degree) {
0466 return generateLine(engine, logger);
0467 }
0468 Line_t line{linePars};
0469
0470 ACTS_DEBUG(
0471 "\n\n\n"
0472 << __func__ << "() " << __LINE__ << " - Generated parameters "
0473 << std::format("theta: {:.2f}, phi: {:.2f}, y0: {:.1f}, x0: {:.1f}",
0474 linePars[toUnderlying(ParIndex::theta)] / 1._degree,
0475 linePars[toUnderlying(ParIndex::phi)] / 1._degree,
0476 linePars[toUnderlying(ParIndex::y0)],
0477 linePars[toUnderlying(ParIndex::x0)])
0478 << " --> " << toString(line.position()) << " + "
0479 << toString(line.direction()));
0480
0481 return line;
0482 }
0483
0484 class MeasurementGenerator {
0485 public:
0486 struct Config {
0487
0488 bool createStraws{true};
0489
0490 bool smearRadius{true};
0491
0492 bool twinStraw{false};
0493
0494 double twinStrawReso{5._cm};
0495
0496 bool createStrips{true};
0497
0498 bool smearStrips{true};
0499
0500 bool discretizeStrips{false};
0501
0502 bool combineSpacePoints{false};
0503
0504 double stripPitchLoc0{4._cm};
0505
0506 double stripPitchLoc1{3._cm};
0507
0508 std::vector<Vector3> stripDirLoc1 =
0509 std::vector<Vector3>(8, Vector3::UnitX());
0510
0511 std::vector<Vector3> stripDirLoc0 =
0512 std::vector<Vector3>(8, Vector3::UnitY());
0513
0514 Acts::CalibrationContext calibContext{};
0515
0516 Acts::Transform3 localToGlobal{Acts::Transform3::Identity()};
0517 };
0518
0519
0520
0521
0522
0523
0524
0525
0526
0527
0528
0529 static Container_t spawn(const Line_t& line, const double t0,
0530 RandomEngine& engine, const Config& genCfg,
0531 const Logger& logger) {
0532
0533 const Vector3 posStaggering{0., std::cos(60._degree), std::sin(60._degree)};
0534
0535 const Vector3 negStaggering{0., -std::cos(60._degree),
0536 std::sin(60._degree)};
0537
0538 constexpr std::size_t nLayersPerMl = 8;
0539
0540 constexpr std::size_t nTubeLayers = nLayersPerMl * 2;
0541
0542 constexpr double chamberDistance = 3._m;
0543
0544 constexpr double tubeRadius = 15._mm;
0545
0546 constexpr double tubeLayerDist = 1.2_m;
0547
0548 constexpr double tubeStripDist = 30._cm;
0549
0550 constexpr double stripLayDist = 0.5_cm;
0551
0552 std::array<Vector3, nTubeLayers> tubePositions{
0553 filledArray<Vector3, nTubeLayers>(chamberDistance * Vector3::UnitZ())};
0554
0555 for (std::size_t l = 1; l < nTubeLayers; ++l) {
0556 const Vector3& layStag{l % 2 == 1 ? posStaggering : negStaggering};
0557 tubePositions[l] = tubePositions[l - 1] + 2. * tubeRadius * layStag;
0558
0559 if (l == nLayersPerMl) {
0560 tubePositions[l] += tubeLayerDist * Vector3::UnitZ();
0561 }
0562 }
0563
0564 ACTS_DEBUG("##############################################");
0565
0566 for (std::size_t l = 0; l < nTubeLayers; ++l) {
0567 ACTS_DEBUG(" *** " << (l + 1) << " - " << toString(tubePositions[l]));
0568 }
0569 ACTS_DEBUG("##############################################");
0570
0571 Container_t measurements{};
0572
0573
0574 if (genCfg.createStraws) {
0575 const SpCalibrator calibrator{genCfg.localToGlobal};
0576 for (const auto& [i_layer, stag] : enumerate(tubePositions)) {
0577 auto planeExtpLow =
0578 intersectPlane(line.position(), line.direction(), Vector3::UnitZ(),
0579 stag.z() - tubeRadius);
0580 auto planeExtpHigh =
0581 intersectPlane(line.position(), line.direction(), Vector3::UnitZ(),
0582 stag.z() + tubeRadius);
0583 ACTS_DEBUG("spawn() - Extrapolated to plane "
0584 << toString(planeExtpLow.position()) << " "
0585 << toString(planeExtpHigh.position()));
0586
0587 const auto dToFirstLow = static_cast<int>(std::ceil(
0588 (planeExtpLow.position().y() - stag.y()) / (2. * tubeRadius)));
0589 const auto dToFirstHigh = static_cast<int>(std::ceil(
0590 (planeExtpHigh.position().y() - stag.y()) / (2. * tubeRadius)));
0591
0592 const int dT = copySign(1, dToFirstHigh - dToFirstLow);
0593
0594
0595
0596 for (int tN = dToFirstLow - dT; tN != dToFirstHigh + 2 * dT; tN += dT) {
0597 Vector3 tube = stag + 2. * tN * tubeRadius * Vector3::UnitY();
0598 const double rad = Acts::abs(signedDistance(
0599 tube, Vector3::UnitX(), line.position(), line.direction()));
0600 if (rad > tubeRadius) {
0601 continue;
0602 }
0603
0604 const double smearedR =
0605 genCfg.smearRadius
0606 ? Acts::abs(
0607 normal_t{rad, SpCalibrator::driftUncert(rad)}(engine))
0608 : rad;
0609 if (smearedR > tubeRadius) {
0610 continue;
0611 }
0612
0613 if (genCfg.twinStraw) {
0614 const auto iSectWire = lineIntersect<3>(
0615 line.position(), line.direction(), tube, Vector3::UnitX());
0616 tube = iSectWire.position();
0617 tube[eX] = normal_t{tube[eX], genCfg.twinStrawReso}(engine);
0618 }
0619 ACTS_DEBUG("spawn() - Tube position: " << toString(tube)
0620 << ", radius: " << rad);
0621
0622 auto twinUncert =
0623 genCfg.twinStraw
0624 ? std::make_optional<double>(genCfg.twinStrawReso)
0625 : std::nullopt;
0626 auto& sp =
0627 measurements.emplace_back(std::make_unique<FitTestSpacePoint>(
0628 tube, smearedR, SpCalibrator::driftUncert(smearedR),
0629 twinUncert));
0630 sp->setLayer(i_layer);
0631 sp->updateTime(SpCalibrator::driftTime(sp->driftRadius()) + t0 +
0632 calibrator.closestApproachDist(line.position(),
0633 line.direction(), *sp) /
0634 PhysicalConstants::c);
0635 }
0636 }
0637 }
0638 if (genCfg.createStrips) {
0639
0640
0641
0642
0643
0644 auto discretize = [&genCfg, &engine](const Vector3& extPos,
0645 const std::size_t lay,
0646 const bool loc1) -> Vector3 {
0647 const double pitch =
0648 loc1 ? genCfg.stripPitchLoc1 : genCfg.stripPitchLoc0;
0649 assert(pitch > 0.);
0650
0651 const Vector3& stripDir =
0652 loc1 ? genCfg.stripDirLoc1.at(lay) : genCfg.stripDirLoc0.at(lay);
0653 const Vector3 stripNormal = stripDir.cross(Vector3::UnitZ());
0654 const double dist = stripNormal.dot(extPos);
0655 if (genCfg.smearStrips) {
0656 return normal_t{dist, pitch / std::sqrt(12)}(engine)*stripNormal;
0657 }
0658 if (genCfg.discretizeStrips) {
0659 return pitch *
0660 static_cast<int>(std::ceil((dist - 0.5 * pitch) / pitch)) *
0661 stripNormal;
0662 }
0663 return dist * stripNormal;
0664 };
0665
0666 const double stripCovLoc0 =
0667 Acts::square(genCfg.stripPitchLoc0) / std::sqrt(12.);
0668 const double stripCovLoc1 =
0669 Acts::square(genCfg.stripPitchLoc1) / std::sqrt(12.);
0670
0671 for (std::size_t sL = 0; sL < std::max(genCfg.stripDirLoc0.size(),
0672 genCfg.stripDirLoc1.size());
0673 ++sL) {
0674 const double distInZ = tubeStripDist + sL * stripLayDist;
0675 const double planeLow = tubePositions.front().z() - distInZ;
0676 const double planeHigh = tubePositions.back().z() + distInZ;
0677
0678 for (const double plane : {planeLow, planeHigh}) {
0679 const auto extp = intersectPlane(line.position(), line.direction(),
0680 Vector3::UnitZ(), plane);
0681 ACTS_VERBOSE("spawn() - Propagated line to "
0682 << toString(extp.position()) << ".");
0683
0684 if (genCfg.combineSpacePoints &&
0685 sL < std::min(genCfg.stripDirLoc0.size(),
0686 genCfg.stripDirLoc1.size())) {
0687 const Vector3 extpPos{discretize(extp.position(), sL, false) +
0688 discretize(extp.position(), sL, true) +
0689 plane * Vector3::UnitZ()};
0690 measurements.emplace_back(std::make_unique<FitTestSpacePoint>(
0691 extpPos, genCfg.stripDirLoc0.at(sL), genCfg.stripDirLoc1.at(sL),
0692 stripCovLoc0, stripCovLoc1));
0693 measurements.back()->setLayer(sL);
0694 } else {
0695 if (sL < genCfg.stripDirLoc0.size()) {
0696 const Vector3 extpPos{discretize(extp.position(), sL, false) +
0697 plane * Vector3::UnitZ()};
0698 measurements.emplace_back(std::make_unique<FitTestSpacePoint>(
0699 extpPos, genCfg.stripDirLoc0.at(sL),
0700 genCfg.stripDirLoc0.at(sL).cross(Vector3::UnitZ()),
0701 stripCovLoc0, 0.));
0702 auto& nM{*measurements.back()};
0703 nM.setLayer(sL);
0704 ACTS_VERBOSE("spawn() - Created loc0 strip @" << toString(nM)
0705 << ".");
0706 }
0707 if (sL < genCfg.stripDirLoc1.size()) {
0708 const Vector3 extpPos{discretize(extp.position(), sL, true) +
0709 plane * Vector3::UnitZ()};
0710 measurements.emplace_back(std::make_unique<FitTestSpacePoint>(
0711 extpPos, genCfg.stripDirLoc1.at(sL),
0712 genCfg.stripDirLoc1.at(sL).cross(Vector3::UnitZ()), 0.,
0713 stripCovLoc1));
0714 auto& nM{*measurements.back()};
0715 nM.setLayer(sL);
0716 ACTS_VERBOSE("spawn() - Created loc1 strip @" << toString(nM)
0717 << ".");
0718 }
0719 }
0720 }
0721 }
0722 }
0723 ACTS_DEBUG("Track hit in total " << measurements.size() << " sensors.");
0724 std::ranges::sort(measurements, [&line](const auto& spA, const auto& spB) {
0725 return line.direction().dot(spA->localPosition() - line.position()) <
0726 line.direction().dot(spB->localPosition() - line.position());
0727 });
0728 return measurements;
0729 }
0730 };
0731
0732
0733
0734
0735 ParamVec_t startParameters(const Line_t& line, const Container_t& hits) {
0736 ParamVec_t pars{};
0737
0738 double tanAlpha{0.};
0739 double tanBeta{0.};
0740
0741 auto firstPhi = std::ranges::find_if(
0742 hits, [](const auto& sp) { return sp->measuresLoc0(); });
0743 auto lastPhi =
0744 std::ranges::find_if(std::ranges::reverse_view(hits),
0745 [](const auto& sp) { return sp->measuresLoc0(); });
0746
0747 if (firstPhi != hits.end() && lastPhi != hits.rend()) {
0748 const Vector3 firstToLastPhi =
0749 (**lastPhi).localPosition() - (**firstPhi).localPosition();
0750 tanAlpha = firstToLastPhi.x() / firstToLastPhi.z();
0751
0752 pars[toUnderlying(FitParIndex::x0)] =
0753 (**lastPhi).localPosition().x() -
0754 (**lastPhi).localPosition().z() * tanAlpha;
0755 }
0756
0757 auto firstTube =
0758 std::ranges::find_if(hits, [](const auto& sp) { return sp->isStraw(); });
0759 auto lastTube =
0760 std::ranges::find_if(std::ranges::reverse_view(hits),
0761 [](const auto& sp) { return sp->isStraw(); });
0762
0763 if (firstTube != hits.end() && lastTube != hits.rend()) {
0764 const int signFirst =
0765 CompSpacePointAuxiliaries::strawSign(line, **firstTube);
0766 const int signLast = CompSpacePointAuxiliaries::strawSign(line, **lastTube);
0767
0768 auto seedPars = CompositeSpacePointLineSeeder::constructTangentLine(
0769 **lastTube, **firstTube,
0770 CompositeSpacePointLineSeeder::encodeAmbiguity(signLast, signFirst));
0771 tanBeta = std::tan(seedPars.theta);
0772 pars[toUnderlying(FitParIndex::y0)] = seedPars.y0;
0773 } else {
0774 auto firstEta = std::ranges::find_if(hits, [](const auto& sp) {
0775 return !sp->isStraw() && sp->measuresLoc1();
0776 });
0777 auto lastEta = std::ranges::find_if(
0778 std::ranges::reverse_view(hits),
0779 [](const auto& sp) { return !sp->isStraw() && sp->measuresLoc1(); });
0780
0781 if (firstEta != hits.end() && lastEta != hits.rend()) {
0782 const Vector3 firstToLastEta =
0783 (**lastEta).localPosition() - (**firstEta).localPosition();
0784 tanBeta = firstToLastEta.y() / firstToLastEta.z();
0785
0786 pars[toUnderlying(FitParIndex::y0)] =
0787 (**lastEta).localPosition().y() -
0788 (**lastEta).localPosition().z() * tanBeta;
0789 }
0790 }
0791 const Vector3 seedDir = makeDirectionFromAxisTangents(tanAlpha, tanBeta);
0792
0793 pars[toUnderlying(FitParIndex::theta)] = theta(seedDir);
0794 pars[toUnderlying(FitParIndex::phi)] = phi(seedDir);
0795 return pars;
0796 }
0797
0798 bool isGoodHit(const FitTestSpacePoint& sp) {
0799 return !sp.isStraw() || sp.isGood();
0800 };
0801
0802 }