12#define BOOST_TEST_MODULE ITSMFT workflow session
13#define BOOST_TEST_MAIN
14#define BOOST_TEST_DYN_LINK
15#include <boost/test/unit_test.hpp>
17#include <boost/mpl/list.hpp>
30using LayerCounts = boost::mpl::list<std::integral_constant<int, ITSNLayers>, std::integral_constant<int, MFTNLayers>>;
40 static constexpr auto Detector = N ==
ITSNLayers ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT;
44 std::shared_ptr<tbb::task_arena> arena;
46 std::array<LayerId, N> mapping{};
47 std::vector<ROFRecord> rofs{{{100, 5}, 0, 0, 0}};
48 std::vector<CompClusterExt>
clusters;
50 explicit Rig(
bool drop =
false,
size_t memory = std::numeric_limits<size_t>::max())
52 auto parameters = test::makeTestTrackingParameters(
Detector);
53 parameters.UseDiamond =
true;
54 auto plan = test::makeTrackingPlan(parameters);
55 plan.execution = {
memory, drop};
58 TrackerInitialization init{catalog, {0}, {}, std::move(plan), std::make_shared<BoundedMemoryResource>()};
59 BOOST_REQUIRE(tracker.
initialize(session.frame, init));
68 std::vector<o2::its::LayerTiming> timings(N);
69 std::fill(timings.begin(), timings.end(),
o2::its::LayerTiming{.mNROFsTF = 1, .mROFLength = 40});
70 session.configureTiming(timings, [](
int) {
return true; });
96 Rig<Count::value> rig;
97 int loaded = 0, completed = 0;
99 auto cleanup = rig.session.cleanupOnExit();
102 BOOST_CHECK(origin == rig.rofs.front().getBCData());
103 BOOST_CHECK_EQUAL(rig.session.frame.getTotalMeasurements(), 0u);
104 BOOST_CHECK_EQUAL(rig.session.frame.getROFViews().overlap.mLayerCount, Count::value); }, [&](
const TrackingStatistics&
result) {
106 BOOST_CHECK(result.elapsedMs > 0.f);
107 BOOST_REQUIRE_EQUAL(result.acceptedTrackCounts.size(), 1u);
108 BOOST_CHECK_EQUAL(result.acceptedTrackCounts[0], 0u); });
119 Rig<Count::value> rig;
120 const auto views = rig.session.frame.getROFViews();
121 const auto source = rig.source();
122 auto& frame = rig.session.frame;
124 frame.getDetectorConfiguration().getSurfaceCatalog()));
127 const auto boundaries = frame.getROFrameClusters(
layer);
128 BOOST_REQUIRE_EQUAL(boundaries.size(), 2u);
133 frame.setROFViews(views);
135 frame.setROFViews(0, views, Count::value - 1);
136 const std::array<int, 3> boundaries{0, 0, 0};
137 frame.setROFClusters(0, boundaries);
140 frame.setROFViews(0, views, 0);
146 for (
bool drop : {
false,
true}) {
147 Rig<Count::value> rig{drop};
148 rig.rofs[0].setNEntries(1);
150 const auto run = [&] {
151 auto cleanup = rig.session.cleanupOnExit();
156 BOOST_CHECK_THROW(run(), std::runtime_error);
164 for (
bool drop : {
false,
true}) {
165 Rig<Count::value> rig{drop};
166 const auto run = [&] {
167 rig.session.loadWithRecovery(drop, [&] {
173 BOOST_REQUIRE_EQUAL(rig.session.frame.getTotalMeasurements(), 1u);
175 auto patterns = gsl::span<const unsigned char>{}.begin();
179 BOOST_CHECK_EXCEPTION(run(), std::runtime_error, [](
const std::runtime_error& error) {
180 return std::string(error.what()).find(
"Cluster pattern ID is outside the topology dictionary") != std::string::npos;
188 for (
bool drop : {
false,
true}) {
189 Rig<Count::value> rig{drop};
190 auto source = rig.source();
192 const auto run = [&] {
193 rig.session.loadWithRecovery(drop, [&] {
197 BOOST_CHECK_EXCEPTION(run(), std::runtime_error, [](
const std::runtime_error& error) {
198 const std::string
message = error.what();
199 return message.find(
"Invalid ROF timing: source=0 rof=0") != std::string::npos;
207 for (
bool drop : {
false,
true}) {
208 Rig<Count::value> rig{drop};
209 auto source = rig.source();
210 source.dictionary =
nullptr;
211 const auto run = [&] {
212 auto cleanup = rig.session.cleanupOnExit();
215 BOOST_CHECK_THROW(run(), std::runtime_error);
219 auto cleanup = rig.session.cleanupOnExit();
222 BOOST_CHECK_THROW(
publish(), std::runtime_error);
229 for (
bool drop : {
false,
true}) {
230 for (
bool bounded : {
false,
true}) {
231 Rig<Count::value> rig{drop};
232 const auto run = [&] {
233 auto cleanup = rig.session.cleanupOnExit();
234 const auto outcome = rig.session.process(rig.tracker, rig.traits, rig.source(), [&](
const o2::InteractionRecord&) {
236 throw std::bad_alloc{}; }, [](
const TrackingStatistics&) { BOOST_FAIL(
"must not track after failed loading"); });
243 BOOST_CHECK_THROW(run(), std::bad_alloc);
252 Rig<Count::value> rig{
true, 1};
253 auto cleanup = rig.session.cleanupOnExit();
254 const auto outcome = rig.session.process(rig.tracker, rig.traits, rig.source(), [](
const o2::InteractionRecord&) {}, [](
const TrackingStatistics&) { BOOST_FAIL(
"must not complete a dropped TF"); });
256 BOOST_CHECK(rig.session.frame.getGenericTracks().empty());
262 Rig<Count::value> rig;
264 auto cleanup = rig.session.cleanupOnExit();
265 std::vector<o2::its::LayerTiming> timings(Count::value);
266 std::fill(timings.begin(), timings.end(),
o2::its::LayerTiming{.mNROFsTF = 3, .mROFLength = 40});
267 rig.session.configureTiming(timings, [](
int rof) {
return rof != 1; });
269 const auto views = rig.session.frame.getROFViews();
276 std::fill(timings.begin(), timings.end(),
o2::its::LayerTiming{.mNROFsTF = 3, .mROFLength = 40});
277 timings[1].mROFLength = 41;
278 BOOST_CHECK_THROW(rig.session.configureTiming(timings, [](
int) { return true; }), std::runtime_error);
279 BOOST_CHECK_EQUAL(rig.session.frame.getROFViews().overlap.getLayer(1).mROFLength, 40u);
286 for (
auto outcome : {
true,
false}) {
293 for (
bool drop : {
false,
true}) {
294 for (
bool standard : {
false,
true}) {
295 Rig<Count::value> rig{drop};
296 const auto run = [&] {
297 auto cleanup = rig.session.cleanupOnExit();
299 if (standard) {
throw std::logic_error{
"unexpected loading failure"}; }
300 throw 7; }, [](
const TrackingStatistics&) { BOOST_FAIL(
"must not complete after an exception"); });
303 BOOST_CHECK_THROW(run(), std::logic_error);
305 BOOST_CHECK_THROW(run(),
int);
314struct TestOutputAllocator {
315 std::map<int, std::any>
values;
316 template <
typename Vector,
typename Iterator>
327 std::vector<GenericTrack>
tracks;
331 Rig<Count::value> rig;
332 TestOutputAllocator outputs;
334 auto cleanup = rig.session.cleanupOnExit();
336 staged.tracks.resize(1);
338 staged.clusterIndices = {17, 23};
340 staged.clusterIndices[0] = 99;
341 staged.trackROFs[0].setNEntries(0);
342 staged.tracks.clear();
345 const auto& rofs = std::any_cast<const std::vector<ROFRecord>&>(outputs.values.at(0));
346 BOOST_REQUIRE_EQUAL(rofs.size(), 1u);
349 BOOST_CHECK_EQUAL(std::any_cast<
const decltype(Staged{}.tracks)&>(outputs.values.at(1)).size(), 1u);
350 const auto&
indices = std::any_cast<const std::vector<int>&>(outputs.values.at(2));
351 const std::vector<int>
expected{17, 23};
357 struct AlpideTiming {
359 int getROFLengthInBC(
int)
const {
return length; }
360 int getROFDelayInBC(
int)
const {
return 3; }
361 int getROFBiasInBC(
int)
const {
return 4; }
363 Rig<Count::value> rig;
364 const std::vector<uint32_t> timeErrors(Count::value, 5);
365 const auto timings = rig.session.layerTimings(alpide, 2, timeErrors);
366 for (
const auto& timing : timings) {
373 BOOST_CHECK_EXCEPTION(rig.session.layerTimings(alpide, 0, timeErrors), std::runtime_error,
374 [](
const std::runtime_error& error) { return std::string(error.what()).find(
"zero ROFs") != std::string::npos; });
375 BOOST_CHECK_THROW(rig.session.layerTimings(alpide, 2, std::vector<uint32_t>(Count::value - 1)), std::runtime_error);
377 BOOST_CHECK_EXCEPTION(rig.session.layerTimings(alpide, 2, timeErrors), std::runtime_error,
378 [](
const std::runtime_error& error) { return std::string(error.what()).find(
"non-positive ROF length") != std::string::npos; });
384 std::vector<o2::its::LayerTiming> timings(Count::value);
385 std::fill(timings.begin(), timings.end(),
o2::its::LayerTiming{.mNROFsTF = 3, .mROFLength = 40});
386 session.configureTiming(timings, [](
int rof) {
return rof == 0; });
388 vertex.getTimeStamp().setTimeStamp(20);
389 vertex.getTimeStamp().setTimeStampError(5);
390 session.vertices.update(&
vertex, 1);
391 const auto overlapStorage = session.overlap.getView().mFlatTable;
392 const auto vertexStorage = session.vertices.getView().mFlatTable;
393 const auto maskStorage = session.mask.getView().mFlatMask;
394 BOOST_REQUIRE_EQUAL(session.vertices.getView().getVertices(0, 0).getEntries(), 1u);
396 session.invalidatePublication();
400 session.configureTiming(timings, [&](
int rof) { ++calls;
return rof == 2; });
403 BOOST_CHECK(session.overlap.getView().mFlatTable == overlapStorage);
404 BOOST_CHECK(session.vertices.getView().mFlatTable == vertexStorage);
405 BOOST_CHECK(session.mask.getView().mFlatMask == maskStorage);
407 for (
int rof = 0; rof < 3; ++rof) {
408 const auto range = session.vertices.getView().getVertices(
layer, rof);
415 vertex.getTimeStamp().setTimeStamp(100);
416 session.vertices.update(&
vertex, 1);
417 BOOST_CHECK_EQUAL(session.vertices.getView().getVertices(0, 2).getEntries(), 1u);
418 std::fill(timings.begin(), timings.end(),
o2::its::LayerTiming{.mNROFsTF = 3, .mROFLength = 40});
419 session.configureTiming(timings, [](
int) {
return false; });
420 BOOST_CHECK_EQUAL(session.vertices.getView().getVertices(0, 2).getEntries(), 0u);
421 BOOST_CHECK(!session.frame.getROFViews().mask.isROFEnabled(0, 2));
427 constexpr std::array
fields{&Timing::mNROFsTF, &Timing::mROFLength, &Timing::mROFDelay,
428 &Timing::mROFBias, &Timing::mROFAddTimeErr};
429 std::vector<Timing> baseline(Count::value);
430 std::fill(baseline.begin(), baseline.end(), Timing{.mNROFsTF = 3, .mROFLength = 40});
432 const auto accept = [](
int rof) {
return rof < 3 && rof != 1; };
433 const auto compareWithFresh = [&](
const auto& timings) {
437 const auto actual = session.overlap.getView();
438 const auto expected = fresh.overlap.getView();
440 for (
auto field :
fields) {
444 for (uint32_t rof = 0; rof < timings[
layer].mNROFsTF; ++rof) {
447 for (
int to = 0; to < Count::value; ++to) {
457 for (
auto field :
fields) {
458 compareWithFresh(baseline);
459 auto changed = baseline;
460 for (
auto& timing : changed) {
463 compareWithFresh(changed);
464 compareWithFresh(changed);
465 compareWithFresh(baseline);
466 if (field != &Timing::mNROFsTF) {
468 auto nonuniform = baseline;
469 nonuniform[
layer].*field += 1;
470 BOOST_CHECK_THROW(session.configureTiming(nonuniform, accept), std::runtime_error);
476 auto changed = baseline;
477 changed[
layer].mNROFsTF += 1;
478 compareWithFresh(changed);
479 compareWithFresh(baseline);
486 std::vector<o2::its::LayerTiming> timings(Count::value);
487 std::fill(timings.begin(), timings.end(),
o2::its::LayerTiming{.mNROFsTF = 3, .mROFLength = 40});
488 session.configureTiming(timings, [](
int) {
return true; });
489 for (
bool changeTiming : {
false,
true}) {
491 for (
auto& timing : timings) {
492 timing.mROFLength += 1;
495 BOOST_CHECK_THROW(session.configureTiming(timings, [](
int rof) {
497 throw std::runtime_error{
"filter failed"};
503 const auto storage = session.overlap.getView().mFlatTable;
504 session.configureTiming(timings, [](
int rof) {
return rof == 2; });
505 BOOST_CHECK(session.overlap.getView().mFlatTable == storage);
518 std::vector<o2::its::LayerTiming> timings(Count::value, {.mNROFsTF = 3, .mROFLength = 40});
519 const auto accept = [](int) {
return true; };
521 const auto cached = session.overlap.getView().mFlatTable;
522 for (
auto count : {0, Count::value - 1, Count::value + 1}) {
523 auto invalid = timings;
524 invalid.resize(
count, timings.front());
525 BOOST_CHECK_THROW(session.configureTiming(invalid, accept), std::runtime_error);
526 BOOST_CHECK(session.frame.getROFViews().overlap.mFlatTable == cached);
528 session.configureTiming(timings, accept);
529 BOOST_CHECK(session.overlap.getView().mFlatTable == cached);
header::DataOrigin origin
std::vector< std::shared_ptr< arrow::Field > > fields
std::vector< o2::itsmft::ROFRecord > trackROFs
std::vector< int > clusterIndices
std::vector< o2::its::TrackITS > tracks
static int initFieldFromGRP(const o2::parameters::GRPMagField *grp, bool verbose=false)
void setNThreads(int n, std::shared_ptr< tbb::task_arena > &arena)
bool initialize(TimeFrame &frame, const TrackerInitialization &configuration)
void configureTiming(gsl::span< const o2::its::LayerTiming > timings, AcceptROF &&accept)
GLsizei GLsizei GLchar * source
GLenum GLsizei GLsizei GLint * values
GLuint GLsizei GLsizei * length
GLsizei GLenum const void * indices
GLenum GLuint GLint GLint layer
GLuint GLsizei const GLchar * message
o2::itsmft::tracking::LayerTiming LayerTiming
ClusterData< T > extractClusterData(const CompClusterExt &c, gsl::span< const unsigned char >::iterator &patterns, const TopologyDictionary *dict)
void copyTrackingOutputColumns(Allocator &outputs, Output rofs, Output tracks, Output indices, const Staged &staged)
void validateSourceROFTiming(const ClusterSourceInput &source, const o2::InteractionRecord &origin, const o2::its::LayerTiming &timing)
constexpr std::array< SurfaceDescriptor, MFTNLayers > kMFTSurfaces
void loadTimeFrameSources(TimeFrame &, gsl::span< const ClusterSourceInput >, SurfaceCatalogView, std::vector< std::vector< uint32_t > > *externalIndicesBySurface=nullptr, std::vector< std::vector< uint32_t > > *clusterSizesBySurface=nullptr)
constexpr int MFTNLayers
MFT CA half-disk layer count.
constexpr std::array< SurfaceDescriptor, ITSNLayers > kITSSurfaces
constexpr int ITSNLayers
ITS CA layer count.
CATrackerPublicationAction decideCATrackerPublicationAction(bool active, bool success) noexcept
constexpr auto make(F f) -> std::array< int, N >
BOOST_GLOBAL_FIXTURE(Fixture)
std::map< std::string, ID > expected
boost::mpl::list< std::integral_constant< int, ITSNLayers >, std::integral_constant< int, MFTNLayers > > LayerCounts
BOOST_AUTO_TEST_CASE_TEMPLATE(ValidEmptyInputCompletesBeforeCleanup, Count, LayerCounts)
BOOST_CHECK_NO_THROW(algorithm::merge(target, other))
std::vector< Cluster > clusters
BOOST_CHECK_EQUAL(triggersD.size(), triggers.size())