File indexing completed on 2026-07-16 08:08:51
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/detail/FastStrawLineFitter.hpp"
0013 #include "Acts/Surfaces/detail/LineHelper.hpp"
0014 #include "Acts/Surfaces/detail/PlanarHelper.hpp"
0015 #include "Acts/Utilities/StringHelpers.hpp"
0016
0017 #include <format>
0018 #include <random>
0019
0020 #include "TFile.h"
0021 #include "TTree.h"
0022
0023 using namespace Acts;
0024 using namespace Acts::Experimental;
0025 using namespace Acts::Experimental::detail;
0026 using namespace Acts::UnitLiterals;
0027 using RandomEngine = std::mt19937;
0028 using uniform_t = std::uniform_real_distribution<double>;
0029 using gauss_t = std::normal_distribution<double>;
0030
0031 constexpr std::size_t nTrials = 1;
0032 constexpr auto logLvl = Logging::Level::INFO;
0033
0034 namespace ActsTests {
0035
0036 ACTS_LOCAL_LOGGER(getDefaultLogger("FastStrawLineFitTests", logLvl));
0037
0038 class StrawTestPoint;
0039 using TestStrawCont_t = std::vector<std::unique_ptr<StrawTestPoint>>;
0040 using Line_t = CompSpacePointAuxiliaries::Line_t;
0041 using ResidualIdx = FastStrawLineFitter::ResidualIdx;
0042
0043
0044 template <typename T>
0045 std::ostream& operator<<(std::ostream& ostr, const std::vector<T>& v) {
0046 ostr << "[";
0047 for (std::size_t i = 0; i < v.size(); ++i) {
0048 ostr << v[i];
0049 if (i + 1 != v.size()) {
0050 ostr << ", ";
0051 }
0052 }
0053 ostr << "]";
0054 return ostr;
0055 }
0056
0057 constexpr double inNanoS(const double x) {
0058 return x / 1._ns;
0059 }
0060
0061 class StrawTestPoint {
0062 public:
0063 StrawTestPoint(const Vector3& pos, const double driftR,
0064 const double driftRUncert)
0065 : m_pos{pos}, m_driftR{abs(driftR)} {
0066 m_cov[toUnderlying(ResidualIdx::bending)] = square(driftRUncert);
0067 }
0068
0069 StrawTestPoint(const Vector3& pos, const double uncert)
0070 : m_pos{pos}, m_isStraw{false} {
0071 m_cov[toUnderlying(ResidualIdx::bending)] = square(uncert);
0072 }
0073
0074 const Vector3& localPosition() const { return m_pos; }
0075
0076 const Vector3& sensorDirection() const { return m_wireDir; }
0077
0078 const Vector3& toNextSensor() const { return m_toNext; }
0079
0080 const Vector3& planeNormal() const { return m_planeNorm; }
0081
0082 double driftRadius() const { return m_driftR; }
0083
0084 const std::array<double, 3>& covariance() const { return m_cov; }
0085
0086 double driftUncert() const {
0087 return std::sqrt(m_cov[toUnderlying(ResidualIdx::bending)]);
0088 }
0089
0090 double time() const { return m_drifT; }
0091
0092 bool isStraw() const { return m_isStraw; }
0093
0094 bool hasTime() const { return false; }
0095
0096 bool measuresLoc0() const { return false; }
0097
0098 bool measuresLoc1() const { return true; }
0099 void setRadius(const double r, const double uncertR) {
0100 m_driftR = abs(r);
0101 m_cov[toUnderlying(ResidualIdx::bending)] = square(uncertR);
0102 }
0103 void setTimeRecord(const double t) { m_drifT = t; }
0104
0105 private:
0106 Vector3 m_pos{Vector3::Zero()};
0107 Vector3 m_wireDir{Vector3::UnitX()};
0108 Vector3 m_toNext{Vector3::UnitY()};
0109 Vector3 m_planeNorm{Vector3::UnitZ()};
0110 double m_driftR{0.};
0111 std::array<double, 3> m_cov{filledArray<double, 3>(0.)};
0112 double m_drifT{0.};
0113 bool m_isStraw{true};
0114 };
0115 static_assert(CompositeSpacePoint<StrawTestPoint>);
0116
0117 class StrawTestCalibrator {
0118 public:
0119
0120
0121 inline static const double CoeffRtoT = 750._ns / std::pow(15._mm, 1. / 3.);
0122 inline static const double CoeffTtoR = 1 / std::pow(CoeffRtoT, 3);
0123
0124 static double calcDriftUncert(const double driftR) {
0125 return 0.1_mm + 0.15_mm * std::pow(1._mm + std::abs(driftR), -2.);
0126 }
0127 static double driftTime(const double r) {
0128 return CoeffRtoT * std::pow(r, 1. / 3.);
0129 }
0130 static double driftRadius(const double t) {
0131 return CoeffTtoR * std::pow(t, 3);
0132 }
0133
0134 static double driftRadius(const CalibrationContext& ,
0135 const StrawTestPoint& straw, const double t0) {
0136 return driftRadius(straw.time() - t0);
0137 }
0138 static double driftVelocity(const CalibrationContext& ,
0139 const StrawTestPoint& straw, const double t0) {
0140 return 3 * CoeffTtoR * std::pow(straw.time() - t0, 2);
0141 }
0142 static double driftAcceleration(const Acts::CalibrationContext& ,
0143 const StrawTestPoint& straw,
0144 const double t0) {
0145 return 6 * CoeffTtoR * (straw.time() - t0);
0146 }
0147 };
0148 static_assert(
0149 CompositeSpacePointFastCalibrator<StrawTestCalibrator, StrawTestPoint>);
0150
0151
0152
0153
0154
0155 Line_t generateLine(RandomEngine& engine) {
0156 using ParIndex = Line_t::ParIndex;
0157 Line_t::ParamVector linePars{};
0158 linePars[toUnderlying(ParIndex::x0)] = 0.;
0159 linePars[toUnderlying(ParIndex::phi)] = 90._degree;
0160 linePars[toUnderlying(ParIndex::y0)] = uniform_t{-5000., 5000.}(engine);
0161 linePars[toUnderlying(ParIndex::theta)] =
0162 uniform_t{0.1_degree, 179.9_degree}(engine);
0163 if (abs(linePars[toUnderlying(ParIndex::theta)] - 90._degree) < 0.2_degree) {
0164 return generateLine(engine);
0165 }
0166 Line_t line{linePars};
0167 ACTS_DEBUG("Generated parameters theta: "
0168 << (linePars[toUnderlying(ParIndex::theta)] / 1._degree)
0169 << ", y0: " << linePars[toUnderlying(ParIndex::y0)] << " - "
0170 << toString(line.position()) << " + "
0171 << toString(line.direction()));
0172 return line;
0173 }
0174
0175
0176
0177
0178
0179
0180
0181
0182
0183
0184 TestStrawCont_t generateStrawCircles(const Line_t& trajLine,
0185 RandomEngine& engine, bool smearRadius) {
0186 const Vector3 posStaggering{0., std::cos(60._degree), std::sin(60._degree)};
0187 const Vector3 negStaggering{0., -std::cos(60._degree), std::sin(60._degree)};
0188
0189 constexpr std::size_t nLayersPerMl = 8;
0190
0191 constexpr std::size_t nTubeLayers = nLayersPerMl * 2;
0192
0193 constexpr double tubeRadius = 15._mm;
0194
0195 constexpr double tubeLayerDist = 1.2_m;
0196
0197 std::array<Vector3, nTubeLayers> tubePositions{
0198 filledArray<Vector3, nTubeLayers>(Vector3{0., tubeRadius, tubeRadius})};
0199
0200 for (std::size_t l = 1; l < nTubeLayers; ++l) {
0201 const Vector3& layStag{l % 2 == 1 ? posStaggering : negStaggering};
0202 tubePositions[l] = tubePositions[l - 1] + 2. * tubeRadius * layStag;
0203
0204 if (l == nLayersPerMl) {
0205 tubePositions[l] += tubeLayerDist * Vector3::UnitZ();
0206 }
0207 }
0208
0209 ACTS_DEBUG("##############################################");
0210
0211 for (std::size_t l = 0; l < nTubeLayers; ++l) {
0212 ACTS_DEBUG(" *** " << (l + 1) << " - " << toString(tubePositions[l]));
0213 }
0214 ACTS_DEBUG("##############################################");
0215
0216 TestStrawCont_t circles{};
0217
0218
0219 for (const auto& stag : tubePositions) {
0220 auto planeExtpLow =
0221 PlanarHelper::intersectPlane(trajLine.position(), trajLine.direction(),
0222 Vector3::UnitZ(), stag.z() - tubeRadius);
0223 auto planeExtpHigh =
0224 PlanarHelper::intersectPlane(trajLine.position(), trajLine.direction(),
0225 Vector3::UnitZ(), stag.z() + tubeRadius);
0226
0227 ACTS_DEBUG("Extrapolated to plane " << toString(planeExtpLow.position())
0228 << " "
0229 << toString(planeExtpHigh.position()));
0230
0231 const auto dToFirstLow = static_cast<int>(std::ceil(
0232 (planeExtpLow.position().y() - stag.y()) / (2. * tubeRadius)));
0233 const auto dToFirstHigh = static_cast<int>(std::ceil(
0234 (planeExtpHigh.position().y() - stag.y()) / (2. * tubeRadius)));
0235
0236 ACTS_DEBUG("Extrapolated to plane "
0237 << toString(planeExtpLow.position()) << " "
0238 << toString(planeExtpHigh.position())
0239 << " Hit tubes: " << dToFirstLow << " " << dToFirstHigh);
0240
0241
0242 const int dT = dToFirstHigh > dToFirstLow ? 1 : -1;
0243
0244
0245
0246 for (int tN = dToFirstLow - dT; tN != dToFirstHigh + 2 * dT; tN += dT) {
0247 const Vector3 tube = stag + 2. * tN * tubeRadius * Vector3::UnitY();
0248 const double rad = Acts::detail::LineHelper::signedDistance(
0249 tube, Vector3::UnitX(), trajLine.position(), trajLine.direction());
0250
0251 if (std::abs(rad) > tubeRadius) {
0252 continue;
0253 }
0254 gauss_t dist{rad, StrawTestCalibrator::calcDriftUncert(rad)};
0255 const double smearedR = smearRadius ? std::abs(dist(engine)) : rad;
0256 if (smearedR > tubeRadius) {
0257 continue;
0258 }
0259 circles.emplace_back(std::make_unique<StrawTestPoint>(
0260 tube, smearedR, StrawTestCalibrator::calcDriftUncert(smearedR)));
0261 ACTS_DEBUG("Tube position: "
0262 << toString(tube) << ", signedRadius: " << rad
0263 << ", smearedRadius: " << smearedR << ", uncer: "
0264 << StrawTestCalibrator::calcDriftUncert(smearedR));
0265 }
0266 }
0267 ACTS_DEBUG("Track hit in total " << circles.size() << " tubes ");
0268 return circles;
0269 }
0270
0271 TestStrawCont_t generateStrips(const Line_t& trajLine, RandomEngine& engine) {
0272 TestStrawCont_t strips{};
0273 constexpr std::array<double, 16> stripZ{-500._mm, -545_mm, -540_mm, -535._mm,
0274 -250._mm, -245_mm, -240_mm, -235._mm,
0275 335._mm, 340_mm, 345_mm, 350._mm,
0276 435._mm, 440_mm, 445_mm, 400._mm};
0277 constexpr double stripPitch = 1._cm;
0278 for (const auto z : stripZ) {
0279 auto planeExtp = PlanarHelper::intersectPlane(
0280 trajLine.position(), trajLine.direction(), Vector3::UnitZ(), z);
0281 Vector3 stripPos = planeExtp.position();
0282 stripPos[eY] = gauss_t{stripPos[eY], stripPitch}(engine);
0283 strips.emplace_back(std::make_unique<StrawTestPoint>(stripPos, stripPitch));
0284 }
0285 return strips;
0286 }
0287
0288
0289
0290 double calcChi2(const TestStrawCont_t& measurements, const Line_t& track) {
0291 double chi2{0.};
0292 for (const auto& meas : measurements) {
0293 const double dist = Acts::detail::LineHelper::signedDistance(
0294 meas->localPosition(), meas->sensorDirection(), track.position(),
0295 track.direction());
0296 ACTS_DEBUG("Distance straw: " << toString(meas->localPosition())
0297 << ", r: " << meas->driftRadius()
0298 << " - to track: " << abs(dist));
0299
0300 chi2 += square((abs(dist) - meas->driftRadius()) / meas->driftUncert());
0301 }
0302 return chi2;
0303 }
0304
0305 #define DECLARE_BRANCH(dTYPE, NAME) \
0306 dTYPE NAME{}; \
0307 outTree->Branch(#NAME, &NAME);
0308
0309 BOOST_AUTO_TEST_SUITE(SeedingSuite)
0310
0311 void testSimpleStrawFit(RandomEngine& engine, TFile& outFile) {
0312 auto outTree = std::make_unique<TTree>("StrawFitTree", "FastFitTree");
0313
0314 DECLARE_BRANCH(double, trueY0);
0315 DECLARE_BRANCH(double, trueTheta);
0316 DECLARE_BRANCH(double, recoY0);
0317 DECLARE_BRANCH(double, recoTheta);
0318 DECLARE_BRANCH(double, uncertY0);
0319 DECLARE_BRANCH(double, uncertTheta);
0320 DECLARE_BRANCH(double, chi2);
0321 DECLARE_BRANCH(std::size_t, nDoF);
0322 DECLARE_BRANCH(std::size_t, nIter);
0323
0324 FastStrawLineFitter::Config cfg{};
0325 FastStrawLineFitter fastFitter{cfg, getDefaultLogger("FitterNoT0", logLvl)};
0326 ACTS_INFO("Start simple straw fit test.");
0327 for (std::size_t n = 0; n < nTrials; ++n) {
0328 if ((n + 1) % 1000 == 0) {
0329 ACTS_INFO(" -- processed " << (n + 1) << "/" << nTrials << " events");
0330 }
0331 auto track = generateLine(engine);
0332 auto strawPoints = generateStrawCircles(track, engine, true);
0333 if (strawPoints.size() < 3) {
0334 ACTS_WARNING(__func__ << "() - " << __LINE__ << ": -- event: " << n
0335 << ", track " << toString(track.position()) << " + "
0336 << toString(track.direction())
0337 << " did not lead to any valid measurement ");
0338 continue;
0339 }
0340 const std::vector<int> trueDriftSigns =
0341 CompSpacePointAuxiliaries::strawSigns(track, strawPoints);
0342
0343 BOOST_CHECK_LE(calcChi2(generateStrawCircles(track, engine, false), track),
0344 1.e-12);
0345 ACTS_DEBUG("True drift signs: " << trueDriftSigns << ", chi2: " << chi2);
0346
0347 auto fitResult = fastFitter.fit(strawPoints, trueDriftSigns);
0348 if (!fitResult) {
0349 continue;
0350 }
0351 auto trackPars = track.parameters();
0352
0353 trueY0 = trackPars[toUnderlying(Line_t::ParIndex::y0)];
0354 trueTheta = trackPars[toUnderlying(Line_t::ParIndex::theta)];
0355
0356 trackPars[toUnderlying(Line_t::ParIndex::theta)] = (*fitResult).theta;
0357 trackPars[toUnderlying(Line_t::ParIndex::y0)] = (*fitResult).y0;
0358 trackPars[toUnderlying(Line_t::ParIndex::phi)] = 90._degree;
0359 track.updateParameters(trackPars);
0360 ACTS_DEBUG("Updated parameters: "
0361 << (trackPars[toUnderlying(Line_t::ParIndex::theta)] / 1._degree)
0362 << ", y0: " << trackPars[toUnderlying(Line_t::ParIndex::y0)]
0363 << " -- " << toString(track.position()) << " + "
0364 << toString(track.direction()));
0365
0366 const double testChi2 = calcChi2(strawPoints, track);
0367 ACTS_DEBUG("testChi2: " << testChi2 << ", fit:" << (*fitResult).chi2);
0368
0369 BOOST_CHECK_LE(abs(testChi2 - (*fitResult).chi2), 1.e-9);
0370 recoTheta = (*fitResult).theta;
0371 recoY0 = (*fitResult).y0;
0372 uncertTheta = (*fitResult).dTheta;
0373 uncertY0 = (*fitResult).dY0;
0374 nDoF = (*fitResult).nDoF;
0375 chi2 = (*fitResult).chi2;
0376 nIter = (*fitResult).nIter;
0377 outTree->Fill();
0378 }
0379 outFile.WriteObject(outTree.get(), outTree->GetName());
0380 }
0381
0382 void testFitWithT0(RandomEngine& engine, TFile& outFile) {
0383 auto outTree = std::make_unique<TTree>("StrawFitTreeT0", "FastFitTree");
0384
0385 DECLARE_BRANCH(double, trueY0);
0386 DECLARE_BRANCH(double, trueTheta);
0387 DECLARE_BRANCH(double, trueT0);
0388 DECLARE_BRANCH(double, recoY0);
0389 DECLARE_BRANCH(double, recoTheta);
0390 DECLARE_BRANCH(double, recoT0);
0391 DECLARE_BRANCH(double, uncertY0);
0392 DECLARE_BRANCH(double, uncertTheta);
0393 DECLARE_BRANCH(double, uncertT0);
0394 DECLARE_BRANCH(double, chi2);
0395 DECLARE_BRANCH(double, meanSign);
0396 DECLARE_BRANCH(std::size_t, nDoF);
0397 DECLARE_BRANCH(std::size_t, nIter);
0398
0399 FastStrawLineFitter::Config cfg{};
0400 cfg.maxIter = 500;
0401 FastStrawLineFitter fastFitter{cfg, getDefaultLogger("FitterWithT0", logLvl)};
0402 StrawTestCalibrator calibrator{};
0403 CalibrationContext cctx{};
0404 ACTS_INFO("Start straw fit with t0 test.");
0405 for (std::size_t n = 0; n < nTrials; ++n) {
0406 if ((n + 1) % 1000 == 0) {
0407 ACTS_INFO(" -- processed " << (n + 1) << "/" << nTrials << " events");
0408 }
0409 auto track = generateLine(engine);
0410 const double timeOffSet = uniform_t{-50._ns, 50._ns}(engine);
0411
0412 ACTS_DEBUG("Generated time offset: " << inNanoS(timeOffSet) << " [ns]");
0413 auto strawPoints = generateStrawCircles(track, engine, true);
0414
0415 if (strawPoints.size() < 4) {
0416 ACTS_WARNING(__func__ << "() - " << __LINE__ << ": -- event: " << n
0417 << ", track " << toString(track.position()) << " + "
0418 << toString(track.direction())
0419 << " did not lead to any valid measurement ");
0420 continue;
0421 }
0422 BOOST_CHECK_LE(calcChi2(generateStrawCircles(track, engine, false), track),
0423 1.e-12);
0424
0425
0426 const std::vector<int> trueDriftSigns =
0427 CompSpacePointAuxiliaries::strawSigns(track, strawPoints);
0428
0429 ACTS_DEBUG("Straw signs: " << trueDriftSigns);
0430
0431 for (auto& meas : strawPoints) {
0432 const double dTime = StrawTestCalibrator::driftTime(meas->driftRadius());
0433 BOOST_CHECK_CLOSE(StrawTestCalibrator::driftRadius(dTime),
0434 meas->driftRadius(), 1.e-12);
0435 meas->setTimeRecord(dTime + timeOffSet);
0436
0437 const double updatedR =
0438 StrawTestCalibrator::driftRadius(dTime + timeOffSet);
0439
0440 BOOST_CHECK_CLOSE(StrawTestCalibrator::driftRadius(dTime),
0441 calibrator.driftRadius(cctx, *meas, timeOffSet), 1.e-3);
0442
0443 ACTS_DEBUG("Update drift radius of tube "
0444 << toString(meas->localPosition()) << " from "
0445 << meas->driftRadius() << " to " << updatedR
0446 << ", dTime: " << inNanoS(dTime));
0447 meas->setRadius(updatedR, StrawTestCalibrator::calcDriftUncert(updatedR));
0448
0449
0450 constexpr double h = 1.e-8_ns;
0451 const double numV =
0452 -(calibrator.driftRadius(cctx, *meas, timeOffSet + h) -
0453 calibrator.driftRadius(cctx, *meas, timeOffSet - h)) /
0454 (2. * h);
0455 BOOST_CHECK_LE(
0456 abs(numV - calibrator.driftVelocity(cctx, *meas, timeOffSet)) /
0457 std::max(numV, 1.),
0458 1.e-3);
0459 }
0460
0461 auto result = fastFitter.fit(cctx, calibrator, strawPoints, trueDriftSigns,
0462 std::nullopt);
0463
0464 if (!result) {
0465 continue;
0466 }
0467 auto linePars = track.parameters();
0468
0469 trueY0 = linePars[toUnderlying(Line_t::ParIndex::y0)];
0470 trueTheta = linePars[toUnderlying(Line_t::ParIndex::theta)];
0471 trueT0 = inNanoS(timeOffSet);
0472
0473 recoY0 = (*result).y0;
0474 recoTheta = (*result).theta;
0475 recoT0 = inNanoS((*result).t0);
0476 uncertY0 = (*result).dY0;
0477 uncertTheta = (*result).dTheta;
0478 uncertT0 = inNanoS((*result).dT0);
0479 nDoF = (*result).nDoF;
0480 chi2 = (*result).chi2;
0481 nIter = (*result).nIter;
0482 meanSign = {0.};
0483 std::ranges::for_each(trueDriftSigns,
0484 [&meanSign](const int sign) { meanSign += sign; });
0485 meanSign /= static_cast<double>(trueDriftSigns.size());
0486 outTree->Fill();
0487 }
0488 outFile.WriteObject(outTree.get(), outTree->GetName());
0489 }
0490
0491 void testStripFit(RandomEngine& engine, TFile& outFile) {
0492 auto outTree = std::make_unique<TTree>("StripFitTree", "FastFitTree");
0493
0494 DECLARE_BRANCH(double, trueY0);
0495 DECLARE_BRANCH(double, trueTheta);
0496 DECLARE_BRANCH(double, recoY0);
0497 DECLARE_BRANCH(double, recoTheta);
0498 DECLARE_BRANCH(double, uncertY0);
0499 DECLARE_BRANCH(double, uncertTheta);
0500 DECLARE_BRANCH(double, chi2);
0501 DECLARE_BRANCH(std::size_t, nDoF);
0502 DECLARE_BRANCH(std::size_t, nIter);
0503
0504 FastStrawLineFitter::Config cfg{};
0505 FastStrawLineFitter fastFitter{cfg, getDefaultLogger("StripFitter", logLvl)};
0506 ACTS_INFO("Start strip fit test.");
0507 for (std::size_t n = 0; n < nTrials; ++n) {
0508 if ((n + 1) % 1000 == 0) {
0509 ACTS_INFO(" -- processed " << (n + 1) << "/" << nTrials << " events");
0510 }
0511 auto track = generateLine(engine);
0512 auto linePars = track.parameters();
0513
0514 trueY0 = linePars[toUnderlying(Line_t::ParIndex::y0)];
0515 trueTheta = linePars[toUnderlying(Line_t::ParIndex::theta)];
0516
0517 auto stripPoints = generateStrips(track, engine);
0518
0519 auto result = fastFitter.fit(stripPoints, ResidualIdx::bending);
0520 if (!result) {
0521 continue;
0522 }
0523
0524 recoY0 = (*result).y0;
0525 recoTheta = (*result).theta;
0526 uncertY0 = (*result).dY0;
0527 uncertTheta = (*result).dTheta;
0528 nDoF = (*result).nDoF;
0529 nIter = (*result).nIter;
0530 chi2 = 0.;
0531 auto pos = recoY0 * Vector3::UnitY();
0532 auto dir = makeDirectionFromPhiTheta(90._degree, recoTheta);
0533 for (const auto& strip : stripPoints) {
0534 chi2 += CompSpacePointAuxiliaries::chi2Term(pos, dir, *strip);
0535 }
0536
0537 outTree->Fill();
0538 }
0539
0540 outFile.WriteObject(outTree.get(), outTree->GetName());
0541 }
0542
0543 BOOST_AUTO_TEST_CASE(FitterTests) {
0544 RandomEngine engine{1800};
0545
0546 std::unique_ptr<TFile> outFile{
0547 TFile::Open("FastStrawLineFitTest.root", "RECREATE")};
0548
0549 BOOST_CHECK_EQUAL(outFile->IsZombie(), false);
0550
0551 testSimpleStrawFit(engine, *outFile);
0552 testStripFit(engine, *outFile);
0553 testFitWithT0(engine, *outFile);
0554 }
0555
0556 BOOST_AUTO_TEST_SUITE_END()
0557 }