Skip to content

Commit f4cb578

Browse files
committed
ITSMFT/ITS3: centralize cluster digit IDs and test squashing labels
1 parent 714cf05 commit f4cb578

6 files changed

Lines changed: 184 additions & 25 deletions

File tree

‎Detectors/ITSMFT/common/reconstruction/CMakeLists.txt‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,8 @@ if (OpenMP_CXX_FOUND)
6868
target_link_libraries(${targetName} PRIVATE OpenMP::OpenMP_CXX)
6969
endif()
7070

71+
o2_add_test(ClustererMCLabels
72+
SOURCES test/testClustererMCLabels.cxx
73+
COMPONENT_NAME itsmft
74+
PUBLIC_LINK_LIBRARIES O2::ITSMFTReconstruction
75+
LABELS itsmft)

‎Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/PixelData.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,8 @@ class ChipPixelData
273273
std::vector<uint32_t>& getPixIds() { return mPixIds; }
274274
std::vector<int>& getPixelsOrder() { return mPixelsOrder; }
275275
uint32_t getOrderedPixId(int pos) const { return mPixIds[mPixelsOrder[pos]]; }
276+
// Squashing can remove or reorder digits; otherwise their indices are contiguous.
277+
uint32_t getDigitID(int pos) const { return mPixIds.empty() ? mStartID + pos : getOrderedPixId(pos); }
276278

277279
private:
278280
uint8_t mROFlags = 0; // readout flags from the chip trailer

‎Detectors/ITSMFT/common/reconstruction/src/Clusterer.cxx‎

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -235,11 +235,7 @@ void Clusterer::ClustererThread::finishChip(ChipPixelData* curChipData, CompClus
235235
pixArrBuff.push_back(pix); // needed for cluster topology
236236
bbox.adjust(pix.getRowDirect(), pix.getCol());
237237
if (labelsClusPtr) {
238-
if (parent->mSquashingDepth) { // the MCtruth for this pixel is stored in chip data: due to squashing we lose contiguity
239-
fetchMCLabels(curChipData->getOrderedPixId(pixEntry.second), labelsDigPtr, nlab);
240-
} else { // the MCtruth for this pixel is at curChipData->startID+pixEntry.second
241-
fetchMCLabels(pixEntry.second + curChipData->getStartID(), labelsDigPtr, nlab);
242-
}
238+
fetchMCLabels(curChipData->getDigitID(pixEntry.second), labelsDigPtr, nlab);
243239
}
244240
next = pixEntry.first;
245241
}
@@ -256,11 +252,7 @@ void Clusterer::ClustererThread::finishChip(ChipPixelData* curChipData, CompClus
256252
pixArrBuff.push_back(pix); // needed for cluster topology
257253
bbox.adjust(pix.getRowDirect(), pix.getCol());
258254
if (labelsClusPtr) {
259-
if (parent->mSquashingDepth) { // the MCtruth for this pixel is stored in chip data: due to squashing we lose contiguity
260-
fetchMCLabels(curChipData->getOrderedPixId(pixEntry.second), labelsDigPtr, nlab);
261-
} else { // the MCtruth for this pixel is at curChipData->startID+pixEntry.second
262-
fetchMCLabels(pixEntry.second + curChipData->getStartID(), labelsDigPtr, nlab);
263-
}
255+
fetchMCLabels(curChipData->getDigitID(pixEntry.second), labelsDigPtr, nlab);
264256
}
265257
next = pixEntry.first;
266258
}
@@ -315,10 +307,7 @@ void Clusterer::ClustererThread::finishChipSingleHitFast(uint32_t hit, ChipPixel
315307

316308
if (labelsClusPtr) { // MC labels were requested
317309
int nlab = 0;
318-
// Squashing can remove earlier digits on the chip, so the surviving
319-
// pixel's source index need not be contiguous with the chip's start.
320-
const auto digitID = parent->mSquashingDepth ? curChipData->getOrderedPixId(hit) : curChipData->getStartID() + hit;
321-
fetchMCLabels(digitID, labelsDigPtr, nlab);
310+
fetchMCLabels(curChipData->getDigitID(hit), labelsDigPtr, nlab);
322311
auto cnt = compClusPtr->size();
323312
for (int i = 0; i < nlab; i++) {
324313
labelsClusPtr->addElement(cnt, labelsBuff[i]);
@@ -440,6 +429,9 @@ void Clusterer::ClustererThread::updateChip(const ChipPixelData* curChipData, ui
440429
void Clusterer::ClustererThread::fetchMCLabels(int digID, const ConstMCTruth* labelsDig, int& nfilled)
441430
{
442431
// transfer MC labels to cluster
432+
if (!labelsDig) {
433+
return;
434+
}
443435
auto sortBuffer = [this]() { std::sort(this->labelsBuff.begin(), this->labelsBuff.end(), [](Label const& a, Label const& b) { return a.getTrackID() < b.getTrackID(); }); };
444436
for (const auto& l : labelsDig->getLabels(digID)) {
445437
bool skip = false;
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2+
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3+
// All rights not expressly granted are reserved.
4+
//
5+
// This software is distributed under the terms of the GNU General Public
6+
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7+
//
8+
// In applying this license CERN does not waive the privileges and immunities
9+
// granted to it by virtue of its status as an Intergovernmental Organization
10+
// or submit itself to any jurisdiction.
11+
12+
#define BOOST_TEST_MODULE Test Clusterer MC labels
13+
#define BOOST_TEST_MAIN
14+
#define BOOST_TEST_DYN_LINK
15+
#include <boost/test/unit_test.hpp>
16+
#include <algorithm>
17+
18+
#include "ITSMFTReconstruction/DigitPixelReader.h"
19+
#include "SimulationDataFormat/MCTruthContainer.h"
20+
#ifdef TEST_ITS3_CLUSTERER
21+
#include "ITS3Reconstruction/Clusterer.h"
22+
using Clusterer = o2::its3::Clusterer;
23+
#else
24+
#include "ITSMFTReconstruction/Clusterer.h"
25+
using Clusterer = o2::itsmft::Clusterer;
26+
#endif
27+
28+
namespace
29+
{
30+
using namespace o2::itsmft;
31+
using MCTruth = o2::dataformats::MCTruthContainer<o2::MCCompLabel>;
32+
using CompClusCont = std::vector<CompClusterExt>;
33+
using PatternCont = std::vector<unsigned char>;
34+
using ROFRecCont = std::vector<ROFRecord>;
35+
36+
struct ClusterResult {
37+
CompClusCont clusters;
38+
ROFRecCont rofs;
39+
MCTruth labels;
40+
};
41+
42+
ClusterResult clusterize(const std::vector<Digit>& digits, const ROFRecCont& rofs, int readerDepth, int clustererDepth, bool withTruth = true)
43+
{
44+
MCTruth truth;
45+
for (size_t i = 0; i < digits.size(); ++i) {
46+
truth.addElement(i, o2::MCCompLabel(i, 0, 0));
47+
}
48+
o2::dataformats::ConstMCTruthContainer<o2::MCCompLabel> flatTruth;
49+
truth.flatten_to(flatTruth);
50+
o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel> truthView(flatTruth);
51+
52+
DigitPixelReader reader;
53+
reader.setSquashingDepth(readerDepth);
54+
reader.setSquashingDist(1);
55+
reader.setMaxBCSeparationToSquash(10);
56+
reader.setDigits(digits);
57+
reader.setROFRecords(rofs);
58+
reader.setDigitsMCTruth(withTruth ? &truthView : nullptr);
59+
reader.setDecodeNextAuto(true);
60+
61+
Clusterer clusterer;
62+
clusterer.setNChips(1);
63+
clusterer.setMaxBCSeparationToMask(0);
64+
clusterer.setMaxROFDepthToSquash(clustererDepth);
65+
ClusterResult result;
66+
PatternCont patterns;
67+
clusterer.process(1, reader, &result.clusters, &patterns, &result.rofs, &result.labels);
68+
return result;
69+
}
70+
71+
void checkLabels(const ClusterResult& result, size_t cluster, const std::vector<int>& expectedTracks)
72+
{
73+
const auto labels = result.labels.getLabels(cluster);
74+
BOOST_REQUIRE_EQUAL(labels.size(), expectedTracks.size());
75+
std::vector<int> tracks;
76+
for (const auto& label : labels) {
77+
tracks.push_back(label.getTrackID());
78+
BOOST_CHECK_EQUAL(label.getEventID(), 0);
79+
BOOST_CHECK_EQUAL(label.getSourceID(), 0);
80+
}
81+
std::sort(tracks.begin(), tracks.end());
82+
BOOST_CHECK_EQUAL_COLLECTIONS(tracks.begin(), tracks.end(), expectedTracks.begin(), expectedTracks.end());
83+
}
84+
} // namespace
85+
86+
BOOST_AUTO_TEST_CASE(SinglePixelSurvivingSquashing)
87+
{
88+
// The first digit in ROF 1 repeats ROF 0 and is squashed away. The survivor
89+
// must retain digit 2's label rather than the label at the chip's start ID (1).
90+
const std::vector<Digit> digits{{0, 10, 10}, {0, 10, 10}, {0, 30, 30}};
91+
const ROFRecCont rofs{{{0, 1}, 0, 0, 1}, {{1, 1}, 1, 1, 2}};
92+
for (int depth : {0, 1}) {
93+
const auto result = clusterize(digits, rofs, 1, depth);
94+
BOOST_REQUIRE_EQUAL(result.clusters.size(), 2);
95+
BOOST_REQUIRE_EQUAL(result.rofs.size(), 2);
96+
BOOST_CHECK_EQUAL(result.rofs[1].getNEntries(), 1);
97+
BOOST_CHECK_EQUAL(result.clusters[1].getRow(), 30);
98+
BOOST_CHECK_EQUAL(result.clusters[1].getCol(), 30);
99+
checkLabels(result, 0, {0});
100+
checkLabels(result, 1, {2});
101+
}
102+
}
103+
104+
BOOST_AUTO_TEST_CASE(MultiPixelLabelsAfterSquashingAndSorting)
105+
{
106+
// Digit 1 is merged into ROF 0 and sorted before digit 0; digit 2 repeats
107+
// digit 0. The remaining two-pixel cluster must use digits 3 and 4.
108+
const std::vector<Digit> digits{{0, 10, 10}, {0, 10, 9}, {0, 10, 10}, {0, 30, 30}, {0, 31, 30}};
109+
const ROFRecCont rofs{{{0, 1}, 0, 0, 1}, {{1, 1}, 1, 1, 4}};
110+
for (int depth : {0, 1}) {
111+
const auto result = clusterize(digits, rofs, 1, depth);
112+
BOOST_REQUIRE_EQUAL(result.clusters.size(), 2);
113+
checkLabels(result, 0, {0, 1});
114+
checkLabels(result, 1, {3, 4});
115+
}
116+
}
117+
118+
BOOST_AUTO_TEST_CASE(LabelsFollowPixelSortingBetweenClusters)
119+
{
120+
// Overflow digits are appended to the chip, then sorted into two different
121+
// clusters. Check each cluster's labels to detect a lost sorting permutation.
122+
const std::vector<Digit> digits{{0, 10, 10}, {0, 30, 30}, {0, 10, 9}, {0, 30, 29}, {0, 50, 50}};
123+
const ROFRecCont rofs{{{0, 1}, 0, 0, 2}, {{1, 1}, 1, 2, 3}};
124+
const auto result = clusterize(digits, rofs, 1, 1);
125+
BOOST_REQUIRE_EQUAL(result.clusters.size(), 3);
126+
checkLabels(result, 0, {0, 2});
127+
checkLabels(result, 1, {1, 3});
128+
checkLabels(result, 2, {4});
129+
}
130+
131+
BOOST_AUTO_TEST_CASE(ContiguousDigitsWithClustererSquashingEnabled)
132+
{
133+
const std::vector<Digit> digits{{0, 10, 10}, {0, 30, 30}, {0, 31, 30}};
134+
const ROFRecCont rofs{{{0, 1}, 0, 0, 1}, {{1, 1}, 1, 1, 2}};
135+
for (int depth : {0, 1}) {
136+
const auto result = clusterize(digits, rofs, 0, depth);
137+
BOOST_REQUIRE_EQUAL(result.clusters.size(), 2);
138+
checkLabels(result, 0, {0});
139+
checkLabels(result, 1, {1, 2});
140+
}
141+
}
142+
143+
BOOST_AUTO_TEST_CASE(ClusterLabelsRequestedWithoutDigitTruth)
144+
{
145+
const std::vector<Digit> digits{{0, 10, 10}, {0, 30, 30}, {0, 31, 30}};
146+
const ROFRecCont rofs{{{0, 1}, 0, 0, 1}, {{1, 1}, 1, 1, 2}};
147+
for (int readerDepth : {0, 1}) {
148+
for (int clustererDepth : {0, 1}) {
149+
const auto result = clusterize(digits, rofs, readerDepth, clustererDepth, false);
150+
BOOST_CHECK_EQUAL(result.clusters.size(), 2);
151+
BOOST_CHECK_EQUAL(result.labels.getNElements(), 0);
152+
}
153+
}
154+
}

‎Detectors/Upgrades/ITS3/reconstruction/CMakeLists.txt‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,14 @@ if (OpenMP_CXX_FOUND)
5353
target_compile_definitions(${targetName} PRIVATE WITH_OPENMP)
5454
target_link_libraries(${targetName} PRIVATE OpenMP::OpenMP_CXX)
5555
endif()
56+
57+
o2_add_test(ClustererMCLabels
58+
SOURCES ${CMAKE_SOURCE_DIR}/Detectors/ITSMFT/common/reconstruction/test/testClustererMCLabels.cxx
59+
COMPONENT_NAME its3
60+
PUBLIC_LINK_LIBRARIES O2::ITS3Reconstruction
61+
TARGETVARNAME clustererMCLabelsTest
62+
NAME its3-clusterer-mc-labels
63+
LABELS its3)
64+
if (clustererMCLabelsTest)
65+
target_compile_definitions(${clustererMCLabelsTest} PRIVATE TEST_ITS3_CLUSTERER)
66+
endif()

‎Detectors/Upgrades/ITS3/reconstruction/src/Clusterer.cxx‎

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -234,11 +234,7 @@ void Clusterer::ClustererThread::finishChip(ChipPixelData* curChipData, CompClus
234234
pixArrBuff.push_back(pix); // needed for cluster topology
235235
bbox.adjust(pix.getRowDirect(), pix.getCol());
236236
if (labelsClusPtr) {
237-
if (parent->mSquashingDepth) { // the MCtruth for this pixel is stored in chip data: due to squashing we lose contiguity
238-
fetchMCLabels(curChipData->getOrderedPixId(pixEntry.second), labelsDigPtr, nlab);
239-
} else { // the MCtruth for this pixel is at curChipData->startID+pixEntry.second
240-
fetchMCLabels(pixEntry.second + curChipData->getStartID(), labelsDigPtr, nlab);
241-
}
237+
fetchMCLabels(curChipData->getDigitID(pixEntry.second), labelsDigPtr, nlab);
242238
}
243239
next = pixEntry.first;
244240
}
@@ -255,11 +251,7 @@ void Clusterer::ClustererThread::finishChip(ChipPixelData* curChipData, CompClus
255251
pixArrBuff.push_back(pix); // needed for cluster topology
256252
bbox.adjust(pix.getRowDirect(), pix.getCol());
257253
if (labelsClusPtr) {
258-
if (parent->mSquashingDepth) { // the MCtruth for this pixel is stored in chip data: due to squashing we lose contiguity
259-
fetchMCLabels(curChipData->getOrderedPixId(pixEntry.second), labelsDigPtr, nlab);
260-
} else { // the MCtruth for this pixel is at curChipData->startID+pixEntry.second
261-
fetchMCLabels(pixEntry.second + curChipData->getStartID(), labelsDigPtr, nlab);
262-
}
254+
fetchMCLabels(curChipData->getDigitID(pixEntry.second), labelsDigPtr, nlab);
263255
}
264256
next = pixEntry.first;
265257
}
@@ -316,7 +308,7 @@ void Clusterer::ClustererThread::finishChipSingleHitFast(uint32_t hit, ChipPixel
316308

317309
if (labelsClusPtr) { // MC labels were requested
318310
int nlab = 0;
319-
fetchMCLabels(curChipData->getStartID() + hit, labelsDigPtr, nlab);
311+
fetchMCLabels(curChipData->getDigitID(hit), labelsDigPtr, nlab);
320312
auto cnt = compClusPtr->size();
321313
for (int i = 0; i < nlab; i++) {
322314
labelsClusPtr->addElement(cnt, labelsBuff[i]);
@@ -454,6 +446,9 @@ void Clusterer::ClustererThread::updateChip(const ChipPixelData* curChipData, ui
454446
void Clusterer::ClustererThread::fetchMCLabels(int digID, const ConstMCTruth* labelsDig, int& nfilled)
455447
{
456448
// transfer MC labels to cluster
449+
if (!labelsDig) {
450+
return;
451+
}
457452
auto sortBuffer = [this]() { std::sort(this->labelsBuff.begin(), this->labelsBuff.end(), [](Label const& a, Label const& b) { return a.getTrackID() < b.getTrackID(); }); };
458453
for (const auto& label : labelsDig->getLabels(digID)) {
459454
bool skip = false;

0 commit comments

Comments
 (0)