Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions DataFormats/Detectors/TPC/include/DataFormatsTPC/CMV.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,13 @@ struct Data {
return positive ? magnitude : -magnitude;
}

// Encode from float: clamps magnitude to 15 bits, range ±255.992
// Encode from float: truncates magnitude to 15 bits, range ±255.992
void setCMVFloat(float value)
{
const bool positive = (value >= 0.f);
const uint16_t magnitude = static_cast<uint16_t>(std::abs(value) * 128.f + 0.5f) & 0x7FFF;
const uint16_t magnitude = static_cast<uint16_t>(
std::lround(std::abs(value) * 128.f)) &
0x7FFF;
cmv = (positive ? 0x8000 : 0x0000) | magnitude;
}
};
Expand Down Expand Up @@ -119,4 +121,4 @@ struct Container {

} // namespace o2::tpc::cmv

#endif
#endif
4 changes: 3 additions & 1 deletion Detectors/TPC/calibration/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ o2_add_library(TPCCalibration
src/PressureTemperatureHelper.cxx
src/CMVContainer.cxx
src/CorrectionMapsLoader.cxx
src/CMVHelper.cxx
PUBLIC_LINK_LIBRARIES O2::DataFormatsTPC O2::TPCBaseRecSim
O2::TPCReconstruction ROOT::Minuit
Microsoft.GSL::GSL
Expand Down Expand Up @@ -119,7 +120,8 @@ o2_target_root_dictionary(TPCCalibration
include/TPCCalibration/CorrectdEdxDistortions.h
include/TPCCalibration/PressureTemperatureHelper.h
include/TPCCalibration/CMVContainer.h
include/TPCCalibration/CorrectionMapsLoader.h)
include/TPCCalibration/CorrectionMapsLoader.h
include/TPCCalibration/CMVHelper.h)

o2_add_test_root_macro(macro/comparePedestalsAndNoise.C
PUBLIC_LINK_LIBRARIES O2::TPCBaseRecSim
Expand Down
52 changes: 52 additions & 0 deletions Detectors/TPC/calibration/include/TPCCalibration/CMVHelper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
// All rights not expressly granted are reserved.
//
// This software is distributed under the terms of the GNU General Public
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

/// @file CMVHelper.h
/// @author Tuba Gündem, tuba.gundem@cern.ch
/// @brief Helper utilities for reading CMV ROOT files

#ifndef ALICEO2_TPC_CMVHELPER_H_
#define ALICEO2_TPC_CMVHELPER_H_

#include <string>

#include "TFile.h"
#include "TTree.h"

namespace o2::tpc
{

struct CMVPerTF;
struct CMVPerTFCompressed;

struct CMVFileHandle {
TFile* file{nullptr};
TTree* tree{nullptr};
bool isCompressed{false};
CMVPerTFCompressed* tfCompressed{nullptr};
CMVPerTF* tfRaw{nullptr};
CMVPerTF* tfDecoded{nullptr}; ///< scratch buffer used when decompressing
long firstTFInTree{-1}; ///< first global TF index from tree UserInfo ("firstTF"); -1 if absent
long lastTFInTree{-1}; ///< last global TF index from tree UserInfo ("lastTF"); -1 if absent

/// Open path and set up branch addresses. Returns false on any error
bool open(const std::string& path);

/// Load entry iEntry and return a pointer to the decoded CMVPerTF, or nullptr on error
const CMVPerTF* getEntry(long long iEntry);

/// Release all resources
void close();
};

} // namespace o2::tpc

#endif // ALICEO2_TPC_CMVHELPER_H_
118 changes: 55 additions & 63 deletions Detectors/TPC/calibration/macro/drawCMV.C
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,19 @@

#if !defined(__CLING__) || defined(__ROOTCLING__)
#include <string>
#include <vector>
#include <string_view>
#include <vector>
#include <fmt/format.h>

#include "TFile.h"
#include "TParameter.h"
#include "TTree.h"
#include "TH1F.h"
#include "TH2F.h"
#include "TCanvas.h"

#include "TPCCalibration/CMVContainer.h"
#include "TPCBase/Utils.h"
#include "TPCCalibration/CMVContainer.h"
#include "TPCCalibration/CMVHelper.h"

#endif

using namespace o2::tpc;
Expand All @@ -38,39 +38,20 @@ TObjArray* drawCMV(std::string_view filename, std::string_view outDir, std::stri
arrCanvases->SetName("CMV");

// open file
TFile f(filename.data(), "READ");
if (f.IsZombie()) {
CMVFileHandle fh;
if (!fh.open(std::string(filename))) {
fmt::print("ERROR: cannot open '{}'\n", filename);
return arrCanvases;
}
fmt::print("Opened file: {}\n", filename);
fmt::print("Tree 'ccdb_object' found, entries: {}\n", fh.tree->GetEntries());

// get TTree
TTree* tree = nullptr;
f.GetObject("ccdb_object", tree);
if (!tree) {
fmt::print("ERROR: TTree 'ccdb_object' not found\n");
return arrCanvases;
}
fmt::print("Tree 'ccdb_object' found, entries: {}\n", tree->GetEntries());

// read metadata
long firstTF = -1, lastTF = -1;
if (auto* userInfo = tree->GetUserInfo()) {
for (int i = 0; i < userInfo->GetSize(); ++i) {
if (auto* p = dynamic_cast<TParameter<long>*>(userInfo->At(i))) {
if (std::string(p->GetName()) == "firstTF")
firstTF = p->GetVal();
if (std::string(p->GetName()) == "lastTF")
lastTF = p->GetVal();
}
}
}
fmt::print("firstTF: {}, lastTF: {}\n", firstTF, lastTF);
fmt::print("firstTF: {}, lastTF: {}\n", fh.firstTFInTree, fh.lastTFInTree);

const int nEntries = tree->GetEntries();
const int nEntries = fh.tree->GetEntries();
if (nEntries == 0) {
fmt::print("ERROR: no entries in tree\n");
fh.close();
return arrCanvases;
}

Expand All @@ -80,61 +61,62 @@ TObjArray* drawCMV(std::string_view filename, std::string_view outDir, std::stri
TH2F* h2d = new TH2F("hCMVvsTimeBin", ";Timebin (200 ns);Common Mode Values (ADC)",
100, 0, nTimeBins,
110, -100.5, 9.5);
h2d->SetDirectory(nullptr);
h2d->SetStats(1);
TH1F* h1d = new TH1F("hCMV", ";Common Mode Values (ADC);Counts",
110, -100.5, 9.5);
h1d->SetDirectory(nullptr);
h1d->SetStats(1);

// auto-detect branch format: compressed or raw
const bool isCompressed = (tree->GetBranch("CMVPerTFCompressed") != nullptr);
const bool isRaw = (tree->GetBranch("CMVPerTF") != nullptr);
if (!isCompressed && !isRaw) {
fmt::print("ERROR: no recognised branch found (expected 'CMVPerTFCompressed' or 'CMVPerTF')\n");
return arrCanvases;
}
fmt::print("Branch format: {}\n", isCompressed ? "CMVPerTFCompressed" : "CMVPerTF (raw)");

o2::tpc::CMVPerTFCompressed* tfCompressed = nullptr;
o2::tpc::CMVPerTF* tfRaw = nullptr;
CMVPerTF* tfDecoded = isCompressed ? new CMVPerTF() : nullptr;
TH1F* h1dCRU = new TH1F("hCRU", ";CRU;Counts",
360, -0.5, 359.5);
h1dCRU->SetDirectory(nullptr);
h1dCRU->SetStats(1);
TH2F* h2dCRU = new TH2F("hCMVvsCRU", ";CRU;Common Mode Values (ADC)",
360, -0.5, 359.5,
110, -100.5, 9.5);
h2dCRU->SetDirectory(nullptr);
h2dCRU->SetStats(0);

if (isCompressed) {
tree->SetBranchAddress("CMVPerTFCompressed", &tfCompressed);
} else {
tree->SetBranchAddress("CMVPerTF", &tfRaw);
}
fmt::print("Branch format: {}\n", fh.isCompressed ? "CMVPerTFCompressed" : "CMVPerTF (raw)");

long firstOrbit = -1;
long firstOrbitDPL = -1;

// Pre-allocate fill arrays once; x-values (timebins) are constant across entries and CRUs
const int fillsPerEntry = nCRUs * nTimeBins;
std::vector<double> xArr(fillsPerEntry), yArr(fillsPerEntry), wArr(fillsPerEntry, 1.0), cruArr(fillsPerEntry);
for (int cru = 0; cru < nCRUs; ++cru) {
for (int tb = 0; tb < nTimeBins; ++tb) {
xArr[cru * nTimeBins + tb] = tb;
cruArr[cru * nTimeBins + tb] = cru;
}
}

for (int i = 0; i < nEntries; ++i) {
tree->GetEntry(i);

// Decompress if needed; resolve to a unified CMVPerTF pointer
const CMVPerTF* tf = nullptr;
if (isCompressed) {
tfCompressed->decompress(tfDecoded);
tf = tfDecoded;
} else {
tf = tfRaw;
const CMVPerTF* tf = fh.getEntry(i);
if (!tf) {
continue;
}

firstOrbit = tf->firstOrbit;
firstOrbitDPL = tf->firstOrbitDPL;
fmt::print("firstOrbit: {}, firstOrbitDPL: {}\n", firstOrbit, firstOrbitDPL);

fmt::print("Entry {}: firstOrbit: {}, firstOrbitDPL: {}\n", i, firstOrbit, firstOrbitDPL);

for (int cru = 0; cru < nCRUs; ++cru) {
for (int tb = 0; tb < nTimeBins; ++tb) {
const float cmvValue = tf->getCMVFloat(cru, tb);
h2d->Fill(tb, cmvValue);
h1d->Fill(cmvValue);
yArr[cru * nTimeBins + tb] = tf->getCMVFloat(cru, tb);
// fmt::print("Entry {}: cru: {}, tb: {}, cmv: {}\n", i, cru, tb, tf->getCMVFloat(cru, tb));
}
}
h2d->FillN(fillsPerEntry, xArr.data(), yArr.data(), wArr.data());
h1d->FillN(fillsPerEntry, yArr.data(), wArr.data());
h2dCRU->FillN(fillsPerEntry, cruArr.data(), yArr.data(), wArr.data());
h1dCRU->FillN(fillsPerEntry, cruArr.data(), wArr.data());
}

delete tfDecoded;
tree->ResetBranchAddresses();
delete tfCompressed;
fh.close();

// draw
auto* c = new TCanvas("cCMVvsTimeBin", "");
Expand All @@ -149,10 +131,20 @@ TObjArray* drawCMV(std::string_view filename, std::string_view outDir, std::stri

arrCanvases->Add(c1);

auto* c2 = new TCanvas("cCRUDistribution", "");
h1dCRU->Draw();

arrCanvases->Add(c2);

auto* c3 = new TCanvas("cCMVvsCRU", "");
c3->SetLogz();
h2dCRU->Draw("colz");

arrCanvases->Add(c3);

if (outDir.size()) {
utils::saveCanvases(*arrCanvases, outDir, "", rootFileName);
}

f.Close();
return arrCanvases;
}
98 changes: 98 additions & 0 deletions Detectors/TPC/calibration/src/CMVHelper.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
// All rights not expressly granted are reserved.
//
// This software is distributed under the terms of the GNU General Public
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

/// @file CMVHelper.cxx
/// @author Tuba Gündem, tuba.gundem@cern.ch
/// @brief Helper utilities for reading CMV ROOT files

#include "TPCCalibration/CMVHelper.h"

#include <iostream>

#include "TPCCalibration/CMVContainer.h"
#include "TParameter.h"

namespace o2::tpc
{

bool CMVFileHandle::open(const std::string& path)
{
file = TFile::Open(path.c_str());
if (!file || file->IsZombie()) {
std::cerr << "CMVFileHandle: failed to open: " << path << "\n";
return false;
}
file->GetObject("ccdb_object", tree);
if (!tree) {
std::cerr << "CMVFileHandle: TTree 'ccdb_object' not found in: " << path << "\n";
close();
return false;
}

// Extract firstTF / lastTF from UserInfo if stored by the aggregation workflow
if (auto* ui = tree->GetUserInfo()) {
if (auto* p = dynamic_cast<TParameter<long>*>(ui->FindObject("firstTF"))) {
firstTFInTree = p->GetVal();
}
if (auto* p = dynamic_cast<TParameter<long>*>(ui->FindObject("lastTF"))) {
lastTFInTree = p->GetVal();
}
}

isCompressed = (tree->GetBranch("CMVPerTFCompressed") != nullptr);
const bool isRaw = (tree->GetBranch("CMVPerTF") != nullptr);
if (!isCompressed && !isRaw) {
std::cerr << "CMVFileHandle: no recognised branch (CMVPerTFCompressed / CMVPerTF) in: "
<< path << "\n";
close();
return false;
}

if (isCompressed) {
tree->SetBranchAddress("CMVPerTFCompressed", &tfCompressed);
tfDecoded = new CMVPerTF();
} else {
tree->SetBranchAddress("CMVPerTF", &tfRaw);
}
return true;
}

const CMVPerTF* CMVFileHandle::getEntry(long long iEntry)
{
tree->GetEntry(iEntry);
if (isCompressed) {
if (!tfCompressed) {
return nullptr;
}
tfCompressed->decompress(tfDecoded);
return tfDecoded;
}
return tfRaw;
}

void CMVFileHandle::close()
{
if (tree) {
tree->ResetBranchAddresses();
tree = nullptr;
}
tfCompressed = nullptr;
tfRaw = nullptr;
delete tfDecoded;
tfDecoded = nullptr;
if (file) {
file->Close();
delete file;
file = nullptr;
}
}

} // namespace o2::tpc
1 change: 1 addition & 0 deletions Detectors/TPC/calibration/src/TPCCalibrationLinkDef.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@
#pragma link C++ class std::vector < o2::tpc::DigitAdd> + ;
#pragma link C++ class o2::tpc::PressureTemperatureHelper + ;

#pragma link C++ struct o2::tpc::CMVFileHandle + ;
#pragma link C++ class o2::tpc::CMVPerTF + ;
#pragma link C++ class o2::tpc::CMVPerTFCompressed + ;

Expand Down
5 changes: 5 additions & 0 deletions Detectors/TPC/workflow/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -309,4 +309,9 @@ o2_add_executable(cmv-aggregate
SOURCES src/tpc-aggregate-cmv.cxx
PUBLIC_LINK_LIBRARIES O2::TPCWorkflow)

o2_add_executable(cmv-trigger
COMPONENT_NAME tpc
SOURCES test/test_cmv-trigger.cxx
PUBLIC_LINK_LIBRARIES O2::TPCWorkflow)

add_subdirectory(readers)
Loading