Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ o2_add_library(DataFormatsIOTOF
SOURCES src/Digit.cxx
# SOURCES src/MCLabel.cxx
SOURCES src/Cluster.cxx
PUBLIC_LINK_LIBRARIES O2::DataFormatsITSMFT)
PUBLIC_LINK_LIBRARIES O2::DataFormatsITSMFT
O2::IOTOFBase
O2::FrameworkLogger)

o2_target_root_dictionary(DataFormatsIOTOF
HEADERS include/DataFormatsIOTOF/Digit.h
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,180 @@
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

/// \file Cluster.h
/// \brief Definition of the IOTOF cluster
#ifndef ALICEO2_DATAFORMATSIOTOF_CLUSTER_H
#define ALICEO2_DATAFORMATSIOTOF_CLUSTER_H

#include <Rtypes.h>
#include <cstdint>
#include <string>
#include <iosfwd>
#include <Rtypes.h>

#include "Framework/Logger.h"

namespace o2
{
namespace iotof
{

/// Compact encoding for ALICE3 IOTOF cluster parameters inside a single 64-bit word.
struct ClusterInfo {
// Bit widths (Total: 52 bits out of 64)
static constexpr int NBitsRow = 9;
static constexpr int NBitsCol = 8;
static constexpr int NBitsRowSpan = 4;
static constexpr int NBitsColSpan = 4;
static constexpr int NBitsPattern = 16;
static constexpr int NBitsTopology = 11;

// Bit offsets (ordered logically from LSB to MSB)
static constexpr int ShiftRow = 0;
static constexpr int ShiftCol = ShiftRow + NBitsRow; // 9
static constexpr int ShiftRowSpan = ShiftCol + NBitsCol; // 17
static constexpr int ShiftColSpan = ShiftRowSpan + NBitsRowSpan; // 21
static constexpr int ShiftPattern = ShiftColSpan + NBitsColSpan; // 25
static constexpr int ShiftTopology = ShiftPattern + NBitsPattern; // 41

// Bit masks
static constexpr uint64_t MaskRow = (1ULL << NBitsRow) - 1;
static constexpr uint64_t MaskCol = (1ULL << NBitsCol) - 1;
static constexpr uint64_t MaskRowSpan = (1ULL << NBitsRowSpan) - 1;
static constexpr uint64_t MaskColSpan = (1ULL << NBitsColSpan) - 1;
static constexpr uint64_t MaskPattern = (1ULL << NBitsPattern) - 1;
static constexpr uint64_t MaskTopology = (1ULL << NBitsTopology) - 1;

uint64_t data{0};

// Constructors
constexpr ClusterInfo() = default;
constexpr ClusterInfo(uint64_t d) : data(d) {}

// Static packer
static constexpr uint64_t pack(uint32_t row, uint32_t col, uint32_t rowSpan,
uint32_t colSpan, uint32_t pattern, uint32_t topology)
{
return ((static_cast<uint64_t>(row) & MaskRow) << ShiftRow) |
((static_cast<uint64_t>(col) & MaskCol) << ShiftCol) |
((static_cast<uint64_t>(rowSpan) & MaskRowSpan) << ShiftRowSpan) |
((static_cast<uint64_t>(colSpan) & MaskColSpan) << ShiftColSpan) |
((static_cast<uint64_t>(pattern) & MaskPattern) << ShiftPattern) |
((static_cast<uint64_t>(topology) & MaskTopology) << ShiftTopology);
}

namespace o2::iotof
// Getters
constexpr uint32_t getRow() const { return (data >> ShiftRow) & MaskRow; }
constexpr uint32_t getCol() const { return (data >> ShiftCol) & MaskCol; }
constexpr uint32_t getRowSpan() const { return (data >> ShiftRowSpan) & MaskRowSpan; }
constexpr uint32_t getColSpan() const { return (data >> ShiftColSpan) & MaskColSpan; }
constexpr uint32_t getPattern() const { return (data >> ShiftPattern) & MaskPattern; }
constexpr uint32_t getTopology() const { return (data >> ShiftTopology) & MaskTopology; }

// Setters
constexpr void setRow(uint32_t r)
{
data = (data & ~(MaskRow << ShiftRow)) | ((static_cast<uint64_t>(r) & MaskRow) << ShiftRow);
}
constexpr void setCol(uint32_t c)
{
data = (data & ~(MaskCol << ShiftCol)) | ((static_cast<uint64_t>(c) & MaskCol) << ShiftCol);
}
constexpr void setRowSpan(uint32_t rs)
{
data = (data & ~(MaskRowSpan << ShiftRowSpan)) | ((static_cast<uint64_t>(rs) & MaskRowSpan) << ShiftRowSpan);
}
constexpr void setColSpan(uint32_t cs)
{
data = (data & ~(MaskColSpan << ShiftColSpan)) | ((static_cast<uint64_t>(cs) & MaskColSpan) << ShiftColSpan);
}
constexpr void setPattern(uint32_t p)
{
data = (data & ~(MaskPattern << ShiftPattern)) | ((static_cast<uint64_t>(p) & MaskPattern) << ShiftPattern);
}
constexpr void setTopology(uint32_t t)
{
data = (data & ~(MaskTopology << ShiftTopology)) | ((static_cast<uint64_t>(t) & MaskTopology) << ShiftTopology);
}

ClassDefNV(ClusterInfo, 1);
};

class Cluster
{
public:
static constexpr uint16_t InvalidPatternID = static_cast<uint16_t>(ClusterInfo::MaskPattern);

Cluster() = default;
Cluster(UShort_t row, UShort_t col, UShort_t rowSpan, UShort_t colSpan, UShort_t patt, UShort_t topo, UShort_t chipID = 0, time_t time = 0.0f)
: mChipID(chipID), mTime(time)
{
mClusterInfo.data = ClusterInfo::pack(row, col, rowSpan, colSpan, patt, topo);
}

void set(UShort_t row, UShort_t col, UShort_t rowSpan, UShort_t colSpan, UShort_t patt, UShort_t topo, UShort_t chipID, time_t time)
{
mClusterInfo.data = ClusterInfo::pack(row, col, rowSpan, colSpan, patt, topo);
mChipID = chipID;
mTime = time;
}

struct Cluster {
uint16_t chipID = 0;
uint16_t row = 0;
uint16_t col = 0;
uint16_t size = 1;
double time = 0.0;
// Unpack Getters
uint32_t getRow() const { return mClusterInfo.getRow(); }
uint32_t getCol() const { return mClusterInfo.getCol(); }
uint32_t getRowSpan() const { return mClusterInfo.getRowSpan(); }
uint32_t getColSpan() const { return mClusterInfo.getColSpan(); }
uint32_t getPattern() const { return mClusterInfo.getPattern(); }
uint32_t getTopology() const { return mClusterInfo.getTopology(); }
int getSize() const
{
// Count the number of set bits in the pattern to determine the size of the cluster
uint32_t pattern = getPattern();
int size = 0;
while (pattern) {
size += pattern & 1;
pattern >>= 1;
}
return size;
}

// BaseCluster / Interface Compatibility Getters
uint32_t getChipID() const { return mChipID; }
uint32_t getSensorID() const { return mChipID; }
time_t getTime() const { return mTime; }
uint64_t getPackedData() const { return mClusterInfo.data; }

// Setters
void setRow(UShort_t r) { mClusterInfo.setRow(r); }
void setCol(UShort_t c) { mClusterInfo.setCol(c); }
void setRowSpan(UShort_t rs) { mClusterInfo.setRowSpan(rs); }
void setColSpan(UShort_t cs) { mClusterInfo.setColSpan(cs); }
void setPatternID(UShort_t p) { mClusterInfo.setPattern(p); }
void setTopology(UShort_t t) { mClusterInfo.setTopology(t); }
void setChipID(UShort_t c) { mChipID = c; }
void setTime(time_t t) { mTime = t; }

// Operators & Debugging
bool operator==(const Cluster& cl) const
{
return mClusterInfo.data == cl.mClusterInfo.data && mChipID == cl.mChipID && mTime == cl.mTime;
}

void print() const;
std::string asString() const;

ClassDefNV(Cluster, 1);
private:
ClusterInfo mClusterInfo{}; ///< 64-bit packed structure containing geometry/topology
UShort_t mChipID{0}; ///< Chip / Sensor ID
float mTime{0.0f}; ///< Hit timing information

void sanityCheck();

ClassDefNV(Cluster, 2);
};

} // namespace o2::iotof
} // namespace iotof
} // namespace o2

std::ostream& operator<<(std::ostream& stream, const o2::iotof::Cluster& cl);

#endif
#endif /* ALICEO2_DATAFORMATSIOTOF_CLUSTER_H */
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,64 @@
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

/// \file Cluster.cxx
/// \brief Implementation of the IOTOF cluster

#include "DataFormatsIOTOF/Cluster.h"
#include <sstream>
#include "Framework/Logger.h"
#include <cassert>
#include <iostream>
#include <format>

// Root ClassImp macros for serialization metadata
ClassImp(o2::iotof::ClusterInfo);
ClassImp(o2::iotof::Cluster);

namespace o2::iotof
namespace o2
{
namespace iotof
{

std::string Cluster::asString() const
{
std::ostringstream stream;
stream << "chip=" << chipID << " row=" << row << " col=" << col << " size=" << size;
return stream.str();
LOG(debug) << "[Cluster::asString] Converting Cluster to string";
return std::format(
"chip: {:5d} | row: {:3d} col: {:3d} | span: {:2d}x{:2d} | pattern: {:5d} topology: {:4d}",
getChipID(),
getRow(),
getCol(),
getRowSpan(),
getColSpan(),
getPattern(),
getTopology());
}

//______________________________________________________________________________
void Cluster::print() const
{
std::cout << *this << "\n";
}

//______________________________________________________________________________
void Cluster::sanityCheck()
{
LOG(debug) << "[Cluster::sanityCheck] Performing sanity check on Cluster fields";

// Ensure extracted values fit within allowed bit masks
assert(getRow() <= ClusterInfo::MaskRow);
assert(getCol() <= ClusterInfo::MaskCol);
assert(getRowSpan() <= ClusterInfo::MaskRowSpan);
assert(getColSpan() <= ClusterInfo::MaskColSpan);
assert(getPattern() <= ClusterInfo::MaskPattern);
assert(getTopology() <= ClusterInfo::MaskTopology);
}

} // namespace o2::iotof
} // namespace iotof
} // namespace o2

// Stream operator implementation
std::ostream& operator<<(std::ostream& stream, const o2::iotof::Cluster& cl)
{
stream << cl.asString();
return stream;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#pragma link C++ class o2::iotof::Digit + ;
#pragma link C++ class std::vector < o2::iotof::Digit> + ;

#pragma link C++ class o2::iotof::ClusterInfo + ;
#pragma link C++ class o2::iotof::Cluster + ;
#pragma link C++ class std::vector < o2::iotof::Cluster> + ;

Expand Down
2 changes: 2 additions & 0 deletions Detectors/Upgrades/ALICE3/IOTOF/base/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@

o2_add_library(IOTOFBase
SOURCES src/GeometryTGeo.cxx
src/Segmentation.cxx
src/IOTOFBaseParam.cxx
PUBLIC_LINK_LIBRARIES O2::DetectorsBase
O2::MathUtils)

o2_target_root_dictionary(IOTOFBase
HEADERS include/IOTOFBase/GeometryTGeo.h
include/IOTOFBase/Segmentation.h
include/IOTOFBase/IOTOFBaseParam.h)
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ class Segmentation
/// the center of the sensitive volulme.
/// \param int iRow Detector x cell coordinate. Has the range 0 <= iRow < mNumberOfRows
/// \param int iCol Detector z cell coordinate. Has the range 0 <= iCol < mNumberOfColumns
bool localToDetector(float x, float z, int& iRow, int& iCol, const int subDetectorID);
bool localToDetector(float x, float z, int& iRow, int& iCol, const int subDetectorID) const;
/// same but w/o check for row/column range
void localToDetectorUnchecked(float xRow, float zCol, int& iRow, int& iCol, const int subDetectorID);
void localToDetectorUnchecked(float xRow, float zCol, int& iRow, int& iCol, const int subDetectorID) const;

/// Transformation from Detector cell coordiantes to Geant detector centered
/// Transformation from Detector cell coordinates to Geant detector centered
/// local coordinates (cm)
/// \param int iRow Detector x cell coordinate. Has the range 0 <= iRow < mNumberOfRows
/// \param int iCol Detector z cell coordinate. Has the range 0 <= iCol < mNumberOfColumns
Expand All @@ -67,7 +67,7 @@ class Segmentation

// w/o check for row/col range
template <typename T = float, typename L = float>
void detectorToLocalUnchecked(L row, L col, T& xRow, T& zCol, const int subDetectorID)
void detectorToLocalUnchecked(L row, L col, T& xRow, T& zCol, const int subDetectorID) const
{
if (subDetectorID != 0 && subDetectorID != 1) {
row = col = -1;
Expand All @@ -78,7 +78,7 @@ class Segmentation
zCol = col * specsConfig.PitchCol + getFirstColCoordinate(subDetectorID);
}
template <typename T = float, typename L = float>
void detectorToLocalUnchecked(L row, L col, math_utils::Point3D<T>& loc, const int subDetectorID)
void detectorToLocalUnchecked(L row, L col, math_utils::Point3D<T>& loc, const int subDetectorID) const
{
if (subDetectorID != 0 && subDetectorID != 1) {
row = col = -1;
Expand All @@ -88,7 +88,7 @@ class Segmentation
loc.SetCoordinates(getFirstRowCoordinate(subDetectorID) - row * specsConfig.PitchRow, T(0.), col * specsConfig.PitchCol + getFirstColCoordinate(subDetectorID));
}
template <typename T = float, typename L = float>
void detectorToLocalUnchecked(L row, L col, std::array<T, 3>& loc, const int subDetectorID)
void detectorToLocalUnchecked(L row, L col, std::array<T, 3>& loc, const int subDetectorID) const
{
if (subDetectorID != 0 && subDetectorID != 1) {
row = col = -1;
Expand All @@ -103,7 +103,7 @@ class Segmentation
// same but with check for row/col range

template <typename T = float, typename L = float>
bool detectorToLocal(L row, L col, T& xRow, T& zCol, const int subDetectorID)
bool detectorToLocal(L row, L col, T& xRow, T& zCol, const int subDetectorID) const
{
if (subDetectorID != 0 && subDetectorID != 1) {
row = col = -1;
Expand All @@ -118,7 +118,7 @@ class Segmentation
}

template <typename T = float, typename L = float>
bool detectorToLocal(L row, L col, math_utils::Point3D<T>& loc, const int subDetectorID)
bool detectorToLocal(L row, L col, math_utils::Point3D<T>& loc, const int subDetectorID) const
{
if (subDetectorID != 0 && subDetectorID != 1) {
row = col = -1;
Expand All @@ -132,7 +132,7 @@ class Segmentation
return true;
}
template <typename T = float, typename L = float>
bool detectorToLocal(L row, L col, std::array<T, 3>& loc, const int subDetectorID)
bool detectorToLocal(L row, L col, std::array<T, 3>& loc, const int subDetectorID) const
{
if (subDetectorID != 0 && subDetectorID != 1) {
row = col = -1;
Expand All @@ -146,12 +146,12 @@ class Segmentation
return true;
}

float getFirstRowCoordinate(const int subDetectorID)
float getFirstRowCoordinate(const int subDetectorID) const
{
const auto& specsConfig = ChipSpecificsParam::Instance();
return 0.5 * ((specsConfig.ActiveMatrixSizeRows() - specsConfig.PassiveEdgeTop + specsConfig.PassiveEdgeReadOut) - specsConfig.PitchRow);
}
float getFirstColCoordinate(const int subDetectorID)
float getFirstColCoordinate(const int subDetectorID) const
{
const auto& specsConfig = ChipSpecificsParam::Instance();
return 0.5 * (specsConfig.PitchCol - specsConfig.ActiveMatrixSizeCols());
Expand All @@ -161,7 +161,7 @@ class Segmentation
};

//_________________________________________________________________________________________________
inline void Segmentation::localToDetectorUnchecked(float xRow, float zCol, int& iRow, int& iCol, const int subDetectorID)
inline void Segmentation::localToDetectorUnchecked(float xRow, float zCol, int& iRow, int& iCol, const int subDetectorID) const
{
// convert to row/col w/o over/underflow check
if (subDetectorID != 0 && subDetectorID != 1) {
Expand All @@ -187,7 +187,7 @@ inline void Segmentation::localToDetectorUnchecked(float xRow, float zCol, int&
}

//_________________________________________________________________________________________________
inline bool Segmentation::localToDetector(float xRow, float zCol, int& iRow, int& iCol, const int subDetectorID)
inline bool Segmentation::localToDetector(float xRow, float zCol, int& iRow, int& iCol, const int subDetectorID) const
{
// convert to row/col
if (subDetectorID != 0 && subDetectorID != 1) {
Expand Down
Loading