Skip to content

Commit 3c207a6

Browse files
committed
Added small value zeroing, added --use-compression and --cmv-zero-threshold flags to TPCDistributeCMVSpec, updated drawCMV.C macro to auto detect branch format (compressed or not)
1 parent baf4d6d commit 3c207a6

File tree

6 files changed

+84
-19
lines changed

6 files changed

+84
-19
lines changed

Detectors/TPC/calibration/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ o2_add_test_root_macro(macro/prepareITFiles.C
155155
COMPILE_ONLY
156156
PUBLIC_LINK_LIBRARIES O2::TPCCalibration
157157
LABELS tpc)
158+
o2_add_test_root_macro(macro/drawCMV.C
159+
COMPILE_ONLY
160+
PUBLIC_LINK_LIBRARIES O2::TPCCalibration O2::TPCBase
161+
LABELS tpc)
158162

159163
o2_add_test(IDCFourierTransform
160164
COMPONENT_NAME calibration

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ struct CMVPerTFCompressed {
5656
/// CMV data for one TF across all CRUs
5757
/// Raw 16-bit CMV values are stored in a flat C array indexed as [cru * NTimeBinsPerTF + timeBin]
5858
/// CRU::MaxCRU and cmv::NTimeBinsPerTF are compile-time constants, so no dynamic allocation is needed
59-
/// Each TTree entry corresponds to one CMVPerTF object (one TF)
6059
struct CMVPerTF {
6160
uint32_t firstOrbit{0}; ///< First orbit of this TF, from heartbeatOrbit of the first CMV packet
6261
uint16_t firstBC{0}; ///< First bunch crossing of this TF, from heartbeatBC of the first CMV packet
@@ -70,6 +69,10 @@ struct CMVPerTF {
7069
/// Return the float CMV value for a given CRU and timebin within this TF
7170
float getCMVFloat(const int cru, const int timeBin) const;
7271

72+
/// Zero out raw CMV values whose float magnitude is below threshold (default 1.0 ADC)
73+
/// This converts the sign-magnitude raw value to 0x0000 for all entries with |float value| < threshold
74+
void zeroSmallValues(float threshold = 1.0f);
75+
7376
/// Compress this object into a CMVPerTFCompressed using delta+zigzag+varint encoding
7477
CMVPerTFCompressed compress() const;
7578

Detectors/TPC/calibration/macro/drawCMV.C

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -76,22 +76,44 @@ TObjArray* drawCMV(std::string_view filename, std::string_view outDir)
7676
constexpr int nTimeBins = cmv::NTimeBinsPerTF;
7777

7878
TH2F* h2d = new TH2F("hCMVvsTimeBin", ";Timebin (200 ns);Common Mode Values (ADC)",
79-
nTimeBins / 16, 0, nTimeBins,
80-
110, -100, 10);
79+
100, 0, nTimeBins,
80+
110, -100.5, 9.5);
8181
h2d->SetStats(0);
8282

83-
// branch setup
84-
o2::tpc::CMVPerTFCompressed* tfEntry = nullptr;
85-
tree->SetBranchAddress("CMVPerTFCompressed", &tfEntry);
83+
// auto-detect branch format: compressed (delta+zigzag+varint) or raw CMVPerTF
84+
const bool isCompressed = (tree->GetBranch("CMVPerTFCompressed") != nullptr);
85+
const bool isRaw = (tree->GetBranch("CMVPerTF") != nullptr);
86+
if (!isCompressed && !isRaw) {
87+
fmt::print("ERROR: neither 'CMVPerTFCompressed' nor 'CMVPerTF' branch found\n");
88+
return arrCanvases;
89+
}
90+
fmt::print("Branch format: {}\n", isCompressed ? "CMVPerTFCompressed (delta+zigzag+varint)" : "CMVPerTF (raw)");
8691

87-
// allocate once outside the loop to avoid repeated zero-initialisation of the large array
88-
auto* tf = new CMVPerTF();
92+
// branch setup
93+
o2::tpc::CMVPerTFCompressed* tfCompressed = nullptr;
94+
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) {
99+
tree->SetBranchAddress("CMVPerTFCompressed", &tfCompressed);
100+
} else {
101+
tree->SetBranchAddress("CMVPerTF", &tfRaw);
102+
}
89103

90104
long firstOrbit = -1;
91105

92106
for (int i = 0; i < nEntries; ++i) {
93107
tree->GetEntry(i);
94-
tfEntry->decompress(tf);
108+
109+
// resolve to a unified pointer regardless of storage format
110+
const CMVPerTF* tf = nullptr;
111+
if (isCompressed) {
112+
tfCompressed->decompress(tfDecoded);
113+
tf = tfDecoded;
114+
} else {
115+
tf = tfRaw;
116+
}
95117

96118
if (i == 0) {
97119
firstOrbit = tf->firstOrbit;
@@ -104,9 +126,9 @@ TObjArray* drawCMV(std::string_view filename, std::string_view outDir)
104126
}
105127
}
106128

107-
delete tf;
129+
delete tfDecoded;
108130
tree->ResetBranchAddresses();
109-
delete tfEntry;
131+
delete tfCompressed;
110132

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

Detectors/TPC/calibration/src/CMVContainer.cxx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,19 @@ float CMVPerTF::getCMVFloat(const int cru, const int timeBin) const
9393
return positive ? magnitude : -magnitude;
9494
}
9595

96+
void CMVPerTF::zeroSmallValues(float threshold)
97+
{
98+
if (threshold <= 0.f) {
99+
return;
100+
}
101+
for (uint32_t i = 0; i < static_cast<uint32_t>(CRU::MaxCRU) * cmv::NTimeBinsPerTF; ++i) {
102+
const float mag = (mDataPerTF[i] & 0x7FFF) / 128.f;
103+
if (mag < threshold) {
104+
mDataPerTF[i] = 0;
105+
}
106+
}
107+
}
108+
96109
CMVPerTFCompressed CMVPerTF::compress() const
97110
{
98111
CMVPerTFCompressed out;

Detectors/TPC/workflow/include/TPCWorkflow/TPCDistributeCMVSpec.h

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ class TPCDistributeCMVSpec : public o2::framework::Task
9898
mNTFsDataDrop = mCheckEveryNData;
9999
}
100100
mDumpCMVs = ic.options().get<bool>("dump-cmvs");
101+
mUseCompression = ic.options().get<bool>("use-compression");
102+
mZeroThreshold = ic.options().get<float>("cmv-zero-threshold");
103+
// re-initialise the interval tree now that mUseCompression is known (constructor used the default)
104+
initIntervalTree();
101105
}
102106

103107
void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final
@@ -242,8 +246,18 @@ class TPCDistributeCMVSpec : public o2::framework::Task
242246

243247
if (mProcessedCRU[currentBuffer][relTF] == mCRUs.size()) {
244248
++mProcessedTFs[currentBuffer];
245-
// compress the completed TF and fill one TTree entry, then reset the staging object
246-
mCurrentCompressedTF = mCurrentTF.compress();
249+
// zero out small values before storing (helps ROOT zlib for raw path; benign for compressed path)
250+
if (mIntervalTFCount == 0) {
251+
if (mZeroThreshold > 0.f) {
252+
LOGP(info, "Zeroing CMV values with |value| < {:.3f} ADC counts before storing", mZeroThreshold);
253+
} else {
254+
LOGP(info, "cmv-zero-threshold <= 0, skipping zeroing of small CMV values");
255+
}
256+
}
257+
mCurrentTF.zeroSmallValues(mZeroThreshold);
258+
if (mUseCompression) {
259+
mCurrentCompressedTF = mCurrentTF.compress();
260+
}
247261
mIntervalTree->Fill();
248262
++mIntervalTFCount;
249263
mCurrentTF = CMVPerTF{};
@@ -300,9 +314,11 @@ class TPCDistributeCMVSpec : public o2::framework::Task
300314
std::vector<bool> mSendCCDBOutputOrbitReset{}; ///< flag for received orbit reset time from CCDB
301315
std::vector<bool> mSendCCDBOutputGRPECS{}; ///< flag for received orbit GRPECS from CCDB
302316
bool mBuffer{false}; ///< buffer index
303-
const bool mSendCCDB{false}; ///< send output to CCDB populator
304-
const bool mUsePreciseTimestamp{false}; ///< use precise timestamp from orbit-reset info
317+
bool mSendCCDB{false}; ///< send output to CCDB populator
318+
bool mUsePreciseTimestamp{false}; ///< use precise timestamp from orbit-reset info
305319
bool mDumpCMVs{false}; ///< write a local ROOT debug file
320+
bool mUseCompression{false}; ///< use delta+zigzag+varint compression when storing in TTree; if false, store raw CMVPerTF
321+
float mZeroThreshold{1.0f}; ///< zero out CMV values with |float value| < threshold before storing
306322
long mTimestampStart{0}; ///< CCDB validity start timestamp
307323
dataformats::Pair<long, int> mTFInfo{}; ///< orbit-reset time and NHBFPerTF for precise timestamp
308324
std::unique_ptr<TTree> mIntervalTree{}; ///< TTree accumulating one CMVPerTFCompressed entry per completed TF in the current interval
@@ -323,13 +339,18 @@ class TPCDistributeCMVSpec : public o2::framework::Task
323339
/// Returns real number of TFs taking buffer size into account
324340
unsigned int getNRealTFs() const { return mNTFsBuffer * mTimeFrames; }
325341

326-
/// Create a fresh in-memory TTree for the next aggregation interval, with mCurrentCompressedTF as the fill source
342+
/// Create a fresh in-memory TTree for the next aggregation interval.
343+
/// Branches on CMVPerTFCompressed (delta+zigzag+varint) or CMVPerTF (raw) depending on mUseCompression.
327344
void initIntervalTree()
328345
{
329346
mIntervalTree = std::make_unique<TTree>("ccdb_object", "ccdb_object");
330347
mIntervalTree->SetAutoSave(0);
331348
mIntervalTree->SetDirectory(nullptr);
332-
mIntervalTree->Branch("CMVPerTFCompressed", &mCurrentCompressedTF);
349+
if (mUseCompression) {
350+
mIntervalTree->Branch("CMVPerTFCompressed", &mCurrentCompressedTF);
351+
} else {
352+
mIntervalTree->Branch("CMVPerTF", &mCurrentTF);
353+
}
333354
}
334355

335356
void clearBuffer(const bool currentBuffer)
@@ -543,7 +564,9 @@ DataProcessorSpec getTPCDistributeCMVSpec(const int ilane, const std::vector<uin
543564
Options{{"drop-data-after-nTFs", VariantType::Int, 0, {"Number of TFs after which to drop the data."}},
544565
{"check-data-every-n", VariantType::Int, 0, {"Number of run function called after which to check for missing data (-1 for no checking, 0 for default checking)."}},
545566
{"nFactorTFs", VariantType::Int, 1000, {"Number of TFs to skip for sending oldest TF."}},
546-
{"dump-cmvs", VariantType::Bool, false, {"Dump CMVs to a local ROOT file for debugging"}}}}; // end DataProcessorSpec
567+
{"dump-cmvs", VariantType::Bool, false, {"Dump CMVs to a local ROOT file for debugging"}},
568+
{"use-compression", VariantType::Bool, false, {"Use delta+zigzag+varint compression when storing CMVs in TTree (false = store raw CMVPerTF, relies on ROOT built-in zlib)"}},
569+
{"cmv-zero-threshold", VariantType::Float, 1.0f, {"Zero out CMV values with |value| < threshold (ADC counts) before storing; 0 disables zeroing"}}}}; // end DataProcessorSpec
547570
spec.rank = ilane;
548571
return spec;
549572
}

Detectors/TPC/workflow/src/CMVToVectorSpec.cxx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ o2::framework::DataProcessorSpec getCMVToVectorSpec(const std::string inputSpec,
427427
{"write-raw-data-on-error", VariantType::Bool, false, {"dump raw data in case errors occurred"}},
428428
{"raw-file-name", VariantType::String, "/tmp/cmv_debug.{run}.{raw_type}", {"name of the raw output file"}},
429429
{"raw-data-type", VariantType::Int, 0, {"Which raw data to dump: 0-full TPC with DH, 1-full TPC with DH skip empty, 2-full TPC no DH, 3-full TPC no DH skip empty, 4-IDC raw only 5-CMV raw only"}},
430-
{"check-incomplete-hbf", VariantType::Bool, false, {"false: don't chck; true: check and report"}},
430+
{"check-incomplete-hbf", VariantType::Bool, false, {"false: don't check; true: check and report"}},
431431
} // end Options
432432
}; // end DataProcessorSpec
433433
}

0 commit comments

Comments
 (0)