From 6d7d01314c794e47f2248b556ddcd1830692ed6c Mon Sep 17 00:00:00 2001 From: sandor-lokos Date: Wed, 7 Oct 2026 11:57:11 +0200 Subject: [PATCH 1/5] [PWGUD] Resolve duplicate cppcheck conditions in sgFITAnalyzer --- PWGUD/Tasks/sgFITAnalyzer.cxx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/PWGUD/Tasks/sgFITAnalyzer.cxx b/PWGUD/Tasks/sgFITAnalyzer.cxx index 39aa9c2486a..cff17b961f6 100644 --- a/PWGUD/Tasks/sgFITAnalyzer.cxx +++ b/PWGUD/Tasks/sgFITAnalyzer.cxx @@ -778,36 +778,36 @@ struct SGFITAnalyzer { avPtPVa += track.pt(); vecEtaPVa.push_back(vtrk.Eta()); registry.get(HIST("tracks/etaApv"))->Fill(vtrk.Eta(), 1.); - if (!an) + if (!an) { registry.get(HIST("tracks/eta2Apv"))->Fill(vtrk.Eta(), 1.); - if (!an) z0pva++; - else if (an == 4) + } else if (an == 4) { z1pva++; + } } if (truegapSide == 1) { pvc++; avPtPVc += track.pt(); vecEtaPVc.push_back(vtrk.Eta()); registry.get(HIST("tracks/etaCpv"))->Fill(vtrk.Eta(), 1.); - if (!cn) + if (!cn) { registry.get(HIST("tracks/eta2Cpv"))->Fill(vtrk.Eta(), 1.); - if (!cn) z0pvc++; - else if (cn == 4) + } else if (cn == 4) { z1pvc++; + } } if (truegapSide == 2) { pvac++; avPtPVac += track.pt(); vecEtaPVac.push_back(vtrk.Eta()); registry.get(HIST("tracks/etaACpv"))->Fill(vtrk.Eta(), 1.); - if (!an && !cn) + if (!an && !cn) { registry.get(HIST("tracks/eta2ACpv"))->Fill(vtrk.Eta(), 1.); - if (!an && !cn) z0pvac++; - else if (an >= 3 && cn >= 3) + } else if (an >= 3 && cn >= 3) { z1pvac++; + } } registry.get(HIST("tracks/TPCSignalvspPVC"))->Fill(vtrk.Mag(), signalTPC, 1.); registry.get(HIST("tracks/TOFSignalvspPVC"))->Fill(vtrk.Mag(), signalTOF, 1.); From 060368e32b8733cf883194633aca48b33c33dfcf Mon Sep 17 00:00:00 2001 From: sandor-lokos Date: Wed, 7 Oct 2026 15:48:11 +0200 Subject: [PATCH 2/5] [PWGUD] Version UD FIT bits and fix channel indexing --- PWGUD/Core/UDHelpers.h | 39 ++++++++++++++++----- PWGUD/DataModel/UDTables.h | 47 +++++++++++++++++--------- PWGUD/TableProducer/SGCandProducer.cxx | 3 +- 3 files changed, 63 insertions(+), 26 deletions(-) diff --git a/PWGUD/Core/UDHelpers.h b/PWGUD/Core/UDHelpers.h index d014047bcc9..9567958a44a 100644 --- a/PWGUD/Core/UDHelpers.h +++ b/PWGUD/Core/UDHelpers.h @@ -565,27 +565,48 @@ inline void buildFT0FV0Words(TFT0 const& ft0, TFV0A const& fv0a, constexpr int kFV0Offset = 208; auto ampsA = ft0.amplitudeA(); - const int nA = std::min(ampsA.size(), 96); + auto chanA = ft0.channelA(); + const int nA = std::min(ampsA.size(), chanA.size()); for (int i = 0; i < nA; ++i) { const auto a = ampsA[i]; - setBit(thr1, kFT0AOffset + i, a >= thr1_FT0A); - setBit(thr2, kFT0AOffset + i, a >= thr2_FT0A); + const int c = chanA[i]; + + if (c < 0 || c >= 96) { + continue; + } + + setBit(thr1, kFT0AOffset + c, a >= thr1_FT0A); + setBit(thr2, kFT0AOffset + c, a >= thr2_FT0A); } auto ampsC = ft0.amplitudeC(); - const int nC = std::min(ampsC.size(), 112); + auto chanC = ft0.channelC(); + const int nC = std::min(ampsC.size(), chanC.size()); for (int i = 0; i < nC; ++i) { const auto a = ampsC[i]; - setBit(thr1, kFT0COffset + i, a >= thr1_FT0C); - setBit(thr2, kFT0COffset + i, a >= thr2_FT0C); + const int c = chanC[i]; + + if (c < 0 || c >= 112) { + continue; + } + + setBit(thr1, kFT0COffset + c, a >= thr1_FT0C); + setBit(thr2, kFT0COffset + c, a >= thr2_FT0C); } auto ampsV = fv0a.amplitude(); - const int nV = std::min(ampsV.size(), 48); + auto chanV = fv0a.channel(); + const int nV = std::min(ampsV.size(), chanV.size()); for (int i = 0; i < nV; ++i) { const auto a = ampsV[i]; - setBit(thr1, kFV0Offset + i, a >= thr1_FV0A); - setBit(thr2, kFV0Offset + i, a >= thr2_FV0A); + const int c = chanV[i]; + + if (c < 0 || c >= 48) { + continue; + } + + setBit(thr1, kFV0Offset + c, a >= thr1_FV0A); + setBit(thr2, kFV0Offset + c, a >= thr2_FV0A); } } diff --git a/PWGUD/DataModel/UDTables.h b/PWGUD/DataModel/UDTables.h index afb7728a5ab..c47f5befaa1 100644 --- a/PWGUD/DataModel/UDTables.h +++ b/PWGUD/DataModel/UDTables.h @@ -410,28 +410,43 @@ DECLARE_SOA_TABLE(UDTracksLabels, "AOD", "UDTRACKLABEL", namespace udcollfitbits { +DECLARE_SOA_INDEX_COLUMN(UDCollision, udCollision); /// Link to the UDCOLLISION table + DECLARE_SOA_COLUMN(Thr1W0, thr1W0, uint64_t); /// 1 MIP thresholds for FT0A ch 0 - ch 63 -DECLARE_SOA_COLUMN(Thr1W1, thr1W1, uint64_t); /// 1 MIP thresholds for FT0A ch 64 - ch 96 & FT0C ch 0 - ch 31 -DECLARE_SOA_COLUMN(Thr1W2, thr1W2, uint64_t); /// 1 MIP thresholds for FT0C ch 32 - ch 96 -DECLARE_SOA_COLUMN(Thr1W3, thr1W3, uint64_t); /// 1 MIP thresholds for FT0C ch 97 - 112 & FV0 0 - 47 +DECLARE_SOA_COLUMN(Thr1W1, thr1W1, uint64_t); /// 1 MIP thresholds for FT0A ch 64 - ch 95 & FT0C ch 0 - ch 31 +DECLARE_SOA_COLUMN(Thr1W2, thr1W2, uint64_t); /// 1 MIP thresholds for FT0C ch 32 - ch 95 +DECLARE_SOA_COLUMN(Thr1W3, thr1W3, uint64_t); /// 1 MIP thresholds for FT0C ch 96 - 111 & FV0 0 - 47 DECLARE_SOA_COLUMN(Thr2W0, thr2W0, uint64_t); /// 2 MIP thresholds for FT0A ch 0 - ch 63 -DECLARE_SOA_COLUMN(Thr2W1, thr2W1, uint64_t); /// 2 MIP thresholds for FT0A ch 63 - ch 96 & FT0C ch 0 - ch 31 -DECLARE_SOA_COLUMN(Thr2W2, thr2W2, uint64_t); /// 2 MIP thresholds for FT0C ch 32 - ch 96 -DECLARE_SOA_COLUMN(Thr2W3, thr2W3, uint64_t); /// 2 MIP thresholds for FT0C ch 97 - 112 & FV0 0 - 47 +DECLARE_SOA_COLUMN(Thr2W1, thr2W1, uint64_t); /// 2 MIP thresholds for FT0A ch 64 - ch 95 & FT0C ch 0 - ch 31 +DECLARE_SOA_COLUMN(Thr2W2, thr2W2, uint64_t); /// 2 MIP thresholds for FT0C ch 32 - ch 95 +DECLARE_SOA_COLUMN(Thr2W3, thr2W3, uint64_t); /// 2 MIP thresholds for FT0C ch 96 - 111 & FV0 0 - 47 } // namespace udcollfitbits -DECLARE_SOA_TABLE(UDCollisionFITBits, "AOD", "UDCOLLFITBITS", +DECLARE_SOA_TABLE(UDCollisionFITBits_000, "AOD", "UDCOLLFITBITS", o2::soa::Index<>, - udcollfitbits::Thr1W0, /// 1 MIP thresholds for FT0A ch 0 - ch 63 - udcollfitbits::Thr1W1, /// 1 MIP thresholds for FT0A ch 63 - ch 96 & FT0C ch 0 - ch 31 - udcollfitbits::Thr1W2, /// 1 MIP thresholds for FT0C ch 32 - ch 96 - udcollfitbits::Thr1W3, /// 1 MIP thresholds for FT0C ch 97 - 112 & FV0 0 - 47 - udcollfitbits::Thr2W0, /// 2 MIP thresholds for FT0A ch 0 - ch 63 - udcollfitbits::Thr2W1, /// 2 MIP thresholds for FT0A ch 63 - ch 96 & FT0C ch 0 - ch 31 - udcollfitbits::Thr2W2, /// 2 MIP thresholds for FT0C ch 32 - ch 96 - udcollfitbits::Thr2W3 /// 2 MIP thresholds for FT0C ch 97 - 112 & FV0 0 - 47 -); + udcollfitbits::Thr1W0, + udcollfitbits::Thr1W1, + udcollfitbits::Thr1W2, + udcollfitbits::Thr1W3, + udcollfitbits::Thr2W0, + udcollfitbits::Thr2W1, + udcollfitbits::Thr2W2, + udcollfitbits::Thr2W3); + +DECLARE_SOA_TABLE_VERSIONED(UDCollisionFITBits_001, "AOD", "UDCOLLFITBITS", 1, + o2::soa::Index<>, + udcollfitbits::UDCollisionId, + udcollfitbits::Thr1W0, + udcollfitbits::Thr1W1, + udcollfitbits::Thr1W2, + udcollfitbits::Thr1W3, + udcollfitbits::Thr2W0, + udcollfitbits::Thr2W1, + udcollfitbits::Thr2W2, + udcollfitbits::Thr2W3); + +using UDCollisionFITBits = UDCollisionFITBits_001; using UDTrack = UDTracks::iterator; using UDTrackCov = UDTracksCov::iterator; diff --git a/PWGUD/TableProducer/SGCandProducer.cxx b/PWGUD/TableProducer/SGCandProducer.cxx index 8bc472c455f..6574d613986 100644 --- a/PWGUD/TableProducer/SGCandProducer.cxx +++ b/PWGUD/TableProducer/SGCandProducer.cxx @@ -351,7 +351,8 @@ struct SGCandProducer { fitCuts.thr2_FV0A()); } - outputFITBits(w1[0], w1[1], w1[2], w1[3], + outputFITBits(outputCollisions.lastIndex(), + w1[0], w1[1], w1[2], w1[3], w2[0], w2[1], w2[2], w2[3]); if (newbc.has_zdc()) { From de4116c2c21af939ed94fe1807ca011d0b4d2d32 Mon Sep 17 00:00:00 2001 From: sandor-lokos Date: Wed, 7 Oct 2026 16:11:06 +0200 Subject: [PATCH 3/5] [PWGUD] Resolve cppcheck findings in UD helpers --- PWGUD/Core/UDHelpers.h | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/PWGUD/Core/UDHelpers.h b/PWGUD/Core/UDHelpers.h index 9567958a44a..ed94841fda5 100644 --- a/PWGUD/Core/UDHelpers.h +++ b/PWGUD/Core/UDHelpers.h @@ -425,14 +425,14 @@ bool cleanFDD(T& bc, float maxFITtime, float limitA, float limitC) // lims[4]: FDDC template -bool cleanFIT(T& bc, float maxFITtime, std::vector lims) +bool cleanFIT(T& bc, float maxFITtime, std::vector const& lims) { return cleanFV0(bc, maxFITtime, lims[0]) && cleanFT0(bc, maxFITtime, lims[1], lims[2]) && cleanFDD(bc, maxFITtime, lims[3], lims[4]); } template -bool cleanFITCollision(T& col, float maxFITtime, std::vector lims) +bool cleanFITCollision(T& col, float maxFITtime, std::vector const& lims) { bool isCleanFV0 = true; if (col.has_foundFV0()) { @@ -453,7 +453,7 @@ bool cleanFITCollision(T& col, float maxFITtime, std::vector lims) // ----------------------------------------------------------------------------- template -bool cleanFITA(T& bc, float maxFITtime, std::vector lims) +bool cleanFITA(T& bc, float maxFITtime, std::vector const& lims) { return cleanFV0(bc, maxFITtime, lims[0]) && cleanFT0A(bc, maxFITtime, lims[1]) && @@ -462,7 +462,7 @@ bool cleanFITA(T& bc, float maxFITtime, std::vector lims) // ----------------------------------------------------------------------------- template -bool cleanFITC(T& bc, float maxFITtime, std::vector lims) +bool cleanFITC(T& bc, float maxFITtime, std::vector const& lims) { return cleanFT0C(bc, maxFITtime, lims[2]) && cleanFDDC(bc, maxFITtime, lims[4]); @@ -506,7 +506,7 @@ bool TCE(T& bc) // ----------------------------------------------------------------------------- template -bool TOR(T& bc, float maxFITtime, std::vector lims) +bool TOR(T& bc, float maxFITtime, std::vector const& lims) { auto torA = !cleanFT0A(bc, maxFITtime, lims[1]); auto torC = !cleanFT0C(bc, maxFITtime, lims[2]); @@ -798,24 +798,15 @@ template bool goodCollision(T const& coll, DGCutparHolder const& diffCuts) // Return true if collision is accepted according to user-chosen rules from event selection task { - bool accepted = true; - std::vector sels = diffCuts.collisionSel(); - if (sels[0]) - accepted = accepted && cutNoTimeFrameBorder(coll); - if (sels[1]) - accepted = accepted && cutNoSameBunchPileup(coll); - if (sels[2]) - accepted = accepted && cutNoITSROFrameBorder(coll); - if (sels[3]) - accepted = accepted && cutIsGoodZvtxFT0vsPV(coll); - if (sels[4]) - accepted = accepted && cutIsVertexITSTPC(coll); - if (sels[5]) - accepted = accepted && cutIsVertexTRDmatched(coll); - if (sels[6]) - accepted = accepted && cutIsVertexTOFmatched(coll); - - return accepted; + auto const sels = diffCuts.collisionSel(); + + return (!sels[0] || cutNoTimeFrameBorder(coll)) && + (!sels[1] || cutNoSameBunchPileup(coll)) && + (!sels[2] || cutNoITSROFrameBorder(coll)) && + (!sels[3] || cutIsGoodZvtxFT0vsPV(coll)) && + (!sels[4] || cutIsVertexITSTPC(coll)) && + (!sels[5] || cutIsVertexTRDmatched(coll)) && + (!sels[6] || cutIsVertexTOFmatched(coll)); } // ----------------------------------------------------------------------------- From 8aeda6e47e68692ccd4798e562b7806c8d7ddae0 Mon Sep 17 00:00:00 2001 From: sandor-lokos Date: Thu, 8 Oct 2026 11:21:54 +0200 Subject: [PATCH 4/5] [PWGUD] Extend FIT bit mapping tutorial --- PWGUD/Tasks/upcTestFitBitMapping.cxx | 223 ++++++++++++++++++++------- 1 file changed, 165 insertions(+), 58 deletions(-) diff --git a/PWGUD/Tasks/upcTestFitBitMapping.cxx b/PWGUD/Tasks/upcTestFitBitMapping.cxx index bcee34c60fc..829e1df5f52 100644 --- a/PWGUD/Tasks/upcTestFitBitMapping.cxx +++ b/PWGUD/Tasks/upcTestFitBitMapping.cxx @@ -11,111 +11,218 @@ // /// \file upcTestFitBitMapping.cxx -/// \brief FIT bits to phi, eta mapping +/// \brief Tutorial for accessing and mapping the FIT-bit table /// \author Sandor Lokos, sandor.lokos@cern.ch -/// \since March 2026 +/// \since October 2026 -#include "PWGUD/Core/UDHelpers.h" // udhelpers::Bits256, makeBits256, testBit, getPhiEtaFromFitBit -#include "PWGUD/DataModel/UDTables.h" // aod::UDCollisionFITBits +#include "PWGUD/Core/UDHelpers.h" +#include "PWGUD/DataModel/UDTables.h" -#include // o2::constants::math::TwoPI -#include // o2::ft0::Geometry +#include +#include #include #include #include #include #include +#include #include #include -#include using namespace o2; using namespace o2::framework; struct UpcTestFitBitMapping { - static constexpr int Thr2Selector = 2; // value of whichThr that selects the Thr2 bit set + static constexpr int kThr1Selector = 1; + static constexpr int kThr2Selector = 2; - Configurable whichThr{"whichThr", 1, "Use 1=Thr1 bits or 2=Thr2 bits"}; - Configurable maxEvents{"maxEvents", -1, "Process at most this many rows (-1 = all)"}; + Configurable whichThr{"whichThr", 1, "FIT amplitude threshold to use: 1 for Thr1 or 2 for Thr2"}; - // Minimal offset container compatible with UDHelpers.h expectations: getX/getY/getZ + // Minimal zero-offset container required by getPhiEtaFromFitBit(). + // Detector-alignment corrections are not applied in this tutorial. struct OffsetXYZ { - double x{0}, y{0}, z{0}; + double x{0.}; + double y{0.}; + double z{0.}; + double getX() const { return x; } double getY() const { return y; } double getZ() const { return z; } }; - std::array offsetFT0{}; // iRunOffset = 0 for now - int iRunOffset = 0; + std::array offsetFT0{}; + static constexpr int kRunOffsetIndex = 0; - o2::ft0::Geometry ft0Det{}; + o2::ft0::Geometry ft0Geometry{}; HistogramRegistry registry{ "registry", { - {"hPhiA", "FT0A #varphi;#varphi;counts", {HistType::kTH1F, {{18, 0.0, o2::constants::math::TwoPI}}}}, - {"hEtaA", "FT0A #eta;#eta;counts", {HistType::kTH1F, {{8, 3.5, 5.0}}}}, - {"hEtaPhiA", "FT0A #eta vs #varphi;#eta;#varphi", {HistType::kTH2F, {{8, 3.5, 5.0}, {18, 0.0, o2::constants::math::TwoPI}}}}, - - {"hPhiC", "FT0C #varphi;#varphi;counts", {HistType::kTH1F, {{18, 0.0, o2::constants::math::TwoPI}}}}, - {"hEtaC", "FT0C #eta;#eta;counts", {HistType::kTH1F, {{8, -3.5, -2.0}}}}, - {"hEtaPhiC", "FT0C #eta vs #varphi;#eta;#varphi", {HistType::kTH2F, {{8, -3.5, -2.0}, {18, 0.0, o2::constants::math::TwoPI}}}}, + {"association/hRowsPerCollision", + "FIT-bit rows associated with each collision;rows;collisions", + {HistType::kTH1F, {{4, -0.5, 3.5}}}}, + + {"multiplicity/hFT0A", + "FT0A fired-channel multiplicity;N_{fired}^{FT0A};collisions", + {HistType::kTH1F, {{97, -0.5, 96.5}}}}, + {"multiplicity/hFT0C", + "FT0C fired-channel multiplicity;N_{fired}^{FT0C};collisions", + {HistType::kTH1F, {{113, -0.5, 112.5}}}}, + {"multiplicity/hFV0A", + "FV0A fired-channel multiplicity;N_{fired}^{FV0A};collisions", + {HistType::kTH1F, {{49, -0.5, 48.5}}}}, + {"multiplicity/hFT0AVsFT0C", + "FT0A versus FT0C fired channels;N_{fired}^{FT0A};N_{fired}^{FT0C}", + {HistType::kTH2F, {{97, -0.5, 96.5}, {113, -0.5, 112.5}}}}, + + {"occupancy/hPackedBit", + "Selected FIT-bit occupancy;packed bit;counts", + {HistType::kTH1F, {{256, -0.5, 255.5}}}}, + {"occupancy/hFT0AChannel", + "FT0A channel occupancy;FT0A channel;counts", + {HistType::kTH1F, {{96, -0.5, 95.5}}}}, + {"occupancy/hFT0CChannel", + "FT0C channel occupancy;FT0C channel;counts", + {HistType::kTH1F, {{112, -0.5, 111.5}}}}, + {"occupancy/hFV0AChannel", + "FV0A channel occupancy;FV0A channel;counts", + {HistType::kTH1F, {{48, -0.5, 47.5}}}}, + + {"mapping/hPhiA", + "FT0A #varphi;#varphi;counts", + {HistType::kTH1F, {{18, 0., o2::constants::math::TwoPI}}}}, + {"mapping/hEtaA", + "FT0A #eta;#eta;counts", + {HistType::kTH1F, {{8, 3.5, 5.0}}}}, + {"mapping/hEtaPhiA", + "FT0A #eta versus #varphi;#eta;#varphi", + {HistType::kTH2F, {{8, 3.5, 5.0}, {18, 0., o2::constants::math::TwoPI}}}}, + {"mapping/hXYA", + "FT0A channel positions;x (cm);y (cm)", + {HistType::kTH2F, {{24, -18., 18.}, {24, -18., 18.}}}}, + + {"mapping/hPhiC", + "FT0C #varphi;#varphi;counts", + {HistType::kTH1F, {{18, 0., o2::constants::math::TwoPI}}}}, + {"mapping/hEtaC", + "FT0C #eta;#eta;counts", + {HistType::kTH1F, {{8, -3.5, -2.0}}}}, + {"mapping/hEtaPhiC", + "FT0C #eta versus #varphi;#eta;#varphi", + {HistType::kTH2F, {{8, -3.5, -2.0}, {18, 0., o2::constants::math::TwoPI}}}}, + {"mapping/hXYC", + "FT0C channel positions;x (cm);y (cm)", + {HistType::kTH2F, {{24, -18., 18.}, {24, -18., 18.}}}}, }}; void init(InitContext&) { - // UDHelpers calls calculateChannelCenter() inside, but doing it once here is fine. - ft0Det.calculateChannelCenter(); + if (whichThr.value != kThr1Selector && whichThr.value != kThr2Selector) { + LOGF(fatal, + "Invalid whichThr=%d: allowed values are 1 and 2", + whichThr.value); + } + + ft0Geometry.calculateChannelCenter(); + } + + template + udhelpers::Bits256 getSelectedBits(T const& row) const + { + if (whichThr.value == kThr2Selector) { + return udhelpers::makeBits256( + row.thr2W0(), row.thr2W1(), row.thr2W2(), row.thr2W3()); + } + + return udhelpers::makeBits256( + row.thr1W0(), row.thr1W1(), row.thr1W2(), row.thr1W3()); } - void process(aod::UDCollisionFITBits const& bitsTable) + void process(aod::UDCollisions::iterator const&, + aod::UDCollisionFITBits const& fitBits) { - int64_t nProcessed = 0; + // Because UDCollisionFITBits contains UDCollisionId, the framework + // supplies only the FIT-bit rows associated with the current collision. + registry.fill(HIST("association/hRowsPerCollision"), fitBits.size()); + + // The producer is expected to write exactly one FIT-bit row per collision. + if (fitBits.size() != 1) { + return; + } + + const auto row = fitBits.begin(); + const auto bits = getSelectedBits(row); - for (auto const& row : bitsTable) { - if (maxEvents >= 0 && nProcessed >= maxEvents) { - break; + int nFT0A = 0; + int nFT0C = 0; + int nFV0A = 0; + + // Packed layout: + // 0--95: FT0A + // 96--207: FT0C + // 208--255: FV0A + for (int bit = 0; bit < udhelpers::kTotalBits; ++bit) { + if (!udhelpers::testBit(bits, bit)) { + continue; + } + + registry.fill(HIST("occupancy/hPackedBit"), bit); + + const auto decoded = udhelpers::decodeFitBit(bit); + + if (decoded.det == udhelpers::FitBitRef::Det::FV0) { + ++nFV0A; + registry.fill(HIST("occupancy/hFV0AChannel"), decoded.ch); + continue; + } + + if (decoded.det != udhelpers::FitBitRef::Det::FT0) { + continue; } - ++nProcessed; - // Use udhelpers' canonical packed type + builder - udhelpers::Bits256 w{}; - if (whichThr == Thr2Selector) { - w = udhelpers::makeBits256(row.thr2W0(), row.thr2W1(), row.thr2W2(), row.thr2W3()); + if (decoded.isC) { + ++nFT0C; + registry.fill( + HIST("occupancy/hFT0CChannel"), + decoded.ch - udhelpers::kFT0AChannels); } else { - w = udhelpers::makeBits256(row.thr1W0(), row.thr1W1(), row.thr1W2(), row.thr1W3()); + ++nFT0A; + registry.fill(HIST("occupancy/hFT0AChannel"), decoded.ch); + } + + double phi = 0.; + double eta = 0.; + if (!udhelpers::getPhiEtaFromFitBit( + ft0Geometry, bit, offsetFT0, kRunOffsetIndex, phi, eta)) { + continue; } - // Loop FT0 bits only (0..207). FV0 starts at 208 but ignored here. - for (int bit = 0; bit < udhelpers::kFT0Bits; ++bit) { - if (!udhelpers::testBit(w, bit)) { - continue; - } - - double phi = 0., eta = 0.; - const bool ok = udhelpers::getPhiEtaFromFitBit(ft0Det, bit, offsetFT0, iRunOffset, phi, eta); - if (!ok) { - continue; - } - - if (bit < udhelpers::kFT0AChannels) { - registry.fill(HIST("hPhiA"), phi); - registry.fill(HIST("hEtaA"), eta); - registry.fill(HIST("hEtaPhiA"), eta, phi); - } else { - registry.fill(HIST("hPhiC"), phi); - registry.fill(HIST("hEtaC"), eta); - registry.fill(HIST("hEtaPhiC"), eta, phi); - } + const auto position = ft0Geometry.getChannelCenter(decoded.ch); + const double x = position.X() + offsetFT0[kRunOffsetIndex].getX(); + const double y = position.Y() + offsetFT0[kRunOffsetIndex].getY(); + + if (decoded.isC) { + registry.fill(HIST("mapping/hPhiC"), phi); + registry.fill(HIST("mapping/hEtaC"), eta); + registry.fill(HIST("mapping/hEtaPhiC"), eta, phi); + registry.fill(HIST("mapping/hXYC"), x, y); + } else { + registry.fill(HIST("mapping/hPhiA"), phi); + registry.fill(HIST("mapping/hEtaA"), eta); + registry.fill(HIST("mapping/hEtaPhiA"), eta, phi); + registry.fill(HIST("mapping/hXYA"), x, y); } } + + registry.fill(HIST("multiplicity/hFT0A"), nFT0A); + registry.fill(HIST("multiplicity/hFT0C"), nFT0C); + registry.fill(HIST("multiplicity/hFV0A"), nFV0A); + registry.fill(HIST("multiplicity/hFT0AVsFT0C"), nFT0A, nFT0C); } }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(cfgc)}; } From 0c1614c67a34ccbcc1839a90ae3e0d950a65fe61 Mon Sep 17 00:00:00 2001 From: sandor-lokos Date: Thu, 8 Oct 2026 12:15:21 +0200 Subject: [PATCH 5/5] [PWGUD] Add FIT bit table converter --- PWGUD/TableProducer/Converters/CMakeLists.txt | 4 ++ .../UDCollisionFITBitsConverter.cxx | 61 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 PWGUD/TableProducer/Converters/UDCollisionFITBitsConverter.cxx diff --git a/PWGUD/TableProducer/Converters/CMakeLists.txt b/PWGUD/TableProducer/Converters/CMakeLists.txt index 7d5a6297ba1..9fd7c5b02e9 100644 --- a/PWGUD/TableProducer/Converters/CMakeLists.txt +++ b/PWGUD/TableProducer/Converters/CMakeLists.txt @@ -19,6 +19,10 @@ o2physics_add_dpl_workflow(collisions-converter SOURCES UDCollisionsConverter.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(collision-fit-bits-converter + SOURCES UDCollisionFITBitsConverter.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(collisionselextras-converter SOURCES UDCollisionSelExtrasConverter.cxx diff --git a/PWGUD/TableProducer/Converters/UDCollisionFITBitsConverter.cxx b/PWGUD/TableProducer/Converters/UDCollisionFITBitsConverter.cxx new file mode 100644 index 00000000000..82be0196959 --- /dev/null +++ b/PWGUD/TableProducer/Converters/UDCollisionFITBitsConverter.cxx @@ -0,0 +1,61 @@ +// 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 UDCollisionFITBitsConverter.cxx +/// \brief Converts UDCollisionFITBits from version 000 to 001 +/// +/// The legacy table was written with exactly one FIT-bit row for every +/// UDCollision, in identical row order. Therefore row i of +/// UDCollisionFITBits_000 belongs to row i of UDCollisions. Because the +/// legacy table does not store this association explicitly, the converter +/// materialises it by writing the FIT-bit row's global index as +/// UDCollisionId. The eight packed FIT-bit words are copied unchanged. +/// +/// Executable: o2-analysis-ud-collision-fit-bits-converter +/// +/// \author Sandor Lokos, sandor.lokos@cern.ch + +#include "PWGUD/DataModel/UDTables.h" + +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +struct UDCollisionFITBitsConverter { + Produces outputFITBits; + + void process(aod::UDCollisionFITBits_000 const& legacyFITBits) + { + outputFITBits.reserve(legacyFITBits.size()); + + for (const auto& bits : legacyFITBits) { + outputFITBits(bits.globalIndex(), + bits.thr1W0(), + bits.thr1W1(), + bits.thr1W2(), + bits.thr1W3(), + bits.thr2W0(), + bits.thr2W1(), + bits.thr2W2(), + bits.thr2W3()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +}