Skip to content

Commit 9fc3e05

Browse files
committed
Added sparse and HUffman encoding to the CMVContainer, updatet the drawCMV.C macro and TPCDistributeCMVSpec.h accordingly
1 parent 3c207a6 commit 9fc3e05

File tree

5 files changed

+545
-31
lines changed

5 files changed

+545
-31
lines changed

Detectors/TPC/calibration/include/TPCCalibration/CMVContainer.h

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@
2828
namespace o2::tpc
2929
{
3030

31-
struct CMVPerTF; // forward declaration
31+
struct CMVPerTF; // forward declaration
32+
struct CMVPerTFSparse; // forward declaration
33+
struct CMVPerTFHuffman; // forward declaration
3234

3335
/// Delta+zigzag+varint compressed CMV data for one TF across all CRUs
3436
/// Produced by CMVPerTF::compress(), restored with decompress()
@@ -53,6 +55,56 @@ struct CMVPerTFCompressed {
5355
ClassDefNV(CMVPerTFCompressed, 1)
5456
};
5557

58+
/// Sparse-encoded CMV data for one TF across all CRUs
59+
/// Produced by CMVPerTF::compressSparse(), restored with decompress()
60+
/// Each TTree entry corresponds to one CMVPerTFSparse object (one TF)
61+
///
62+
/// Encoding format (stored in mSparseData):
63+
/// For each CRU 0..MaxCRU-1:
64+
/// varint(N) — number of non-zero timebins in this CRU
65+
/// For each of the N entries (in timebin order):
66+
/// varint(delta) — absolute timeBin for the first entry; tb − prev_tb for subsequent ones
67+
/// uint16_t(raw) — raw sign-magnitude CMV value (little-endian, 2 bytes)
68+
struct CMVPerTFSparse {
69+
uint32_t firstOrbit{0}; ///< First orbit of this TF (copied from CMVPerTF)
70+
uint16_t firstBC{0}; ///< First bunch crossing of this TF (copied from CMVPerTF)
71+
72+
/// Sparse-encoded CMV values
73+
std::vector<uint8_t> mSparseData;
74+
75+
/// Restore a CMVPerTF from this sparse object into *cmv (must not be null)
76+
void decompress(CMVPerTF* cmv) const;
77+
78+
private:
79+
static uint32_t decodeVarint(const uint8_t*& data, const uint8_t* end); ///< Varint decode
80+
81+
public:
82+
ClassDefNV(CMVPerTFSparse, 1)
83+
};
84+
85+
/// Delta+zigzag+canonical Huffman compressed CMV data for one TF across all CRUs
86+
/// Produced by CMVPerTF::compressHuffman(), restored with decompress()
87+
///
88+
/// Serialisation layout (mHuffmanData):
89+
/// 4 bytes LE uint32_t : numSymbols — number of distinct zigzag-encoded delta symbols
90+
/// numSymbols * 5 bytes : symbol table (canonical order, sorted by codeLen ASC then symbol ASC):
91+
/// 4 bytes LE uint32_t : zigzag-encoded symbol value
92+
/// 1 byte : canonical code length (1..32)
93+
/// 8 bytes LE uint64_t : totalBits — number of valid bits in the bitstream
94+
/// ceil(totalBits/8) bytes : bitstream, MSB-first packing within each byte
95+
struct CMVPerTFHuffman {
96+
uint32_t firstOrbit{0}; ///< First orbit of this TF (copied from CMVPerTF)
97+
uint16_t firstBC{0}; ///< First bunch crossing of this TF (copied from CMVPerTF)
98+
99+
/// Huffman-coded payload
100+
std::vector<uint8_t> mHuffmanData;
101+
102+
/// Restore a CMVPerTF from this Huffman object into *cmv (must not be null)
103+
void decompress(CMVPerTF* cmv) const;
104+
105+
ClassDefNV(CMVPerTFHuffman, 1)
106+
};
107+
56108
/// CMV data for one TF across all CRUs
57109
/// Raw 16-bit CMV values are stored in a flat C array indexed as [cru * NTimeBinsPerTF + timeBin]
58110
/// CRU::MaxCRU and cmv::NTimeBinsPerTF are compile-time constants, so no dynamic allocation is needed
@@ -69,13 +121,30 @@ struct CMVPerTF {
69121
/// Return the float CMV value for a given CRU and timebin within this TF
70122
float getCMVFloat(const int cru, const int timeBin) const;
71123

72-
/// Zero out raw CMV values whose float magnitude is below threshold (default 1.0 ADC)
124+
/// Zero out raw CMV values whose float magnitude is below threshold
73125
/// This converts the sign-magnitude raw value to 0x0000 for all entries with |float value| < threshold
74126
void zeroSmallValues(float threshold = 1.0f);
75127

128+
/// Apply dynamic precision reduction: round values to the nearest integer ADC for all values whose rounded magnitude is <= steps
129+
/// Example: steps=3
130+
/// |v| < 0.5 ADC -> 0
131+
/// |v| in [0.5, 1.5) ADC -> 1.0 ADC
132+
/// |v| in [1.5, 2.5) ADC -> 2.0 ADC
133+
/// |v| in [2.5, 3.5) ADC -> 3.0 ADC
134+
/// |v| >= 3.5 ADC -> unchanged (full precision)
135+
///
136+
/// steps=0 dynamic precision is not applied
137+
void applyDynamicPrecision(uint16_t steps);
138+
76139
/// Compress this object into a CMVPerTFCompressed using delta+zigzag+varint encoding
77140
CMVPerTFCompressed compress() const;
78141

142+
/// Compress this object into a CMVPerTFSparse storing only non-zero timebins
143+
CMVPerTFSparse compressSparse() const;
144+
145+
/// Compress this object using delta+zigzag+canonical-Huffman encoding
146+
CMVPerTFHuffman compressHuffman() const;
147+
79148
/// Serialise into a TTree; each Fill() call appends one entry (one TF)
80149
std::unique_ptr<TTree> toTTree() const;
81150

Detectors/TPC/calibration/macro/drawCMV.C

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -80,22 +80,31 @@ TObjArray* drawCMV(std::string_view filename, std::string_view outDir)
8080
110, -100.5, 9.5);
8181
h2d->SetStats(0);
8282

83-
// auto-detect branch format: compressed (delta+zigzag+varint) or raw CMVPerTF
83+
// auto-detect branch format: sparse, huffman, compressed (delta+zigzag+varint), or raw CMVPerTF
84+
const bool isSparse = (tree->GetBranch("CMVPerTFSparse") != nullptr);
85+
const bool isHuffman = (tree->GetBranch("CMVPerTFHuffman") != nullptr);
8486
const bool isCompressed = (tree->GetBranch("CMVPerTFCompressed") != nullptr);
8587
const bool isRaw = (tree->GetBranch("CMVPerTF") != nullptr);
86-
if (!isCompressed && !isRaw) {
87-
fmt::print("ERROR: neither 'CMVPerTFCompressed' nor 'CMVPerTF' branch found\n");
88+
if (!isSparse && !isHuffman && !isCompressed && !isRaw) {
89+
fmt::print("ERROR: no recognised branch found (expected CMVPerTFSparse, CMVPerTFHuffman, CMVPerTFCompressed, or CMVPerTF)\n");
8890
return arrCanvases;
8991
}
90-
fmt::print("Branch format: {}\n", isCompressed ? "CMVPerTFCompressed (delta+zigzag+varint)" : "CMVPerTF (raw)");
92+
const std::string branchFormat = isSparse ? "CMVPerTFSparse (sparse)" : (isHuffman ? "CMVPerTFHuffman (delta+zigzag+Huffman)" : (isCompressed ? "CMVPerTFCompressed (delta+zigzag+varint)" : "CMVPerTF (raw)"));
93+
fmt::print("Branch format: {}\n", branchFormat);
9194

92-
// branch setup
95+
// branch setup — only one pointer is active depending on the detected format
96+
o2::tpc::CMVPerTFSparse* tfSparse = nullptr;
97+
o2::tpc::CMVPerTFHuffman* tfHuffman = nullptr;
9398
o2::tpc::CMVPerTFCompressed* tfCompressed = nullptr;
9499
o2::tpc::CMVPerTF* tfRaw = nullptr;
95-
// staging object for decompression (only used in compressed path)
96-
CMVPerTF* tfDecoded = isCompressed ? new CMVPerTF() : nullptr;
97-
98-
if (isCompressed) {
100+
// staging object for decompression
101+
CMVPerTF* tfDecoded = (isSparse || isHuffman || isCompressed) ? new CMVPerTF() : nullptr;
102+
103+
if (isSparse) {
104+
tree->SetBranchAddress("CMVPerTFSparse", &tfSparse);
105+
} else if (isHuffman) {
106+
tree->SetBranchAddress("CMVPerTFHuffman", &tfHuffman);
107+
} else if (isCompressed) {
99108
tree->SetBranchAddress("CMVPerTFCompressed", &tfCompressed);
100109
} else {
101110
tree->SetBranchAddress("CMVPerTF", &tfRaw);
@@ -108,7 +117,13 @@ TObjArray* drawCMV(std::string_view filename, std::string_view outDir)
108117

109118
// resolve to a unified pointer regardless of storage format
110119
const CMVPerTF* tf = nullptr;
111-
if (isCompressed) {
120+
if (isSparse) {
121+
tfSparse->decompress(tfDecoded);
122+
tf = tfDecoded;
123+
} else if (isHuffman) {
124+
tfHuffman->decompress(tfDecoded);
125+
tf = tfDecoded;
126+
} else if (isCompressed) {
112127
tfCompressed->decompress(tfDecoded);
113128
tf = tfDecoded;
114129
} else {
@@ -128,6 +143,8 @@ TObjArray* drawCMV(std::string_view filename, std::string_view outDir)
128143

129144
delete tfDecoded;
130145
tree->ResetBranchAddresses();
146+
delete tfSparse;
147+
delete tfHuffman;
131148
delete tfCompressed;
132149

133150
fmt::print("firstOrbit: {}\n", firstOrbit);

0 commit comments

Comments
 (0)