File indexing completed on 2026-10-20 08:42:21
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <boost/test/unit_test.hpp>
0010
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/Alignment.hpp"
0013 #include "Acts/Surfaces/PlaneSurface.hpp"
0014 #include "Acts/Surfaces/RectangleBounds.hpp"
0015 #include "Acts/Surfaces/Surface.hpp"
0016 #include "Acts/Utilities/Logger.hpp"
0017 #include "ActsAlignment/Kernel/detail/AlignmentEngine.hpp"
0018 #include "ActsPlugins/Mille/ActsToMille.hpp"
0019
0020 #include <algorithm>
0021 #include <map>
0022 #include <memory>
0023 #include <set>
0024 #include <string>
0025 #include <unordered_map>
0026 #include <vector>
0027
0028 #include "Mille/MilleFactory.h"
0029
0030
0031
0032
0033
0034
0035 namespace {
0036
0037 constexpr std::size_t nSurfaces = 6;
0038
0039
0040
0041 struct Hit {
0042 float residual;
0043 std::size_t surface;
0044 std::vector<std::size_t> dofs = {0, 1, 2, 3, 4, 5};
0045 };
0046
0047 int label(std::size_t surface, std::size_t dof) {
0048 return static_cast<int>(surface * Acts::eAlignmentSize + dof + 1);
0049 }
0050 double derivative(std::size_t surface, std::size_t dof) {
0051 return 0.25 + 0.5 * surface + 0.125 * dof;
0052 }
0053
0054 void writeRecord(Mille::MilleRecord& record, const std::vector<Hit>& hits) {
0055 unsigned int local = 1;
0056 for (const Hit& hit : hits) {
0057 std::vector<int> labels;
0058 std::vector<double> derivatives;
0059 for (std::size_t dof : hit.dofs) {
0060 labels.push_back(label(hit.surface, dof));
0061 derivatives.push_back(derivative(hit.surface, dof));
0062 }
0063
0064
0065 record.addData(hit.residual, 0.1f, std::vector<unsigned int>{local},
0066 std::vector<double>{1.0}, labels, derivatives);
0067 ++local;
0068 }
0069 record.writeRecord();
0070 }
0071
0072 }
0073
0074 BOOST_AUTO_TEST_SUITE(ActsMilleReadRecordSequenceTests)
0075
0076 BOOST_AUTO_TEST_CASE(ReuseStateAcrossRecords) {
0077 ACTS_LOCAL_LOGGER(
0078 Acts::getDefaultLogger("ReuseStateAcrossRecords", Acts::Logging::INFO));
0079
0080 std::vector<std::shared_ptr<Acts::Surface>> surfaces;
0081 std::unordered_map<const Acts::Surface*, std::size_t> idxedAlignSurfaces;
0082 for (std::size_t s = 0; s < nSurfaces; ++s) {
0083 surfaces.push_back(Acts::Surface::makeShared<Acts::PlaneSurface>(
0084 Acts::Transform3::Identity(),
0085 std::make_shared<Acts::RectangleBounds>(10., 10.)));
0086 idxedAlignSurfaces.emplace(surfaces.back().get(), s);
0087 }
0088
0089
0090 const std::vector<std::vector<Hit>> records = {
0091
0092 {{0.01f, 0}, {0.02f, 1}, {0.03f, 2}, {0.04f, 3}, {0.05f, 4}, {0.06f, 5}},
0093
0094 {{0.07f, 5}},
0095
0096 {{0.01f, 0}, {0.02f, 1}, {0.04f, 3}, {0.05f, 4}, {0.06f, 5}},
0097
0098 {{0.01f, 0}, {0.02f, 1}},
0099
0100 {{0.03f, 3}, {0.05f, 4, {2, 3, 4, 5}}},
0101
0102 {{0.f, 1}, {0.02f, 2}},
0103 };
0104
0105 const std::string fname = "ActsMilleReadRecordSequence.dat";
0106 {
0107 std::unique_ptr<Mille::MilleRecord> out = Mille::spawnMilleRecord(fname);
0108 BOOST_REQUIRE(out != nullptr);
0109 for (const auto& hits : records) {
0110 writeRecord(*out, hits);
0111 }
0112 }
0113
0114 auto reader = Mille::spawnMilleReader(fname);
0115 BOOST_REQUIRE(reader != nullptr);
0116 BOOST_REQUIRE(reader->open(fname));
0117
0118
0119 ActsAlignment::detail::TrackAlignmentState state;
0120 for (const auto& hits : records) {
0121 BOOST_REQUIRE(ActsPlugins::ActsToMille::unpackMilleRecord(
0122 *reader, state, idxedAlignSurfaces, logger()) ==
0123 Mille::MilleDecoder::ReadResult::OK);
0124
0125 std::set<std::size_t> onTrack;
0126 for (const Hit& hit : hits) {
0127 onTrack.insert(hit.surface);
0128 }
0129
0130 BOOST_CHECK_EQUAL(state.alignedSurfaces.size(), onTrack.size());
0131 BOOST_CHECK_EQUAL(state.alignmentDof,
0132 Acts::eAlignmentSize * onTrack.size());
0133 BOOST_CHECK_EQUAL(state.measurementDim, hits.size());
0134 BOOST_CHECK_EQUAL(state.alignmentToResidualDerivative.rows(),
0135 static_cast<long>(hits.size()));
0136 BOOST_CHECK_EQUAL(state.alignmentToResidualDerivative.cols(),
0137 static_cast<long>(state.alignmentDof));
0138 std::map<std::size_t, std::size_t> internalOf;
0139 std::size_t expectedInternal = 0;
0140 for (std::size_t s : onTrack) {
0141 internalOf[s] = expectedInternal++;
0142 }
0143 for (const auto& [surface, indices] : state.alignedSurfaces) {
0144 const auto [globalIndex, internalIndex] = indices;
0145 BOOST_CHECK_EQUAL(globalIndex, idxedAlignSurfaces.at(surface));
0146 BOOST_REQUIRE(internalOf.contains(globalIndex));
0147 BOOST_CHECK_EQUAL(internalIndex, internalOf.at(globalIndex));
0148 }
0149
0150 for (std::size_t iMeas = 0; iMeas < hits.size(); ++iMeas) {
0151 const Hit& hit = hits[iMeas];
0152 for (std::size_t s : onTrack) {
0153 for (std::size_t dof = 0; dof < Acts::eAlignmentSize; ++dof) {
0154 const bool written =
0155 s == hit.surface &&
0156 std::ranges::find(hit.dofs, dof) != hit.dofs.end();
0157 const double value = state.alignmentToResidualDerivative(
0158 iMeas, internalOf.at(s) * Acts::eAlignmentSize + dof);
0159 if (written) {
0160 BOOST_CHECK_CLOSE(value, derivative(s, dof), 1e-4);
0161 } else {
0162 BOOST_CHECK_EQUAL(value, 0.);
0163 }
0164 }
0165 }
0166 }
0167 }
0168 BOOST_CHECK(ActsPlugins::ActsToMille::unpackMilleRecord(
0169 *reader, state, idxedAlignSurfaces, logger()) ==
0170 Mille::MilleDecoder::ReadResult::atEof);
0171 }
0172
0173 BOOST_AUTO_TEST_CASE(UnknownSurfaceIsReadError) {
0174 ACTS_LOCAL_LOGGER(
0175 Acts::getDefaultLogger("UnknownSurfaceIsReadError", Acts::Logging::INFO));
0176 std::vector<std::shared_ptr<Acts::Surface>> surfaces;
0177 std::unordered_map<const Acts::Surface*, std::size_t> idxedAlignSurfaces;
0178 for (std::size_t s = 0; s < 2; ++s) {
0179 surfaces.push_back(Acts::Surface::makeShared<Acts::PlaneSurface>(
0180 Acts::Transform3::Identity(),
0181 std::make_shared<Acts::RectangleBounds>(10., 10.)));
0182 idxedAlignSurfaces.emplace(surfaces.back().get(), s);
0183 }
0184
0185 const std::string fname = "ActsMilleReadUnknownSurface.dat";
0186 {
0187 std::unique_ptr<Mille::MilleRecord> out = Mille::spawnMilleRecord(fname);
0188 BOOST_REQUIRE(out != nullptr);
0189 writeRecord(*out, {{0.01f, 0}, {0.02f, 1}});
0190
0191 writeRecord(*out, {{0.01f, 0}, {0.02f, 4}});
0192 }
0193 auto reader = Mille::spawnMilleReader(fname);
0194 BOOST_REQUIRE(reader != nullptr);
0195 BOOST_REQUIRE(reader->open(fname));
0196
0197 #ifdef ACTS_ENABLE_LOG_FAILURE_THRESHOLD
0198
0199 Acts::Logging::ScopedFailureThreshold threshold{Acts::Logging::FATAL};
0200 #endif
0201 ActsAlignment::detail::TrackAlignmentState state;
0202 BOOST_REQUIRE(ActsPlugins::ActsToMille::unpackMilleRecord(
0203 *reader, state, idxedAlignSurfaces, logger()) ==
0204 Mille::MilleDecoder::ReadResult::OK);
0205 BOOST_CHECK_EQUAL(state.alignmentDof, 2 * Acts::eAlignmentSize);
0206 BOOST_CHECK(ActsPlugins::ActsToMille::unpackMilleRecord(
0207 *reader, state, idxedAlignSurfaces, logger()) ==
0208 Mille::MilleDecoder::ReadResult::error);
0209
0210 BOOST_CHECK_EQUAL(state.alignmentDof, 2 * Acts::eAlignmentSize);
0211 BOOST_CHECK_EQUAL(state.alignedSurfaces.size(), 2u);
0212 }
0213
0214 BOOST_AUTO_TEST_CASE(GlobalsWithoutLocalsIsReadError) {
0215 ACTS_LOCAL_LOGGER(Acts::getDefaultLogger("GlobalsWithoutLocalsIsReadError",
0216 Acts::Logging::INFO));
0217 std::vector<std::shared_ptr<Acts::Surface>> surfaces;
0218 std::unordered_map<const Acts::Surface*, std::size_t> idxedAlignSurfaces;
0219 for (std::size_t s = 0; s < 2; ++s) {
0220 surfaces.push_back(Acts::Surface::makeShared<Acts::PlaneSurface>(
0221 Acts::Transform3::Identity(),
0222 std::make_shared<Acts::RectangleBounds>(10., 10.)));
0223 idxedAlignSurfaces.emplace(surfaces.back().get(), s);
0224 }
0225
0226 const std::string fname = "ActsMilleReadGlobalsWithoutLocals.dat";
0227 {
0228 std::unique_ptr<Mille::MilleRecord> out = Mille::spawnMilleRecord(fname);
0229 BOOST_REQUIRE(out != nullptr);
0230 writeRecord(*out, {{0.01f, 0}, {0.02f, 1}});
0231
0232
0233 out->addData(0.01f, 0.1f, std::vector<unsigned int>{1u},
0234 std::vector<double>{1.0}, std::vector<int>{label(0, 0)},
0235 std::vector<double>{derivative(0, 0)});
0236 out->addData(0.02f, 0.1f, std::vector<unsigned int>{2u},
0237 std::vector<double>{0.0}, std::vector<int>{label(1, 0)},
0238 std::vector<double>{derivative(1, 0)});
0239 out->writeRecord();
0240 }
0241 auto reader = Mille::spawnMilleReader(fname);
0242 BOOST_REQUIRE(reader != nullptr);
0243 BOOST_REQUIRE(reader->open(fname));
0244
0245 #ifdef ACTS_ENABLE_LOG_FAILURE_THRESHOLD
0246
0247 Acts::Logging::ScopedFailureThreshold threshold{Acts::Logging::FATAL};
0248 #endif
0249 ActsAlignment::detail::TrackAlignmentState state;
0250 BOOST_REQUIRE(ActsPlugins::ActsToMille::unpackMilleRecord(
0251 *reader, state, idxedAlignSurfaces, logger()) ==
0252 Mille::MilleDecoder::ReadResult::OK);
0253 BOOST_CHECK(ActsPlugins::ActsToMille::unpackMilleRecord(
0254 *reader, state, idxedAlignSurfaces, logger()) ==
0255 Mille::MilleDecoder::ReadResult::error);
0256
0257 BOOST_CHECK_EQUAL(state.alignmentDof, 2 * Acts::eAlignmentSize);
0258 }
0259
0260 BOOST_AUTO_TEST_SUITE_END()