File indexing completed on 2025-10-13 08:19:11
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <boost/test/unit_test.hpp>
0010
0011 #include "Acts/Definitions/Units.hpp"
0012 #include "Acts/Seeding/CompositeSpacePointLineFitter.hpp"
0013 #include "Acts/Seeding/CompositeSpacePointLineSeeder.hpp"
0014 #include "Acts/Utilities/StringHelpers.hpp"
0015 #include "Acts/Utilities/UnitVectors.hpp"
0016 #include "Acts/Utilities/VectorHelpers.hpp"
0017
0018 #include <chrono>
0019 #include <format>
0020 #include <random>
0021 #include <ranges>
0022 #include <set>
0023 #include <span>
0024
0025 #include <TFile.h>
0026 #include <TTree.h>
0027
0028 using namespace Acts;
0029 using namespace Acts::Experimental;
0030 using namespace Acts::Experimental::detail;
0031 using namespace Acts::UnitLiterals;
0032 using namespace Acts::detail::LineHelper;
0033 using namespace Acts::PlanarHelper;
0034 using namespace Acts::VectorHelpers;
0035
0036 using TimePoint_t = std::chrono::system_clock::time_point;
0037 using RandomEngine = std::mt19937;
0038 using uniform = std::uniform_real_distribution<double>;
0039 using normal_t = std::normal_distribution<double>;
0040 using Line_t = CompSpacePointAuxiliaries::Line_t;
0041 using ResidualIdx = CompSpacePointAuxiliaries::ResidualIdx;
0042 using FitParIndex = CompSpacePointAuxiliaries::FitParIndex;
0043 using ParamVec_t = CompositeSpacePointLineFitter::ParamVec_t;
0044 using Fitter_t = CompositeSpacePointLineFitter;
0045
0046 constexpr auto logLvl = Acts::Logging::Level::INFO;
0047 constexpr std::size_t nEvents = 1;
0048
0049 ACTS_LOCAL_LOGGER(getDefaultLogger("StrawLineFitterTest", logLvl));
0050
0051 namespace ActsTests {
0052
0053 class FitTestSpacePoint {
0054 public:
0055
0056
0057
0058
0059
0060 FitTestSpacePoint(const Vector3& pos, const double driftR,
0061 const double driftRUncert,
0062 const std::optional<double> twnUncert = std::nullopt)
0063 : m_position{pos},
0064 m_driftR{driftR},
0065 m_measLoc0{twnUncert != std::nullopt} {
0066 using enum ResidualIdx;
0067 m_covariance[toUnderlying(bending)] = Acts::square(driftRUncert);
0068 m_covariance[toUnderlying(nonBending)] =
0069 Acts::square(twnUncert.value_or(0.));
0070 }
0071
0072
0073 FitTestSpacePoint(const Vector3& stripPos, const Vector3& stripDir,
0074 const Vector3& toNext, const double uncertLoc0,
0075 const double uncertLoc1)
0076 : m_position{stripPos},
0077 m_sensorDir{stripDir},
0078 m_toNextSen{toNext},
0079 m_measLoc0{uncertLoc0 > 0.},
0080 m_measLoc1{uncertLoc1 > 0.} {
0081 using enum ResidualIdx;
0082 m_covariance[toUnderlying(nonBending)] = Acts::square(uncertLoc0);
0083 m_covariance[toUnderlying(bending)] = Acts::square(uncertLoc1);
0084 }
0085
0086 const Vector3& localPosition() const { return m_position; }
0087
0088 const Vector3& sensorDirection() const { return m_sensorDir; }
0089
0090 const Vector3& toNextSensor() const { return m_toNextSen; }
0091
0092 const Vector3& planeNormal() const { return m_planeNorm; }
0093
0094 double driftRadius() const { return m_driftR.value_or(0.); }
0095
0096 const std::array<double, 3>& covariance() const { return m_covariance; }
0097
0098 double time() const { return m_time.value_or(0.); }
0099
0100 bool isStraw() const { return m_driftR.has_value(); }
0101
0102 bool hasTime() const { return m_time.has_value(); }
0103
0104 bool measuresLoc0() const { return m_measLoc0; }
0105
0106 bool measuresLoc1() const { return m_measLoc1 || isStraw(); }
0107
0108 void updateDriftR(const double updatedR) { m_driftR = updatedR; }
0109
0110 void updatePosition(const Vector3& newPos) { m_position = newPos; }
0111
0112 private:
0113 Vector3 m_position{Vector3::Zero()};
0114 Vector3 m_sensorDir{Vector3::UnitX()};
0115 Vector3 m_toNextSen{Vector3::UnitY()};
0116 Vector3 m_planeNorm{m_sensorDir.cross(m_toNextSen).normalized()};
0117 std::optional<double> m_driftR{std::nullopt};
0118 std::optional<double> m_time{std::nullopt};
0119 std::array<double, 3> m_covariance{filledArray<double, 3>(0)};
0120 bool m_measLoc0{false};
0121 bool m_measLoc1{false};
0122 };
0123
0124 static_assert(CompositeSpacePoint<FitTestSpacePoint>);
0125
0126 using Container_t = std::vector<std::shared_ptr<FitTestSpacePoint>>;
0127
0128 static_assert(CompositeSpacePointContainer<Container_t>);
0129 class SpCalibrator {
0130 public:
0131 static double driftUncert(const double r) {
0132 return 0.2_mm / (1._mm + Acts::square(r)) + 0.1_mm;
0133 }
0134
0135
0136
0137
0138
0139
0140 Container_t calibrate(const Acts::CalibrationContext& ,
0141 const Vector3& trackPos, const Vector3& trackDir,
0142 const double ,
0143 const Container_t& uncalibCont) const {
0144 Container_t calibMeas{};
0145 for (const auto& sp : uncalibCont) {
0146 if (!sp->measuresLoc0() || !sp->measuresLoc1()) {
0147
0148 auto bestPos = lineIntersect(trackPos, trackDir, sp->localPosition(),
0149 sp->sensorDirection());
0150 sp->updatePosition(bestPos.position());
0151 }
0152 if (sp->isStraw()) {
0153 sp->updateDriftR(Acts::abs(sp->driftRadius()));
0154 }
0155 }
0156 return uncalibCont;
0157 }
0158
0159
0160 void updateSigns(const Vector3& trackPos, const Vector3& trackDir,
0161 Container_t& measurements) const {
0162 auto signs =
0163 CompSpacePointAuxiliaries::strawSigns(trackPos, trackDir, measurements);
0164
0165 for (std::size_t s = 0; s < signs.size(); ++s) {
0166
0167 if (measurements[s]->isStraw()) {
0168 measurements[s]->updateDriftR(
0169 Acts::abs(measurements[s]->driftRadius()) * signs[s]);
0170 }
0171 }
0172 }
0173 };
0174
0175 static_assert(
0176 CompositeSpacePointCalibrator<SpCalibrator, Container_t, Container_t>);
0177
0178
0179
0180
0181 Line_t generateLine(RandomEngine& engine) {
0182 using ParIndex = Line_t::ParIndex;
0183 Line_t::ParamVector linePars{};
0184 linePars[toUnderlying(ParIndex::phi)] =
0185 std::uniform_real_distribution{-120_degree, 120_degree}(engine);
0186 linePars[toUnderlying(ParIndex::x0)] =
0187 std::uniform_real_distribution{-5000., 5000.}(engine);
0188 linePars[toUnderlying(ParIndex::y0)] =
0189 std::uniform_real_distribution{-5000., 5000.}(engine);
0190 linePars[toUnderlying(ParIndex::theta)] =
0191 std::uniform_real_distribution{5_degree, 175_degree}(engine);
0192 if (Acts::abs(linePars[toUnderlying(ParIndex::theta)] - 90._degree) <
0193 0.2_degree) {
0194 return generateLine(engine);
0195 }
0196 Line_t line{linePars};
0197
0198 ACTS_DEBUG(
0199 "\n\n\n"
0200 << __func__ << "() " << __LINE__ << " - Generated parameters "
0201 << std::format("theta: {:.2f}, phi: {:.2f}, y0: {:.1f}, x0: {:.1f}",
0202 linePars[toUnderlying(ParIndex::theta)] / 1._degree,
0203 linePars[toUnderlying(ParIndex::phi)] / 1._degree,
0204 linePars[toUnderlying(ParIndex::y0)],
0205 linePars[toUnderlying(ParIndex::x0)])
0206 << " --> " << toString(line.position()) << " + "
0207 << toString(line.direction()));
0208
0209 return line;
0210 }
0211
0212 class MeasurementGenerator {
0213 public:
0214 struct Config {
0215
0216 bool createStraws{true};
0217
0218 bool smearRadius{true};
0219
0220 bool twinStraw{false};
0221
0222 double twinStrawReso{5._cm};
0223
0224 bool createStrips{true};
0225
0226 bool smearStrips{true};
0227
0228 bool discretizeStrips{false};
0229
0230 bool combineSpacePoints{false};
0231
0232 bool createStripsLoc0{true};
0233
0234 bool createStripsLoc1{true};
0235
0236 double stripPitchLoc0{4._cm};
0237
0238 double stripPitchLoc1{3._cm};
0239
0240 Vector3 stripDirLoc1{Vector3::UnitX()};
0241
0242 Vector3 stripDirLoc0{Vector3::UnitY()};
0243 };
0244
0245
0246
0247
0248
0249
0250
0251
0252
0253
0254
0255 static Container_t spawn(const Line_t& line, const double ,
0256 RandomEngine& engine, const Config& genCfg) {
0257
0258 const Vector3 posStaggering{0., std::cos(60._degree), std::sin(60._degree)};
0259
0260 const Vector3 negStaggering{0., -std::cos(60._degree),
0261 std::sin(60._degree)};
0262
0263 constexpr std::size_t nLayersPerMl = 8;
0264
0265 constexpr std::size_t nTubeLayers = nLayersPerMl * 2;
0266
0267 constexpr double chamberDistance = 3._m;
0268
0269 constexpr double tubeRadius = 15._mm;
0270
0271 constexpr double tubeLayerDist = 1.2_m;
0272
0273 constexpr double tubeStripDist = 30._cm;
0274
0275 constexpr double stripLayDist = 0.5_cm;
0276
0277 constexpr std::size_t nStripLay = 8;
0278
0279 std::array<Vector3, nTubeLayers> tubePositions{
0280 filledArray<Vector3, nTubeLayers>(chamberDistance * Vector3::UnitZ())};
0281
0282 for (std::size_t l = 1; l < nTubeLayers; ++l) {
0283 const Vector3& layStag{l % 2 == 1 ? posStaggering : negStaggering};
0284 tubePositions[l] = tubePositions[l - 1] + 2. * tubeRadius * layStag;
0285
0286 if (l == nLayersPerMl) {
0287 tubePositions[l] += tubeLayerDist * Vector3::UnitZ();
0288 }
0289 }
0290
0291 ACTS_DEBUG("##############################################");
0292
0293 for (std::size_t l = 0; l < nTubeLayers; ++l) {
0294 ACTS_DEBUG(" *** " << (l + 1) << " - " << toString(tubePositions[l]));
0295 }
0296 ACTS_DEBUG("##############################################");
0297
0298 Container_t measurements{};
0299
0300
0301 if (genCfg.createStraws) {
0302 for (const auto& stag : tubePositions) {
0303 auto planeExtpLow =
0304 intersectPlane(line.position(), line.direction(), Vector3::UnitZ(),
0305 stag.z() - tubeRadius);
0306 auto planeExtpHigh =
0307 intersectPlane(line.position(), line.direction(), Vector3::UnitZ(),
0308 stag.z() + tubeRadius);
0309 ACTS_DEBUG("spawn() - Extrapolated to plane "
0310 << toString(planeExtpLow.position()) << " "
0311 << toString(planeExtpHigh.position()));
0312
0313 const auto dToFirstLow = static_cast<int>(std::ceil(
0314 (planeExtpLow.position().y() - stag.y()) / (2. * tubeRadius)));
0315 const auto dToFirstHigh = static_cast<int>(std::ceil(
0316 (planeExtpHigh.position().y() - stag.y()) / (2. * tubeRadius)));
0317
0318 const int dT = dToFirstHigh > dToFirstLow ? 1 : -1;
0319
0320
0321
0322 for (int tN = dToFirstLow - dT; tN != dToFirstHigh + 2 * dT; tN += dT) {
0323 Vector3 tube = stag + 2. * tN * tubeRadius * Vector3::UnitY();
0324 const double rad = Acts::abs(signedDistance(
0325 tube, Vector3::UnitX(), line.position(), line.direction()));
0326 if (rad > tubeRadius) {
0327 continue;
0328 }
0329
0330 const double smearedR =
0331 genCfg.smearRadius
0332 ? Acts::abs(
0333 normal_t{rad, SpCalibrator::driftUncert(rad)}(engine))
0334 : rad;
0335 if (smearedR > tubeRadius) {
0336 continue;
0337 }
0338
0339 if (genCfg.twinStraw) {
0340 const auto iSectWire = lineIntersect<3>(
0341 line.position(), line.direction(), tube, Vector3::UnitX());
0342 tube = iSectWire.position();
0343 tube[eX] = normal_t{tube[eX], genCfg.twinStrawReso}(engine);
0344 }
0345 ACTS_DEBUG("spawn() - Tube position: " << toString(tube)
0346 << ", radius: " << rad);
0347
0348 measurements.emplace_back(std::make_unique<FitTestSpacePoint>(
0349 tube, smearedR, SpCalibrator::driftUncert(smearedR),
0350 genCfg.twinStraw
0351 ? std::make_optional<double>(genCfg.twinStrawReso)
0352 : std::nullopt));
0353 }
0354 }
0355 }
0356 if (genCfg.createStrips) {
0357
0358
0359
0360
0361
0362 auto discretize = [&genCfg, &engine](const Vector3& extPos,
0363 const bool loc1) -> Vector3 {
0364 const double pitch =
0365 loc1 ? genCfg.stripPitchLoc1 : genCfg.stripPitchLoc0;
0366 assert(pitch > 0.);
0367
0368 const Vector3& stripDir =
0369 loc1 ? genCfg.stripDirLoc1 : genCfg.stripDirLoc0;
0370 const Vector3 stripNormal = stripDir.cross(Vector3::UnitZ());
0371 const double dist = stripNormal.dot(extPos);
0372 if (genCfg.smearStrips) {
0373 return normal_t{dist, pitch / std::sqrt(12)}(engine)*stripNormal;
0374 }
0375 if (genCfg.discretizeStrips) {
0376 return pitch *
0377 static_cast<int>(std::ceil((dist - 0.5 * pitch) / pitch)) *
0378 stripNormal;
0379 }
0380 return dist * stripNormal;
0381 };
0382
0383 const double stripCovLoc0 =
0384 Acts::square(genCfg.stripPitchLoc0) / std::sqrt(12.);
0385 const double stripCovLoc1 =
0386 Acts::square(genCfg.stripPitchLoc1) / std::sqrt(12.);
0387
0388 for (std::size_t sL = 0; sL < nStripLay; ++sL) {
0389 const double distInZ = tubeStripDist + sL * stripLayDist;
0390 const double planeLow = tubePositions.front().z() - distInZ;
0391 const double planeHigh = tubePositions.back().z() + distInZ;
0392
0393 for (const double plane : {planeLow, planeHigh}) {
0394 const auto extp = intersectPlane(line.position(), line.direction(),
0395 Vector3::UnitZ(), plane);
0396 ACTS_VERBOSE("spawn() - Propagated line to "
0397 << toString(extp.position()) << ".");
0398 if (genCfg.combineSpacePoints) {
0399 const Vector3 extpPos{discretize(extp.position(), false) +
0400 discretize(extp.position(), true) +
0401 plane * Vector3::UnitZ()};
0402 measurements.emplace_back(std::make_unique<FitTestSpacePoint>(
0403 extpPos, genCfg.stripDirLoc0, genCfg.stripDirLoc1, stripCovLoc0,
0404 stripCovLoc1));
0405 } else {
0406 if (genCfg.createStripsLoc0) {
0407 const Vector3 extpPos{discretize(extp.position(), false) +
0408 plane * Vector3::UnitZ()};
0409 measurements.emplace_back(std::make_unique<FitTestSpacePoint>(
0410 extpPos, genCfg.stripDirLoc0,
0411 genCfg.stripDirLoc0.cross(Vector3::UnitZ()), stripCovLoc0,
0412 0.));
0413 const auto& nM{*measurements.back()};
0414 ACTS_VERBOSE("spawn() - Created loc0 strip @"
0415 << toString(nM.localPosition())
0416 << ", dir: " << toString(nM.sensorDirection())
0417 << ", to-next:" << toString(nM.toNextSensor())
0418 << " -> covariance: " << nM.covariance()[0] << ".");
0419 }
0420 if (genCfg.createStripsLoc1) {
0421 const Vector3 extpPos{discretize(extp.position(), true) +
0422 plane * Vector3::UnitZ()};
0423 measurements.emplace_back(std::make_unique<FitTestSpacePoint>(
0424 extpPos, genCfg.stripDirLoc1,
0425 genCfg.stripDirLoc1.cross(Vector3::UnitZ()), 0.,
0426 stripCovLoc1));
0427 const auto& nM{*measurements.back()};
0428 ACTS_VERBOSE("spawn() - Created loc1 strip @"
0429 << toString(nM.localPosition())
0430 << ", dir: " << toString(nM.sensorDirection())
0431 << ", to-next:" << toString(nM.toNextSensor())
0432 << " -> covariance: " << nM.covariance()[1] << ".");
0433 }
0434 }
0435 }
0436 }
0437 }
0438 ACTS_DEBUG("Track hit in total " << measurements.size() << " sensors.");
0439 std::ranges::sort(measurements, [&line](const auto& spA, const auto& spB) {
0440 return line.direction().dot(spA->localPosition() - line.position()) <
0441 line.direction().dot(spB->localPosition() - line.position());
0442 });
0443 return measurements;
0444 }
0445 };
0446
0447 using GenCfg_t = MeasurementGenerator::Config;
0448
0449
0450
0451
0452 ParamVec_t startParameters(const Line_t& line, const Container_t& hits) {
0453 ParamVec_t pars{};
0454
0455 double tanPhi{0.};
0456 double tanTheta{0.};
0457
0458 auto firstPhi = std::ranges::find_if(
0459 hits, [](const auto& sp) { return sp->measuresLoc0(); });
0460 auto lastPhi =
0461 std::ranges::find_if(std::ranges::reverse_view(hits),
0462 [](const auto& sp) { return sp->measuresLoc0(); });
0463
0464 if (firstPhi != hits.end() && lastPhi != hits.rend()) {
0465 const Vector3 firstToLastPhi =
0466 (**lastPhi).localPosition() - (**firstPhi).localPosition();
0467 tanPhi = firstToLastPhi.x() / firstToLastPhi.z();
0468
0469 pars[toUnderlying(FitParIndex::x0)] =
0470 (**lastPhi).localPosition().x() -
0471 (**lastPhi).localPosition().z() * tanPhi;
0472 }
0473
0474 auto firstTube =
0475 std::ranges::find_if(hits, [](const auto& sp) { return sp->isStraw(); });
0476 auto lastTube =
0477 std::ranges::find_if(std::ranges::reverse_view(hits),
0478 [](const auto& sp) { return sp->isStraw(); });
0479
0480 if (firstTube != hits.end() && lastTube != hits.rend()) {
0481 const int signFirst =
0482 CompSpacePointAuxiliaries::strawSign(line, **firstTube);
0483 const int signLast = CompSpacePointAuxiliaries::strawSign(line, **lastTube);
0484
0485 auto seedPars = CompositeSpacePointLineSeeder::constructTangentLine(
0486 **lastTube, **firstTube,
0487 CompositeSpacePointLineSeeder::encodeAmbiguity(signLast, signFirst));
0488 tanTheta = std::tan(seedPars.theta);
0489 pars[toUnderlying(FitParIndex::y0)] = seedPars.y0;
0490 } else {
0491 auto firstEta = std::ranges::find_if(hits, [](const auto& sp) {
0492 return !sp->isStraw() && sp->measuresLoc1();
0493 });
0494 auto lastEta = std::ranges::find_if(
0495 std::ranges::reverse_view(hits),
0496 [](const auto& sp) { return !sp->isStraw() && sp->measuresLoc1(); });
0497
0498 if (firstEta != hits.end() && lastEta != hits.rend()) {
0499 const Vector3 firstToLastEta =
0500 (**lastEta).localPosition() - (**firstEta).localPosition();
0501 tanTheta = firstToLastEta.y() / firstToLastEta.z();
0502
0503 pars[toUnderlying(FitParIndex::y0)] =
0504 (**lastEta).localPosition().y() -
0505 (**lastEta).localPosition().z() * tanTheta;
0506 }
0507 }
0508
0509 const Vector3 seedDir = makeDirectionFromAxisTangents(tanPhi, tanTheta);
0510 pars[toUnderlying(FitParIndex::theta)] = theta(seedDir);
0511 pars[toUnderlying(FitParIndex::phi)] = phi(seedDir);
0512 return pars;
0513 }
0514
0515 BOOST_AUTO_TEST_SUITE(SeedingSuite)
0516
0517 BOOST_AUTO_TEST_CASE(SeedTangents) {
0518 RandomEngine engine{1602};
0519 constexpr double tolerance = 1.e-3;
0520
0521 using Seeder = CompositeSpacePointLineSeeder;
0522 using SeedAux = CompSpacePointAuxiliaries;
0523 using enum Seeder::TangentAmbi;
0524 GenCfg_t genCfg{};
0525 genCfg.createStrips = false;
0526 genCfg.createStraws = true;
0527 genCfg.smearRadius = false;
0528
0529 for (std::size_t evt = 0; evt < nEvents; ++evt) {
0530 const auto line = generateLine(engine);
0531 auto testTubes = MeasurementGenerator::spawn(line, 0._ns, engine, genCfg);
0532 const double lineTanTheta = line.direction().y() / line.direction().z();
0533 const double lineY0 = line.position().y();
0534 for (std::size_t m1 = testTubes.size() - 1; m1 > testTubes.size() / 2;
0535 --m1) {
0536 for (std::size_t m2 = 0; m2 < m1; ++m2) {
0537 const auto& bottomTube = *testTubes[m2];
0538 const auto& topTube = *testTubes[m1];
0539 const int signTop = SeedAux::strawSign(line, topTube);
0540 const int signBot = SeedAux::strawSign(line, bottomTube);
0541 const auto trueAmbi = Seeder::encodeAmbiguity(signTop, signBot);
0542
0543 ACTS_DEBUG(__func__ << "() " << __LINE__ << " - "
0544 << std::format("bottom tube @ {:}, r: {:.3f}({:})",
0545 toString(bottomTube.localPosition()),
0546 (signBot * bottomTube.driftRadius()),
0547 signBot > 0 ? "R" : "L")
0548 << ", "
0549 << std::format("top tube @ {:}, r: {:.3f} ({:})",
0550 toString(topTube.localPosition()),
0551 (signTop * topTube.driftRadius()),
0552 signTop > 0 ? "R" : "L"));
0553
0554 bool seenTruePars{false};
0555 std::set<std::pair<double, double>> fourSeedPars{};
0556 for (const auto ambi : {LL, RL, LR, RR}) {
0557 const auto pars =
0558 Seeder::constructTangentLine(topTube, bottomTube, ambi);
0559
0560 const bool isTruePars =
0561 Acts::abs(std::tan(pars.theta) - lineTanTheta) < tolerance &&
0562 Acts::abs(lineY0 - pars.y0) < tolerance;
0563 seenTruePars |= isTruePars;
0564 ACTS_VERBOSE(__func__
0565 << "() " << __LINE__ << " - Test ambiguity "
0566 << CompositeSpacePointLineSeeder::toString(ambi)
0567 << " -> "
0568 << std::format("theta: {:.3f}, ", pars.theta / 1._degree)
0569 << std::format("tanTheta: {:.3f}, ",
0570 std::tan(pars.theta))
0571 << std::format("y0: {:.3f}", pars.y0)
0572 << (!isTruePars ? (ambi == trueAmbi ? " xxxxxx" : "")
0573 : " <-------"));
0574 const Vector3 seedPos = pars.y0 * Vector3::UnitY();
0575 const Vector3 seedDir =
0576 makeDirectionFromAxisTangents(0., std::tan(pars.theta));
0577
0578 const double chi2Top = SeedAux::chi2Term(seedPos, seedDir, topTube);
0579 const double chi2Bot =
0580 SeedAux::chi2Term(seedPos, seedDir, bottomTube);
0581 ACTS_VERBOSE(__func__ << "() " << __LINE__
0582 << " - Resulting chi2 top: " << chi2Top
0583 << ", bottom: " << chi2Bot);
0584 BOOST_CHECK_LE(chi2Top, 1.e-17);
0585 BOOST_CHECK_LE(chi2Bot, 1.e-17);
0586 BOOST_CHECK_EQUAL(
0587 fourSeedPars.insert(std::make_pair(pars.theta, pars.y0)).second,
0588 true);
0589 }
0590 BOOST_CHECK_EQUAL(seenTruePars, true);
0591 BOOST_CHECK_EQUAL(fourSeedPars.size(), 4);
0592 const auto seedPars =
0593 Seeder::constructTangentLine(topTube, bottomTube, trueAmbi);
0594
0595 ACTS_DEBUG(__func__
0596 << "() " << __LINE__ << " - Line tan theta: " << lineTanTheta
0597 << ", reconstructed theta: " << std::tan(seedPars.theta)
0598 << ", line y0: " << lineY0
0599 << ", reconstructed y0: " << seedPars.y0);
0600 BOOST_CHECK_CLOSE(std::tan(seedPars.theta), lineTanTheta, tolerance);
0601 BOOST_CHECK_CLOSE(seedPars.y0, lineY0, tolerance);
0602 }
0603 }
0604 }
0605 }
0606
0607 #define DECLARE_BRANCH(dType, bName) \
0608 dType bName{}; \
0609 outTree->Branch(#bName, &bName);
0610
0611 void runFitTest(const Fitter_t::Config& fitCfg, const GenCfg_t& genCfg,
0612 const std::string& testName, RandomEngine& engine,
0613 TFile& outFile) {
0614 Fitter_t fitter{fitCfg, getDefaultLogger(
0615 std::format("LineFitter_{:}", testName), logLvl)};
0616
0617 auto outTree = std::make_unique<TTree>(
0618 std::format("{:}Tree", testName).c_str(), "MonitorTree");
0619
0620 TimePoint_t start = std::chrono::system_clock::now();
0621
0622 ACTS_INFO("Start test " << testName << ".");
0623
0624 DECLARE_BRANCH(double, trueY0);
0625 DECLARE_BRANCH(double, trueX0);
0626 DECLARE_BRANCH(double, trueTheta);
0627 DECLARE_BRANCH(double, truePhi);
0628 DECLARE_BRANCH(double, trueT0);
0629 DECLARE_BRANCH(double, trueProjTheta);
0630 DECLARE_BRANCH(double, trueProjPhi);
0631
0632 DECLARE_BRANCH(double, recoY0);
0633 DECLARE_BRANCH(double, recoX0);
0634 DECLARE_BRANCH(double, recoTheta);
0635 DECLARE_BRANCH(double, recoPhi);
0636 DECLARE_BRANCH(double, recoT0);
0637 DECLARE_BRANCH(double, recoProjTheta);
0638 DECLARE_BRANCH(double, recoProjPhi);
0639
0640 DECLARE_BRANCH(double, sigmaY0);
0641 DECLARE_BRANCH(double, sigmaX0);
0642 DECLARE_BRANCH(double, sigmaTheta);
0643 DECLARE_BRANCH(double, sigmaPhi);
0644 DECLARE_BRANCH(double, sigmaT0);
0645
0646 DECLARE_BRANCH(double, chi2);
0647 DECLARE_BRANCH(unsigned, nIter);
0648 DECLARE_BRANCH(unsigned, nDoF);
0649
0650
0651
0652
0653
0654
0655
0656 auto fillPars = [](const auto pars, double& y0, double& x0, double& theta,
0657 double& phi) {
0658 y0 = pars[toUnderlying(FitParIndex::y0)];
0659 x0 = pars[toUnderlying(FitParIndex::x0)];
0660 theta = pars[toUnderlying(FitParIndex::theta)] / 1._degree;
0661 phi = pars[toUnderlying(FitParIndex::phi)] / 1._degree;
0662 };
0663
0664 auto fillProjected = [](const auto pars, double& projTheta, double& projPhi) {
0665 auto dir =
0666 makeDirectionFromPhiTheta(pars[toUnderlying(FitParIndex::phi)],
0667 pars[toUnderlying(FitParIndex::theta)]);
0668 projTheta = std::atan(dir[ePos1] / dir[ePos2]) / 1._degree;
0669 projPhi = std::atan(dir[ePos0] / dir[ePos2]) / 1._degree;
0670 };
0671 auto calibrator = std::make_unique<SpCalibrator>();
0672 for (std::size_t evt = 0; evt < nEvents; ++evt) {
0673 const auto line = generateLine(engine);
0674 fillPars(line.parameters(), trueY0, trueX0, trueTheta, truePhi);
0675 fillProjected(line.parameters(), trueProjTheta, trueProjPhi);
0676 const double t0 = uniform{-50._ns, 50._ns}(engine);
0677 trueT0 = t0 / 1._ns;
0678
0679 using FitOpts_t = Fitter_t::FitOptions<Container_t, SpCalibrator>;
0680
0681 FitOpts_t fitOpts{};
0682 fitOpts.calibrator = calibrator.get();
0683 fitOpts.measurements =
0684 MeasurementGenerator::spawn(line, t0, engine, genCfg);
0685 fitOpts.startParameters = startParameters(line, fitOpts.measurements);
0686 fillPars(fitOpts.startParameters, recoY0, recoX0, recoTheta, recoPhi);
0687
0688
0689
0690 auto result = fitter.fit(std::move(fitOpts));
0691 if (!result.converged) {
0692 continue;
0693 }
0694
0695 fillPars(result.parameters, recoY0, recoX0, recoTheta, recoPhi);
0696 fillProjected(result.parameters, recoProjTheta, recoProjPhi);
0697
0698 recoT0 = result.parameters[toUnderlying(FitParIndex::t0)] / 1._ns;
0699
0700 auto extractUncert = [&result](const auto idx) {
0701 return std::sqrt(result.covariance(toUnderlying(idx), toUnderlying(idx)));
0702 };
0703 sigmaY0 = extractUncert(FitParIndex::y0);
0704 sigmaX0 = extractUncert(FitParIndex::x0);
0705 sigmaTheta = extractUncert(FitParIndex::theta) / 1._degree;
0706 sigmaPhi = extractUncert(FitParIndex::phi) / 1._degree;
0707 sigmaT0 = extractUncert(FitParIndex::t0) / 1._ns;
0708
0709 chi2 = result.chi2;
0710 nDoF = result.nDoF;
0711 nIter = result.nIter;
0712
0713 outTree->Fill();
0714 if ((evt + 1) % 10 == 0u) {
0715 ACTS_INFO("Processed " << (evt + 1) << "/" << nEvents << " events.");
0716 }
0717 }
0718
0719 TimePoint_t end = std::chrono::system_clock::now();
0720 auto diff =
0721 std::chrono::duration_cast<std::chrono::seconds>(end - start).count();
0722
0723 outFile.WriteObject(outTree.get(), outTree->GetName());
0724 ACTS_INFO("Test finished. " << outTree->GetEntries()
0725 << " tracks written. Test took " << diff
0726 << " seconds.");
0727 }
0728 #undef DECLARE_BRANCH
0729
0730 BOOST_AUTO_TEST_CASE(SimpleLineFit) {
0731 auto outFile =
0732 std::make_unique<TFile>("StrawLineFitterTest.root", "RECREATE");
0733
0734 Fitter_t::Config fitCfg{};
0735 fitCfg.useHessian = true;
0736 fitCfg.calcAlongStraw = true;
0737
0738 GenCfg_t genCfg{};
0739 genCfg.twinStraw = false;
0740 genCfg.createStrips = false;
0741
0742 {
0743 RandomEngine engine{1602};
0744 runFitTest(fitCfg, genCfg, "StrawOnlyTest", engine, *outFile);
0745 }
0746
0747 {
0748 fitCfg.useFastFitter = true;
0749 RandomEngine engine{1503};
0750 runFitTest(fitCfg, genCfg, "FastStrawOnlyTest", engine, *outFile);
0751 }
0752
0753
0754 {
0755 fitCfg.useFastFitter = true;
0756 genCfg.twinStraw = true;
0757 RandomEngine engine{1701};
0758 runFitTest(fitCfg, genCfg, "StrawAndTwinTest", engine, *outFile);
0759 }
0760 genCfg.createStrips = true;
0761 genCfg.twinStraw = false;
0762 genCfg.combineSpacePoints = false;
0763
0764 {
0765 RandomEngine engine{1404};
0766 runFitTest(fitCfg, genCfg, "StrawAndStripTest", engine, *outFile);
0767 }
0768
0769 {
0770 genCfg.createStraws = false;
0771 genCfg.combineSpacePoints = true;
0772 genCfg.stripPitchLoc1 = 500._um;
0773 RandomEngine engine{2070};
0774 runFitTest(fitCfg, genCfg, "StripOnlyTest", engine, *outFile);
0775 }
0776
0777 {
0778 genCfg.stripDirLoc1 = makeDirectionFromPhiTheta(60._degree, 90._degree);
0779 RandomEngine engine{2225};
0780 runFitTest(fitCfg, genCfg, "StereoStripTest", engine, *outFile);
0781 }
0782 }
0783
0784 BOOST_AUTO_TEST_SUITE_END()
0785 }