Skip to content

Commit baf4d6d

Browse files
committed
Added delta+zigzag+varint compression, added drawCMV.C macro for visualization
1 parent 37e12af commit baf4d6d

File tree

5 files changed

+299
-41
lines changed

5 files changed

+299
-41
lines changed

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

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818

1919
#include <string>
2020
#include <memory>
21-
#include <stdexcept>
22-
#include <fmt/format.h>
21+
#include <vector>
22+
#include <cstdint>
2323

2424
#include "TTree.h"
2525
#include "TPCBase/CRU.h"
@@ -28,6 +28,31 @@
2828
namespace o2::tpc
2929
{
3030

31+
struct CMVPerTF; // forward declaration
32+
33+
/// Delta+zigzag+varint compressed CMV data for one TF across all CRUs
34+
/// Produced by CMVPerTF::compress(), restored with decompress()
35+
/// Each TTree entry corresponds to one CMVPerTFCompressed object (one TF)
36+
struct CMVPerTFCompressed {
37+
uint32_t firstOrbit{0}; ///< First orbit of this TF (copied from CMVPerTF)
38+
uint16_t firstBC{0}; ///< First bunch crossing of this TF (copied from CMVPerTF)
39+
40+
/// Delta+zigzag+varint encoded CMV values
41+
/// Layout: CRU-major, time-minor; delta is reset to zero at each CRU boundary
42+
std::vector<uint8_t> mCompressedData;
43+
44+
/// Restore a CMVPerTF from this compressed object into *cmv (must not be null)
45+
void decompress(CMVPerTF* cmv) const;
46+
47+
private:
48+
static uint16_t signedToCmv(int32_t val); ///< Signed integer -> sign-magnitude uint16_t
49+
static int32_t zigzagDecode(uint32_t value); ///< Zigzag decode
50+
static uint32_t decodeVarint(const uint8_t*& data, const uint8_t* end); ///< Varint decode
51+
52+
public:
53+
ClassDefNV(CMVPerTFCompressed, 1)
54+
};
55+
3156
/// CMV data for one TF across all CRUs
3257
/// Raw 16-bit CMV values are stored in a flat C array indexed as [cru * NTimeBinsPerTF + timeBin]
3358
/// CRU::MaxCRU and cmv::NTimeBinsPerTF are compile-time constants, so no dynamic allocation is needed
@@ -40,33 +65,27 @@ struct CMVPerTF {
4065
uint16_t mDataPerTF[CRU::MaxCRU * cmv::NTimeBinsPerTF]{};
4166

4267
/// Return the raw 16-bit CMV value for a given CRU and timebin within this TF
43-
uint16_t getCMV(const int cru, const int timeBin) const
44-
{
45-
if (cru < 0 || cru >= static_cast<int>(CRU::MaxCRU)) {
46-
throw std::out_of_range(fmt::format("CMVPerTF::getCMV: cru {} out of range [0, {})", cru, static_cast<int>(CRU::MaxCRU)));
47-
}
48-
if (timeBin < 0 || static_cast<uint32_t>(timeBin) >= cmv::NTimeBinsPerTF) {
49-
throw std::out_of_range(fmt::format("CMVPerTF::getCMV: timeBin {} out of range [0, {})", timeBin, static_cast<int>(cmv::NTimeBinsPerTF)));
50-
}
51-
return mDataPerTF[cru * cmv::NTimeBinsPerTF + timeBin];
52-
}
68+
uint16_t getCMV(const int cru, const int timeBin) const;
5369

5470
/// Return the float CMV value for a given CRU and timebin within this TF
55-
float getCMVFloat(const int cru, const int timeBin) const
56-
{
57-
auto cmv = getCMV(cru, timeBin);
58-
const bool positive = (cmv >> 15) & 1; // bit 15: sign (1=positive, 0=negative)
59-
const float magnitude = (cmv & 0x7FFF) / 128.f; // lower 15 bits, shift right by 7 (divide by 2^7)
60-
return positive ? magnitude : -magnitude;
61-
}
71+
float getCMVFloat(const int cru, const int timeBin) const;
72+
73+
/// Compress this object into a CMVPerTFCompressed using delta+zigzag+varint encoding
74+
CMVPerTFCompressed compress() const;
6275

6376
/// Serialise into a TTree; each Fill() call appends one entry (one TF)
6477
std::unique_ptr<TTree> toTTree() const;
6578

6679
/// Write the TTree to a ROOT file
67-
void writeToFile(const std::string& filename, const std::unique_ptr<TTree>& tree) const;
80+
static void writeToFile(const std::string& filename, const std::unique_ptr<TTree>& tree);
81+
82+
private:
83+
static int32_t cmvToSigned(uint16_t raw); ///< Sign-magnitude uint16_t → signed integer
84+
static uint32_t zigzagEncode(int32_t value); ///< Zigzag encode
85+
static void encodeVarintInto(uint32_t value, std::vector<uint8_t>& out); ///< Varint encode
6886

69-
ClassDefNV(CMVPerTF, 8)
87+
public:
88+
ClassDefNV(CMVPerTF, 1)
7089
};
7190

7291
} // namespace o2::tpc
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
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+
#if !defined(__CLING__) || defined(__ROOTCLING__)
13+
#include <string>
14+
#include <string_view>
15+
#include <fmt/format.h>
16+
17+
#include "TFile.h"
18+
#include "TParameter.h"
19+
#include "TTree.h"
20+
#include "TH2F.h"
21+
#include "TCanvas.h"
22+
23+
#include "TPCCalibration/CMVContainer.h"
24+
#include "TPCBase/Utils.h"
25+
#endif
26+
27+
using namespace o2::tpc;
28+
29+
/// Draw CMV (Common Mode Values) vs timebin from a CCDB TTree file
30+
/// \param filename input ROOT file containing the ccdb_object TTree
31+
/// \param outDir output directory for saved plots; nothing is saved if empty
32+
/// \return array of canvases
33+
TObjArray* drawCMV(std::string_view filename, std::string_view outDir)
34+
{
35+
TObjArray* arrCanvases = new TObjArray;
36+
arrCanvases->SetName("CMV");
37+
38+
// open file
39+
TFile f(filename.data(), "READ");
40+
if (f.IsZombie()) {
41+
fmt::print("ERROR: cannot open '{}'\n", filename);
42+
return arrCanvases;
43+
}
44+
fmt::print("Opened file: {}\n", filename);
45+
46+
// get TTree
47+
TTree* tree = nullptr;
48+
f.GetObject("ccdb_object", tree);
49+
if (!tree) {
50+
fmt::print("ERROR: TTree 'ccdb_object' not found\n");
51+
return arrCanvases;
52+
}
53+
fmt::print("Tree 'ccdb_object' found, entries: {}\n", tree->GetEntries());
54+
55+
// read metadata
56+
long firstTF = -1, lastTF = -1;
57+
if (auto* userInfo = tree->GetUserInfo()) {
58+
for (int i = 0; i < userInfo->GetSize(); ++i) {
59+
if (auto* p = dynamic_cast<TParameter<long>*>(userInfo->At(i))) {
60+
if (std::string(p->GetName()) == "firstTF")
61+
firstTF = p->GetVal();
62+
if (std::string(p->GetName()) == "lastTF")
63+
lastTF = p->GetVal();
64+
}
65+
}
66+
}
67+
fmt::print("firstTF: {}, lastTF: {}\n", firstTF, lastTF);
68+
69+
const int nEntries = tree->GetEntries();
70+
if (nEntries == 0) {
71+
fmt::print("ERROR: no entries in tree\n");
72+
return arrCanvases;
73+
}
74+
75+
constexpr int nCRUs = CRU::MaxCRU;
76+
constexpr int nTimeBins = cmv::NTimeBinsPerTF;
77+
78+
TH2F* h2d = new TH2F("hCMVvsTimeBin", ";Timebin (200 ns);Common Mode Values (ADC)",
79+
nTimeBins / 16, 0, nTimeBins,
80+
110, -100, 10);
81+
h2d->SetStats(0);
82+
83+
// branch setup
84+
o2::tpc::CMVPerTFCompressed* tfEntry = nullptr;
85+
tree->SetBranchAddress("CMVPerTFCompressed", &tfEntry);
86+
87+
// allocate once outside the loop to avoid repeated zero-initialisation of the large array
88+
auto* tf = new CMVPerTF();
89+
90+
long firstOrbit = -1;
91+
92+
for (int i = 0; i < nEntries; ++i) {
93+
tree->GetEntry(i);
94+
tfEntry->decompress(tf);
95+
96+
if (i == 0) {
97+
firstOrbit = tf->firstOrbit;
98+
}
99+
100+
for (int cru = 0; cru < nCRUs; ++cru) {
101+
for (int tb = 0; tb < nTimeBins; ++tb) {
102+
h2d->Fill(tb, tf->getCMVFloat(cru, tb));
103+
}
104+
}
105+
}
106+
107+
delete tf;
108+
tree->ResetBranchAddresses();
109+
delete tfEntry;
110+
111+
fmt::print("firstOrbit: {}\n", firstOrbit);
112+
113+
// draw
114+
auto* c = new TCanvas("cCMVvsTimeBin", "");
115+
c->SetLogz();
116+
h2d->Draw("colz");
117+
118+
arrCanvases->Add(c);
119+
120+
if (outDir.size()) {
121+
utils::saveCanvases(*arrCanvases, outDir, "png,pdf", "CMVCanvases.root");
122+
}
123+
124+
f.Close();
125+
return arrCanvases;
126+
}

Detectors/TPC/calibration/src/CMVContainer.cxx

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,125 @@
1313
/// @author Tuba Gündem, tuba.gundem@cern.ch
1414

1515
#include <stdexcept>
16+
#include <cstdint>
17+
#include <cmath>
1618
#include <fmt/format.h>
1719

1820
#include "TFile.h"
1921

2022
#include "TPCCalibration/CMVContainer.h"
23+
#include "TPCBase/CRU.h"
24+
#include "DataFormatsTPC/CMV.h"
2125

2226
namespace o2::tpc
2327
{
2428

29+
int32_t CMVPerTF::cmvToSigned(uint16_t raw)
30+
{
31+
const int32_t mag = raw & 0x7FFF;
32+
return (raw >> 15) ? mag : -mag;
33+
}
34+
35+
uint32_t CMVPerTF::zigzagEncode(int32_t value)
36+
{
37+
return (static_cast<uint32_t>(value) << 1) ^ static_cast<uint32_t>(value >> 31);
38+
}
39+
40+
void CMVPerTF::encodeVarintInto(uint32_t value, std::vector<uint8_t>& out)
41+
{
42+
while (value > 0x7F) {
43+
out.push_back(static_cast<uint8_t>((value & 0x7F) | 0x80));
44+
value >>= 7;
45+
}
46+
out.push_back(static_cast<uint8_t>(value));
47+
}
48+
49+
uint16_t CMVPerTFCompressed::signedToCmv(int32_t val)
50+
{
51+
const uint16_t mag = static_cast<uint16_t>(std::abs(val)) & 0x7FFF;
52+
return static_cast<uint16_t>((val >= 0 ? 0x8000u : 0u) | mag);
53+
}
54+
55+
int32_t CMVPerTFCompressed::zigzagDecode(uint32_t value)
56+
{
57+
return static_cast<int32_t>((value >> 1) ^ -(value & 1));
58+
}
59+
60+
uint32_t CMVPerTFCompressed::decodeVarint(const uint8_t*& data, const uint8_t* end)
61+
{
62+
uint32_t value = 0;
63+
int shift = 0;
64+
while (data < end && (*data & 0x80)) {
65+
value |= static_cast<uint32_t>(*data & 0x7F) << shift;
66+
shift += 7;
67+
++data;
68+
}
69+
if (data >= end) {
70+
throw std::runtime_error("CMVPerTFCompressed::decompress: unexpected end of compressed data");
71+
}
72+
value |= static_cast<uint32_t>(*data) << shift;
73+
++data;
74+
return value;
75+
}
76+
77+
uint16_t CMVPerTF::getCMV(const int cru, const int timeBin) const
78+
{
79+
if (cru < 0 || cru >= static_cast<int>(CRU::MaxCRU)) {
80+
throw std::out_of_range(fmt::format("CMVPerTF::getCMV: cru {} out of range [0, {})", cru, static_cast<int>(CRU::MaxCRU)));
81+
}
82+
if (timeBin < 0 || static_cast<uint32_t>(timeBin) >= cmv::NTimeBinsPerTF) {
83+
throw std::out_of_range(fmt::format("CMVPerTF::getCMV: timeBin {} out of range [0, {})", timeBin, static_cast<int>(cmv::NTimeBinsPerTF)));
84+
}
85+
return mDataPerTF[cru * cmv::NTimeBinsPerTF + timeBin];
86+
}
87+
88+
float CMVPerTF::getCMVFloat(const int cru, const int timeBin) const
89+
{
90+
const uint16_t raw = getCMV(cru, timeBin);
91+
const bool positive = (raw >> 15) & 1; // bit 15: sign (1=positive, 0=negative)
92+
const float magnitude = (raw & 0x7FFF) / 128.f; // lower 15 bits, shift right by 7 (divide by 2^7)
93+
return positive ? magnitude : -magnitude;
94+
}
95+
96+
CMVPerTFCompressed CMVPerTF::compress() const
97+
{
98+
CMVPerTFCompressed out;
99+
out.firstOrbit = firstOrbit;
100+
out.firstBC = firstBC;
101+
out.mCompressedData.reserve(static_cast<size_t>(CRU::MaxCRU) * cmv::NTimeBinsPerTF);
102+
103+
for (int cru = 0; cru < static_cast<int>(CRU::MaxCRU); ++cru) {
104+
int32_t prev = 0;
105+
for (uint32_t tb = 0; tb < cmv::NTimeBinsPerTF; ++tb) {
106+
const int32_t val = cmvToSigned(mDataPerTF[cru * cmv::NTimeBinsPerTF + tb]);
107+
const int32_t delta = val - prev;
108+
prev = val;
109+
encodeVarintInto(zigzagEncode(delta), out.mCompressedData);
110+
}
111+
}
112+
return out;
113+
}
114+
115+
void CMVPerTFCompressed::decompress(CMVPerTF* cmv) const
116+
{
117+
if (!cmv) {
118+
throw std::invalid_argument("CMVPerTFCompressed::decompress: cmv pointer is null");
119+
}
120+
cmv->firstOrbit = firstOrbit;
121+
cmv->firstBC = firstBC;
122+
const uint8_t* ptr = mCompressedData.data();
123+
const uint8_t* end = ptr + mCompressedData.size();
124+
125+
for (int cru = 0; cru < static_cast<int>(CRU::MaxCRU); ++cru) {
126+
int32_t prev = 0;
127+
for (uint32_t tb = 0; tb < cmv::NTimeBinsPerTF; ++tb) {
128+
const int32_t val = prev + zigzagDecode(decodeVarint(ptr, end));
129+
prev = val;
130+
cmv->mDataPerTF[cru * cmv::NTimeBinsPerTF + tb] = signedToCmv(val);
131+
}
132+
}
133+
}
134+
25135
std::unique_ptr<TTree> CMVPerTF::toTTree() const
26136
{
27137
auto tree = std::make_unique<TTree>("ccdb_object", "ccdb_object");
@@ -37,7 +147,7 @@ std::unique_ptr<TTree> CMVPerTF::toTTree() const
37147
return tree;
38148
}
39149

40-
void CMVPerTF::writeToFile(const std::string& filename, const std::unique_ptr<TTree>& tree) const
150+
void CMVPerTF::writeToFile(const std::string& filename, const std::unique_ptr<TTree>& tree)
41151
{
42152
TFile f(filename.c_str(), "RECREATE");
43153
if (f.IsZombie()) {

Detectors/TPC/calibration/src/TPCCalibrationLinkDef.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,5 +125,6 @@
125125
#pragma link C++ class o2::tpc::PressureTemperatureHelper + ;
126126

127127
#pragma link C++ class o2::tpc::CMVPerTF + ;
128+
#pragma link C++ class o2::tpc::CMVPerTFCompressed + ;
128129

129130
#endif

0 commit comments

Comments
 (0)