From 5dfb7d76780f9464cc6f1b32e8c7b7eb28b08912 Mon Sep 17 00:00:00 2001 From: sandor-lokos Date: Tue, 30 Jun 2026 16:02:38 +0200 Subject: [PATCH 1/8] Updated FITbit table linked to UDCollision table; multiplicity measurement task with FIT --- PWGUD/Core/UDHelpers.h | 57 ++- PWGUD/DataModel/UDTables.h | 20 +- PWGUD/TableProducer/SGCandProducer.cxx | 3 +- PWGUD/Tasks/CMakeLists.txt | 5 + PWGUD/Tasks/upcFITMultiplicity.cxx | 479 +++++++++++++++++++++++++ PWGUD/Tasks/upcTestFitBitMapping.cxx | 405 +++++++++++++++++---- 6 files changed, 890 insertions(+), 79 deletions(-) create mode 100644 PWGUD/Tasks/upcFITMultiplicity.cxx diff --git a/PWGUD/Core/UDHelpers.h b/PWGUD/Core/UDHelpers.h index ac3c5cf8b18..9e1384ecef2 100644 --- a/PWGUD/Core/UDHelpers.h +++ b/PWGUD/Core/UDHelpers.h @@ -24,6 +24,7 @@ #include #include +#include "DataFormatsFT0/Digit.h" #include #include #include @@ -543,12 +544,12 @@ bool FITveto(T const& bc, DGCutparHolder const& diffCuts) inline void setBit(uint64_t w[4], int bit, bool val) { - if (!val) { - return; - } - const int word = bit >> 6; - const int offs = bit & 63; - w[word] |= (static_cast(1) << offs); + if (!val) { + return; + } + const int word = bit >> 6; + const int offs = bit & 63; + w[word] |= (static_cast(1) << offs); } template @@ -565,27 +566,51 @@ 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]; // physical FT0C channel id + + 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); } } @@ -664,7 +689,7 @@ inline double getEtaFT0_fromChannel(FT0DetT& ft0Det, int ft0Ch, OffsetsT const& double y = chPos.Y() + offsetFT0[i].getY(); double z = chPos.Z() + offsetFT0[i].getZ(); - // If this channel belongs to FT0C, flip z (matches your original intent) + // If this channel belongs to FT0C, flip z const bool isC = (ft0Ch >= kFT0AChannels); if (isC) { z = -z; diff --git a/PWGUD/DataModel/UDTables.h b/PWGUD/DataModel/UDTables.h index afb7728a5ab..3ab907da107 100644 --- a/PWGUD/DataModel/UDTables.h +++ b/PWGUD/DataModel/UDTables.h @@ -410,6 +410,9 @@ DECLARE_SOA_TABLE(UDTracksLabels, "AOD", "UDTRACKLABEL", namespace udcollfitbits { + +DECLARE_SOA_INDEX_COLUMN(UDCollision, udCollision); /// Link to the UDCOLLISION table, only in version 1 + 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 @@ -421,7 +424,7 @@ DECLARE_SOA_COLUMN(Thr2W2, thr2W2, uint64_t); /// 2 MIP thresholds for FT0C ch 3 DECLARE_SOA_COLUMN(Thr2W3, thr2W3, uint64_t); /// 2 MIP thresholds for FT0C ch 97 - 112 & 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 @@ -433,6 +436,21 @@ DECLARE_SOA_TABLE(UDCollisionFITBits, "AOD", "UDCOLLFITBITS", udcollfitbits::Thr2W3 /// 2 MIP thresholds for FT0C ch 97 - 112 & FV0 0 - 47 ); +DECLARE_SOA_TABLE_VERSIONED(UDCollisionFITBits_001, "AOD", "UDCOLLFITBITS", 1 + o2::soa::Index<>, + udcollfitbits::UDCollisionId, /// Link to the UDCOLLISION table <---- EXTRA + 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 +); + +using UDCollisionFITBits = UDCollisionFITBits_001; + using UDTrack = UDTracks::iterator; using UDTrackCov = UDTracksCov::iterator; using UDTrackExtra = UDTracksExtra::iterator; diff --git a/PWGUD/TableProducer/SGCandProducer.cxx b/PWGUD/TableProducer/SGCandProducer.cxx index a45493d792e..716b4b57b27 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()) { diff --git a/PWGUD/Tasks/CMakeLists.txt b/PWGUD/Tasks/CMakeLists.txt index 0b4af02e173..cc552d2edcd 100644 --- a/PWGUD/Tasks/CMakeLists.txt +++ b/PWGUD/Tasks/CMakeLists.txt @@ -298,6 +298,11 @@ o2physics_add_dpl_workflow(upc-test-fit-bit-mapping SOURCES upcTestFitBitMapping.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(fit-multiplicity + SOURCES upcFITMultiplicity.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(pt-spectra-inclusive-upc SOURCES ptSpectraInclusiveUpc.cxx diff --git a/PWGUD/Tasks/upcFITMultiplicity.cxx b/PWGUD/Tasks/upcFITMultiplicity.cxx new file mode 100644 index 00000000000..25b1d5c910a --- /dev/null +++ b/PWGUD/Tasks/upcFITMultiplicity.cxx @@ -0,0 +1,479 @@ +// 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. +// +// \brief FIT bits to phi, eta mapping + multiplicity from FIT bits +// \author Sandor Lokos, sandor.lokos@cern.ch +// \since March 2026 + +#include "PWGUD/Core/SGSelector.h" +#include "PWGUD/Core/UDHelpers.h" +#include "PWGUD/DataModel/UDTables.h" + +#include "FT0Base/Geometry.h" +#include "Framework/AnalysisTask.h" +#include "Framework/HistogramRegistry.h" +#include "Framework/Logger.h" +#include "Framework/runDataProcessing.h" + +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +using UDCollisionsFull = soa::Join< + aod::UDCollisions, + aod::UDCollisionsSels, + aod::UDCollisionSelExtras, + aod::UDZdcsReduced>; + +struct UpcTestFITBitMapping { + + SGSelector sgSelector; + + Configurable whichThr{"whichThr", 1, "Use 1=Thr1 bits or 2=Thr2 bits"}; + Configurable maxEvents{"maxEvents", -1, "Process at most this many collisions (-1 = all)"}; + Configurable debugPrintEvery{"debugPrintEvery", 1000, "Print every N events (-1 disables)"}; + Configurable debugPrintFirst{"debugPrintFirst", 10, "Always print first N events"}; + Configurable useRctFlag{"useRctFlag", true, "use RCT flags for event selection"}; + Configurable cutRctFlag{"cutRctFlag", 2, "0 = off, 1 = CBT, 2 = CBT+ZDC, 3 = CBThadron, 4 = CBThadron+ZDC"}; + + Configurable applyGapSideZDCVeto{"applyGapSideZDCVeto", true, "Apply ZDC neutron veto on the gap side"}; + Configurable cutZNAnoNeutron{"cutZNAnoNeutron", 1.f, "Maximum allowed ZNA energy for zero-neutron condition"}; + Configurable cutZNCnoNeutron{"cutZNCnoNeutron", 1.f, "Maximum allowed ZNC energy for zero-neutron condition"}; + + Configurable useFT0TimeCut{"useFT0TimeCut", true, "Apply direct FT0 timing cut"}; + Configurable cutFT0TimeA{"cutFT0TimeA", 2., "FT0-A time cut [ns]"}; + Configurable cutFT0TimeC{"cutFT0TimeC", 2., "FT0-C time cut [ns]"}; + + Configurable useFT0AVeto{"useFT0AVeto", true, "Apply FT0-A total amplitude veto"}; + Configurable cutFT0AmpAVeto{"cutFT0AmpAVeto", 100., "Maximum allowed FT0-A total amplitude for veto"}; + + Configurable useFV0AVeto{"useFV0AVeto", true, "Apply FV0-A total amplitude veto"}; + Configurable cutFV0AmpAVeto{"cutFV0AmpAVeto", 50., "Maximum allowed FV0-A total amplitude for veto"}; + + struct OffsetXYZ { + double x{0.}, y{0.}, z{0.}; + double getX() const { return x; } + double getY() const { return y; } + double getZ() const { return z; } + }; + + std::array offsetFT0{}; + int iRunOffset = 0; + + o2::ft0::Geometry ft0Det{}; + + HistogramRegistry registry{ + "registry", + {{"debug/hEventCounter", "Event counter;step;events", {HistType::kTH1F, {{20, -0.5, 19.5}}}}, + {"debug/hFitBitsSize", "fitBits.size() per collision;fitBits.size();events", {HistType::kTH1F, {{10, -0.5, 9.5}}}}, + {"debug/hCollisionIndexMod", "Collision index mod 100;collision.globalIndex() % 100;events", {HistType::kTH1F, {{100, -0.5, 99.5}}}}, + {"debug/hFT0AChannelOccupancy", "FT0A fired channel occupancy;FT0A channel;counts", {HistType::kTH1F, {{97, -0.5, 96.5}}}}, + {"debug/hFT0CChannelOccupancy", "FT0C fired channel occupancy;FT0C channel;counts", {HistType::kTH1F, {{112, 95.5, 207.5}}}}, + {"debug/hFT0CChannelOccupancy_2tracks", "FT0C fired channel occupancy with 2 tracks;FT0C channel;counts", {HistType::kTH1F, {{112, 95.5, 207.5}}}}, + {"debug/hFT0CChannelOccupancy_ntracks", "FT0C fired channel occupancy with >2 tracks;FT0C channel;counts", {HistType::kTH1F, {{112, 95.5, 207.5}}}}, + + {"map/hPhiA", "FT0A #varphi;#varphi;counts", {HistType::kTH1F, {{9, 0, 2 * M_PI}}}}, + {"map/hEtaA", "FT0A #eta;#eta;counts", {HistType::kTH1F, {{7, 3.4, 4.8}}}}, + {"map/hEtaPhiA", "FT0A #eta vs #varphi;#eta;#varphi", {HistType::kTH2F, {{7, 3.4, 4.8}, {9, 0, 2 * M_PI}}}}, + + {"map/hPhiC", "FT0C #varphi;#varphi;counts", {HistType::kTH1F, {{9, 0, 2 * M_PI}}}}, + {"map/hEtaC", "FT0C #eta;#eta;counts", {HistType::kTH1F, {{5, -3.1, -2.1}}}}, + {"map/hEtaPhiC", "FT0C #eta vs #varphi;#eta;#varphi", {HistType::kTH2F, {{5, -3.1, -2.1}, {9, 0, 2 * M_PI}}}}, + + {"map/hPhiC_2tracks", "FT0C #varphi with 2 tracks;#varphi;counts", {HistType::kTH1F, {{9, 0, 2 * M_PI}}}}, + {"map/hEtaC_2tracks", "FT0C #eta with 2 tracks;#eta;counts", {HistType::kTH1F, {{5, -3.1, -2.1}}}}, + {"map/hEtaPhiC_2tracks", "FT0C #eta vs #varphi with 2 tracks;#eta;#varphi", {HistType::kTH2F, {{5, -3.1, -2.1}, {9, 0, 2 * M_PI}}}}, + + {"map/hPhiC_ntracks", "FT0C #varphi with >2 tracks;#varphi;counts", {HistType::kTH1F, {{9, 0, 2 * M_PI}}}}, + {"map/hEtaC_ntracks", "FT0C #eta with >2 tracks;#eta;counts", {HistType::kTH1F, {{5, -3.1, -2.1}}}}, + {"map/hEtaPhiC_ntracks", "FT0C #eta vs #varphi with >2 tracks;#eta;#varphi", {HistType::kTH2F, {{5, -3.1, -2.1}, {9, 0, 2 * M_PI}}}}, + + {"mult/hPnFT0A", "P(n): FT0A fired-channel multiplicity;N_{fired}^{FT0A};events", {HistType::kTH1F, {{97, -0.5, 96.5}}}}, + {"mult/hPnFT0C", "P(n): FT0C fired-channel multiplicity;N_{fired}^{FT0C};events", {HistType::kTH1F, {{50, -0.5, 49.5}}}}, + {"mult/hPnFV0A", "P(n): FV0A fired-channel multiplicity;N_{fired}^{FV0A};events", {HistType::kTH1F, {{49, -0.5, 48.5}}}}, + + {"mult/hNfiredA_vs_C", "FT0A vs FT0C fired channels;N_{fired}^{FT0A};N_{fired}^{FT0C}", {HistType::kTH2F, {{97, -0.5, 96.5}, {113, -0.5, 112.5}}}}, + + {"qaBeforeCuts/hFT0AAmplitudeVsTime", "FT0A amplitude vs time before cuts;FT0A time (ns);FT0A total amplitude", {HistType::kTH2F, {{200, -40., 40.}, {300, 0., 3000.}}}}, + {"qaBeforeCuts/hFT0CAmplitudeVsTime", "FT0C amplitude vs time before cuts;FT0C time (ns);FT0C total amplitude", {HistType::kTH2F, {{200, -40., 40.}, {300, 0., 3000.}}}}, + + {"qaBeforeCuts/hZNEnergy", "ZNA vs ZNC energy before cuts;ZNA energy;ZNC energy", {HistType::kTH2F, {{250, -5.0, 20.}, {250, -5.0, 20.}}}}, + + {"map/hXYA", "FT0A fired channels in x-y;x [cm];y [cm]", {HistType::kTH2F, {{12, -18., 18.}, {12, -18., 18.}}}}, + {"map/hXYC", "FT0C fired channels in x-y;x [cm];y [cm]", {HistType::kTH2F, {{12, -18., 18.}, {12, -18., 18.}}}}, + {"map/hXYC_2tracks", "FT0C fired channels in x-y with 2 tracks;x [cm];y [cm]", {HistType::kTH2F, {{12, -18., 18.}, {12, -18., 18.}}}}, + {"map/hXYC_ntracks", "FT0C fired channels in x-y with >2 tracks;x [cm];y [cm]", {HistType::kTH2F, {{12, -18., 18.}, {12, -18., 18.}}}}}}; + + int countParticlesInRange(udhelpers::Bits256 const& thr1, udhelpers::Bits256 const& thr2, int first, int last) + { + int n = 0; + for (int bit = first; bit <= last; ++bit) { + const bool aboveThr1 = udhelpers::testBit(thr1, bit); + const bool aboveThr2 = udhelpers::testBit(thr2, bit); + + if (aboveThr2) { + n += 2; + } else if (aboveThr1) { + n += 1; + } + } + return n; + } + + template + bool isGoodRctFlag(const C& collision) + { + switch (cutRctFlag) { + case 1: // CBT + return sgSelector.isCBTOk(collision); + case 2: // CBT + ZDC + return sgSelector.isCBTZdcOk(collision); + case 3: // CBT hadron + return sgSelector.isCBTHadronOk(collision); + case 4: // CBT hadron + ZDC + return sgSelector.isCBTHadronZdcOk(collision); + default: // no RCT cut applied + return true; + } + } + + template + bool isZeroNeutronA(const C& collision) + { + const float eZNA = collision.energyCommonZNA(); // exact accessor to be checked + return eZNA <= cutZNAnoNeutron; + } + + template + bool isZeroNeutronC(const C& collision) + { + const float eZNC = collision.energyCommonZNC(); // exact accessor to be checked + return eZNC <= cutZNCnoNeutron; + } + + template + bool collisionPassesCuts(const C& collision) + { + /* good vertex */ + if (!collision.vtxITSTPC()) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 4.); + + /* same bunch pile-up rejection */ + if (!collision.sbp()) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 5.); + + /* ITS ROF rejection */ + if (!collision.itsROFb()) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 6.); + + /* Timeframe border collision rejection */ + if (!collision.tfb()) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 7.); + + /* z-vertex cut */ + if (std::abs(collision.posZ()) > 10.0) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 8.); + + /* RCT flag check*/ + if (useRctFlag) { + if (!isGoodRctFlag(collision)) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 9.); // passed RCT flag + } + + /* FT0-A veto on total amplitude */ + if (useFT0AVeto) { + const float ampA = collision.totalFT0AmplitudeA(); + if (ampA > cutFT0AmpAVeto) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 10.); + } + + /* FV0-A veto on total amplitude */ + if (useFV0AVeto) { + const float ampA = collision.totalFV0AmplitudeA(); + if (ampA > cutFV0AmpAVeto) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 11.); + } + + /* direct FT0 timing cuts */ + if (useFT0TimeCut) { + const float tA = collision.timeFT0A(); + const float tC = collision.timeFT0C(); + + if (abs(tA) > cutFT0TimeA) { + return false; + } + if (abs(tC) > cutFT0TimeC) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 12.); + } + + int gapSide = 0; // Veto on A-side for now. + if (applyGapSideZDCVeto) { + if (gapSide == 0) { // gap-A + if (!isZeroNeutronA(collision)) { + return false; + } + } else if (gapSide == 1) { // gap-C + if (!isZeroNeutronC(collision)) { + return false; + } + } + registry.fill(HIST("debug/hEventCounter"), 13.); + } + + /* + if (applyGapSideZDCVeto) { + if (!isZeroNeutronA(collision) || !isZeroNeutronC(collision)) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 13.); + } + */ + return true; + } + + void init(InitContext&) + { + ft0Det.calculateChannelCenter(); + + LOGF(info, "UpcTestFITBitMapping initialized. whichThr=%d", static_cast(whichThr)); + + LOGF(info, "=== FT0A geometry dump ==="); + for (int ch = 0; ch < udhelpers::kFT0AChannels; ++ch) { + auto pos = ft0Det.getChannelCenter(ch); + + const double x = pos.X(); + const double y = pos.Y(); + const double z = pos.Z(); + + const double phi = RecoDecay::phi(x, y); + const double r = std::sqrt(x * x + y * y); + const double theta = std::atan2(r, z); + const double eta = -std::log(std::tan(0.5 * theta)); + + LOGF(info, "FT0A ch=%3d x=%+8.4f y=%+8.4f z=%+8.4f phi=%+8.4f eta=%+8.4f", + ch, x, y, z, phi, eta); + } + + LOGF(info, "=== FT0C geometry dump ==="); + for (int ch = udhelpers::kFT0AChannels; ch < udhelpers::kFT0Bits; ++ch) { + auto pos = ft0Det.getChannelCenter(ch); + + const double x = pos.X(); + const double y = pos.Y(); + const double z = pos.Z(); + + const double phi = RecoDecay::phi(x, y); + const double r = std::sqrt(x * x + y * y); + const double theta = std::atan2(r, z); + const double eta = -std::log(std::tan(0.5 * theta)); + + LOGF(info, "FT0C ch=%3d x=%+8.4f y=%+8.4f z=%+8.4f phi=%+8.4f eta=%+8.4f", + ch, x, y, z, phi, eta); + } + + LOGF(info, "=== end of FT0 geometry dump ==="); + } + + void process(UDCollisionsFull::iterator const& collision, aod::UDCollisionFITBits const& fitBits) + { + if (maxEvents >= 0 && collision.globalIndex() >= maxEvents) { + return; + } + + registry.fill(HIST("debug/hEventCounter"), 0.); + registry.fill(HIST("debug/hFitBitsSize"), fitBits.size()); + registry.fill(HIST("debug/hCollisionIndexMod"), collision.globalIndex() % 100); + + if (fitBits.size() == 0) { + if (collision.globalIndex() < debugPrintFirst || + (debugPrintEvery > 0 && collision.globalIndex() % debugPrintEvery == 0)) { + LOGF(info, "collision %d: fitBits.size() = 0", collision.globalIndex()); + } + registry.fill(HIST("debug/hEventCounter"), 1.); + return; + + } else if (fitBits.size() == 1) { + LOGF(debug, "collision %d: fitBits.size() = %d , as it should be", collision.globalIndex(), fitBits.size()); + registry.fill(HIST("debug/hEventCounter"), 2.); + + } else { // > 1 case + if (collision.globalIndex() < debugPrintFirst || + (debugPrintEvery > 0 && collision.globalIndex() % debugPrintEvery == 0)) { + LOGF(warn, "collision %d: fitBits.size() = %d (expected 0 or 1)", collision.globalIndex(), fitBits.size()); + } + registry.fill(HIST("debug/hEventCounter"), 3.); + return; + } + + const float ft0TimeA = collision.timeFT0A(); + const float ft0TimeC = collision.timeFT0C(); + const float ft0AmpA = collision.totalFT0AmplitudeA(); + const float ft0AmpC = collision.totalFT0AmplitudeC(); + + registry.fill(HIST("qaBeforeCuts/hFT0AAmplitudeVsTime"), ft0TimeA, ft0AmpA); + registry.fill(HIST("qaBeforeCuts/hFT0CAmplitudeVsTime"), ft0TimeC, ft0AmpC); + registry.fill(HIST("qaBeforeCuts/hZNEnergy"), collision.energyCommonZNA(), collision.energyCommonZNC()); + + /* Checking collision level cuts */ + if (!collisionPassesCuts(collision)) { + return; + } + + /* Only one row per collision is expected. */ + auto row = fitBits.begin(); + + const auto w1 = udhelpers::makeBits256(row.thr1W0(), row.thr1W1(), row.thr1W2(), row.thr1W3()); + const auto w2 = udhelpers::makeBits256(row.thr2W0(), row.thr2W1(), row.thr2W2(), row.thr2W3()); + + const int nFT0A = countParticlesInRange(w1, w2, 0, udhelpers::kFT0AChannels - 1); + const int nFT0C = countParticlesInRange(w1, w2, udhelpers::kFT0AChannels, udhelpers::kFT0Bits - 1); + const int nFV0A = countParticlesInRange(w1, w2, udhelpers::kFT0Bits, udhelpers::kTotalBits - 1); + + registry.fill(HIST("mult/hPnFT0A"), nFT0A); + registry.fill(HIST("mult/hPnFT0C"), nFT0C); + registry.fill(HIST("mult/hPnFV0A"), nFV0A); + registry.fill(HIST("mult/hNfiredA_vs_C"), nFT0A, nFT0C); + + if (collision.globalIndex() < debugPrintFirst || (debugPrintEvery > 0 && collision.globalIndex() % debugPrintEvery == 0)) { + LOGF(info, + "collision %d: fitBits.size=%d nFT0A=%d nFT0C=%d nFV0A=%d", + collision.globalIndex(), fitBits.size(), nFT0A, nFT0C, nFV0A); + } + /// Only print the first 5 entries for debugging purpose + if (collision.globalIndex() < 5) { + for (int bit = 96; bit < 208; ++bit) { + if (udhelpers::testBit(w1, bit)) { + LOGF(info, "ANALYSIS sees one fired bit %d", bit); + } + if (udhelpers::testBit(w2, bit)) { + LOGF(info, "ANALYSIS sees two fired bits %d", bit); + } + } + } + + /// Checking for special event geometry of rho (resonances that decays into 2 tracks, in general) + if (nFT0C == 2) { + for (int bit = 0; bit < udhelpers::kFT0Bits; ++bit) { + if (!udhelpers::testBit(w1, bit)) { + continue; + } + + double phi = 0., eta = 0.; + const bool ok = udhelpers::getPhiEtaFromFitBit(ft0Det, bit, offsetFT0, iRunOffset, phi, eta); + if (!ok) { + continue; + } + + auto pos = ft0Det.getChannelCenter(bit); + + const double x = pos.X() - offsetFT0[iRunOffset].getX(); + const double y = pos.Y() - offsetFT0[iRunOffset].getY(); + + if (bit >= udhelpers::kFT0AChannels) { + registry.fill(HIST("debug/hFT0CChannelOccupancy_2tracks"), bit); + registry.fill(HIST("map/hPhiC_2tracks"), phi); + registry.fill(HIST("map/hEtaC_2tracks"), eta); + registry.fill(HIST("map/hEtaPhiC_2tracks"), eta, phi); + registry.fill(HIST("map/hXYC_2tracks"), x, y); + } + } + } + + if (nFT0C > 2) { + for (int bit = 0; bit < udhelpers::kFT0Bits; ++bit) { + if (!udhelpers::testBit(w1, bit)) { + continue; + } + + double phi = 0., eta = 0.; + const bool ok = udhelpers::getPhiEtaFromFitBit(ft0Det, bit, offsetFT0, iRunOffset, phi, eta); + if (!ok) { + continue; + } + + auto pos = ft0Det.getChannelCenter(bit); + + const double x = pos.X() - offsetFT0[iRunOffset].getX(); + const double y = pos.Y() - offsetFT0[iRunOffset].getY(); + + if (bit >= udhelpers::kFT0AChannels) { + registry.fill(HIST("debug/hFT0CChannelOccupancy_ntracks"), bit); + registry.fill(HIST("map/hPhiC_ntracks"), phi); + registry.fill(HIST("map/hEtaC_ntracks"), eta); + registry.fill(HIST("map/hEtaPhiC_ntracks"), eta, phi); + registry.fill(HIST("map/hXYC_ntracks"), x, y); + } + } + } + + /* Mapping for at least 1 fired channel only */ + for (int bit = 0; bit < udhelpers::kFT0Bits; ++bit) { + if (!udhelpers::testBit(w1, bit)) { + continue; + } + + double phi = 0., eta = 0.; + const bool ok = udhelpers::getPhiEtaFromFitBit(ft0Det, bit, offsetFT0, iRunOffset, phi, eta); + if (!ok) { + continue; + } + + auto pos = ft0Det.getChannelCenter(bit); + + const double x = pos.X() - offsetFT0[iRunOffset].getX(); + const double y = pos.Y() - offsetFT0[iRunOffset].getY(); + + if (bit < udhelpers::kFT0AChannels) { + // LOGF(info,"phi = %.4f, phiBin = %i",phi,phiBin); + registry.fill(HIST("debug/hFT0AChannelOccupancy"), bit); + registry.fill(HIST("map/hPhiA"), phi); + registry.fill(HIST("map/hEtaA"), eta); + registry.fill(HIST("map/hEtaPhiA"), eta, phi); + registry.fill(HIST("map/hXYA"), x, y); + } else { + registry.fill(HIST("debug/hFT0CChannelOccupancy"), bit); + registry.fill(HIST("map/hPhiC"), phi); + registry.fill(HIST("map/hEtaC"), eta); + registry.fill(HIST("map/hEtaPhiC"), eta, phi); + registry.fill(HIST("map/hXYC"), x, y); + } + } + + registry.fill(HIST("debug/hEventCounter"), 4.); + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc, TaskName{"fitbitmapping"})}; +} diff --git a/PWGUD/Tasks/upcTestFitBitMapping.cxx b/PWGUD/Tasks/upcTestFitBitMapping.cxx index bcee34c60fc..91f91e67411 100644 --- a/PWGUD/Tasks/upcTestFitBitMapping.cxx +++ b/PWGUD/Tasks/upcTestFitBitMapping.cxx @@ -9,113 +9,396 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // +// \brief FIT bits to phi, eta mapping + multiplicity from FIT bits +// \author Sandor Lokos, sandor.lokos@cern.ch +// \since March 2026 -/// \file upcTestFitBitMapping.cxx -/// \brief FIT bits to phi, eta mapping -/// \author Sandor Lokos, sandor.lokos@cern.ch -/// \since March 2026 +#include "PWGUD/Core/SGSelector.h" +#include "PWGUD/Core/UDHelpers.h" +#include "PWGUD/DataModel/UDTables.h" -#include "PWGUD/Core/UDHelpers.h" // udhelpers::Bits256, makeBits256, testBit, getPhiEtaFromFitBit -#include "PWGUD/DataModel/UDTables.h" // aod::UDCollisionFITBits - -#include // o2::constants::math::TwoPI -#include // o2::ft0::Geometry -#include -#include -#include -#include -#include -#include +#include "FT0Base/Geometry.h" +#include "Framework/AnalysisTask.h" +#include "Framework/HistogramRegistry.h" +#include "Framework/Logger.h" +#include "Framework/runDataProcessing.h" #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 +using UDCollisionsFull = soa::Join< + aod::UDCollisions, + aod::UDCollisionsSels, + aod::UDCollisionSelExtras, + aod::UDZdcsReduced>; + +// using UDCollisionsFull = aod::UDCollisions; + +struct UpcTestFITBitMapping { + + SGSelector sgSelector; 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 maxEvents{"maxEvents", -1, "Process at most this many collisions (-1 = all)"}; + Configurable debugPrintEvery{"debugPrintEvery", 1000, "Print every N events (-1 disables)"}; + Configurable debugPrintFirst{"debugPrintFirst", 10, "Always print first N events"}; + Configurable useRctFlag{"useRctFlag", true, "use RCT flags for event selection"}; + Configurable cutRctFlag{"cutRctFlag", 2, "0 = off, 1 = CBT, 2 = CBT+ZDC, 3 = CBThadron, 4 = CBThadron+ZDC"}; + + Configurable applyGapSideZDCVeto{"applyGapSideZDCVeto", true, "Apply ZDC neutron veto on the gap side"}; + Configurable cutZNAnoNeutron{"cutZNAnoNeutron", 1.f, "Maximum allowed ZNA energy for zero-neutron condition"}; + Configurable cutZNCnoNeutron{"cutZNCnoNeutron", 1.f, "Maximum allowed ZNC energy for zero-neutron condition"}; + + Configurable useFT0TimeCut{"useFT0TimeCut", true, "Apply direct FT0 timing cut"}; + Configurable cutFT0TimeA{"cutFT0TimeA", 2., "FT0-A time cut [ns]"}; + Configurable cutFT0TimeC{"cutFT0TimeC", 2., "FT0-C time cut [ns]"}; + + Configurable useFT0AVeto{"useFT0AVeto", true, "Apply FT0-A total amplitude veto"}; + Configurable cutFT0AmpAVeto{"cutFT0AmpAVeto", 100., "Maximum allowed FT0-A total amplitude for veto"}; + + Configurable useFV0AVeto{"useFV0AVeto", true, "Apply FV0-A total amplitude veto"}; + Configurable cutFV0AmpAVeto{"cutFV0AmpAVeto", 50., "Maximum allowed FV0-A total amplitude for veto"}; - // Minimal offset container compatible with UDHelpers.h expectations: getX/getY/getZ struct OffsetXYZ { - double x{0}, y{0}, z{0}; + double x{0.}, y{0.}, z{0.}; double getX() const { return x; } double getY() const { return y; } double getZ() const { return z; } }; - std::array offsetFT0{}; // iRunOffset = 0 for now + std::array offsetFT0{}; int iRunOffset = 0; o2::ft0::Geometry ft0Det{}; 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}}}}, + {{"debug/hEventCounter", "Event counter;step;events", {HistType::kTH1F, {{20, -0.5, 19.5}}}}, + {"debug/hFitBitsSize", "fitBits.size() per collision;fitBits.size();events", {HistType::kTH1F, {{10, -0.5, 9.5}}}}, + {"debug/hCollisionIndexMod", "Collision index mod 100;collision.globalIndex() % 100;events", {HistType::kTH1F, {{100, -0.5, 99.5}}}}, + {"debug/hFT0AChannelOccupancy", "FT0A fired channel occupancy;FT0A channel;counts", {HistType::kTH1F, {{97, -0.5, 96.5}}}}, + {"debug/hFT0CChannelOccupancy", "FT0C fired channel occupancy;FT0C channel;counts", {HistType::kTH1F, {{112, 95.5, 207.5}}}}, + + {"map/hPhiA", "FT0A #varphi;#varphi;counts", {HistType::kTH1F, {{9, 0, 2 * M_PI}}}}, + {"map/hEtaA", "FT0A #eta;#eta;counts", {HistType::kTH1F, {{7, 3.4, 4.8}}}}, + {"map/hEtaPhiA", "FT0A #eta vs #varphi;#eta;#varphi", {HistType::kTH2F, {{7, 3.4, 4.8}, {9, 0, 2 * M_PI}}}}, + + {"map/hPhiC", "FT0C #varphi;#varphi;counts", {HistType::kTH1F, {{9, 0, 2 * M_PI}}}}, + {"map/hEtaC", "FT0C #eta;#eta;counts", {HistType::kTH1F, {{5, -3.1, -2.1}}}}, + {"map/hEtaPhiC", "FT0C #eta vs #varphi;#eta;#varphi", {HistType::kTH2F, {{5, -3.1, -2.1}, {9, 0, 2 * M_PI}}}}, + + {"mult/hPnFT0A", "P(n): FT0A fired-channel multiplicity;N_{fired}^{FT0A};events", {HistType::kTH1F, {{97, -0.5, 96.5}}}}, + {"mult/hPnFT0C", "P(n): FT0C fired-channel multiplicity;N_{fired}^{FT0C};events", {HistType::kTH1F, {{50, -0.5, 49.5}}}}, + {"mult/hPnFV0A", "P(n): FV0A fired-channel multiplicity;N_{fired}^{FV0A};events", {HistType::kTH1F, {{49, -0.5, 48.5}}}}, + + {"mult/hNfiredA_vs_C", "FT0A vs FT0C fired channels;N_{fired}^{FT0A};N_{fired}^{FT0C}", {HistType::kTH2F, {{97, -0.5, 96.5}, {113, -0.5, 112.5}}}}, + + {"qaBeforeCuts/hFT0AAmplitudeVsTime", "FT0A amplitude vs time before cuts;FT0A time (ns);FT0A total amplitude", {HistType::kTH2F, {{200, -40., 40.}, {300, 0., 3000.}}}}, + {"qaBeforeCuts/hFT0CAmplitudeVsTime", "FT0C amplitude vs time before cuts;FT0C time (ns);FT0C total amplitude", {HistType::kTH2F, {{200, -40., 40.}, {300, 0., 3000.}}}}, + + {"map/hXYA", "FT0A fired channels in x-y;x [cm];y [cm]", {HistType::kTH2F, {{12, -18., 18.}, {12, -18., 18.}}}}, + {"map/hXYC", "FT0C fired channels in x-y;x [cm];y [cm]", {HistType::kTH2F, {{12, -18., 18.}, {12, -18., 18.}}}}}}; + + int countParticlesInRange(udhelpers::Bits256 const& thr1, udhelpers::Bits256 const& thr2, int first, int last) + { + int n = 0; + for (int bit = first; bit <= last; ++bit) { + const bool aboveThr1 = udhelpers::testBit(thr1, bit); + const bool aboveThr2 = udhelpers::testBit(thr2, bit); + + if (aboveThr2) { + n += 2; + } else if (aboveThr1) { + n += 1; + } + } + return n; + } + + template + bool isGoodRctFlag(const C& collision) + { + switch (cutRctFlag) { + case 1: // CBT + return sgSelector.isCBTOk(collision); + case 2: // CBT + ZDC + return sgSelector.isCBTZdcOk(collision); + case 3: // CBT hadron + return sgSelector.isCBTHadronOk(collision); + case 4: // CBT hadron + ZDC + return sgSelector.isCBTHadronZdcOk(collision); + default: // no RCT cut applied + return true; + } + } + + template + bool isZeroNeutronA(const C& collision) + { + const float eZNA = collision.energyCommonZNA(); // exact accessor to be checked + return eZNA <= cutZNAnoNeutron; + } + + template + bool isZeroNeutronC(const C& collision) + { + const float eZNC = collision.energyCommonZNC(); // exact accessor to be checked + return eZNC <= cutZNCnoNeutron; + } + + template + bool collisionPassesCuts(const C& collision) + { + /* good vertex */ + if (!collision.vtxITSTPC()) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 4.); + + /* same bunch pile-up rejection */ + if (!collision.sbp()) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 5.); + + /* ITS ROF rejection */ + if (!collision.itsROFb()) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 6.); + + /* Timeframe border collision rejection */ + if (!collision.tfb()) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 7.); + + /* z-vertex cut */ + if (std::abs(collision.posZ()) > 10.0) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 8.); + + /* RCT flag check*/ + if (useRctFlag) { + if (!isGoodRctFlag(collision)) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 9.); // passed RCT flag + } + + /* FT0-A veto on total amplitude */ + if (useFT0AVeto) { + const float ampA = collision.totalFT0AmplitudeA(); + if (ampA > cutFT0AmpAVeto) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 10.); + } + + /* FV0-A veto on total amplitude */ + if (useFV0AVeto) { + const float ampA = collision.totalFV0AmplitudeA(); + if (ampA > cutFV0AmpAVeto) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 11.); + } - {"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}}}}, - }}; + /* direct FT0 timing cuts */ + if (useFT0TimeCut) { + const float tA = collision.timeFT0A(); + const float tC = collision.timeFT0C(); + + if (abs(tA) > cutFT0TimeA) { + return false; + } + if (abs(tC) > cutFT0TimeC) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 12.); + } + + /* + int gapSide = 0 ; // Veto on A-side for now. + if (applyGapSideZDCVeto) { + if (gapSide == 0) { // gap-A + if (!isZeroNeutronA(collision)) { + return false; + } + } else if (gapSide == 1) { // gap-C + if (!isZeroNeutronC(collision)) { + return false; + } + } + registry.fill(HIST("debug/hEventCounter"), 13.); + } + */ + + if (applyGapSideZDCVeto) { + if (!isZeroNeutronA(collision) || !isZeroNeutronC(collision)) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 13.); + } + + return true; + } void init(InitContext&) { - // UDHelpers calls calculateChannelCenter() inside, but doing it once here is fine. ft0Det.calculateChannelCenter(); + + LOGF(info, "UpcTestFITBitMapping initialized. whichThr=%d", static_cast(whichThr)); + + LOGF(info, "=== FT0A geometry dump ==="); + for (int ch = 0; ch < udhelpers::kFT0AChannels; ++ch) { + auto pos = ft0Det.getChannelCenter(ch); + + const double x = pos.X(); + const double y = pos.Y(); + const double z = pos.Z(); + + const double phi = RecoDecay::phi(x, y); + const double r = std::sqrt(x * x + y * y); + const double theta = std::atan2(r, z); + const double eta = -std::log(std::tan(0.5 * theta)); + + LOGF(info, "FT0A ch=%3d x=%+8.4f y=%+8.4f z=%+8.4f phi=%+8.4f eta=%+8.4f", + ch, x, y, z, phi, eta); + } + + LOGF(info, "=== FT0C geometry dump ==="); + for (int ch = udhelpers::kFT0AChannels; ch < udhelpers::kFT0Bits; ++ch) { + auto pos = ft0Det.getChannelCenter(ch); + + const double x = pos.X(); + const double y = pos.Y(); + const double z = pos.Z(); + + const double phi = RecoDecay::phi(x, y); + const double r = std::sqrt(x * x + y * y); + const double theta = std::atan2(r, z); + const double eta = -std::log(std::tan(0.5 * theta)); + + LOGF(info, "FT0C ch=%3d x=%+8.4f y=%+8.4f z=%+8.4f phi=%+8.4f eta=%+8.4f", + ch, x, y, z, phi, eta); + } + + LOGF(info, "=== end of FT0 geometry dump ==="); } - void process(aod::UDCollisionFITBits const& bitsTable) + void process(UDCollisionsFull::iterator const& collision, aod::UDCollisionFITBits const& fitBits) { - int64_t nProcessed = 0; + if (maxEvents >= 0 && collision.globalIndex() >= maxEvents) { + return; + } - for (auto const& row : bitsTable) { - if (maxEvents >= 0 && nProcessed >= maxEvents) { - break; + registry.fill(HIST("debug/hEventCounter"), 0.); + registry.fill(HIST("debug/hFitBitsSize"), fitBits.size()); + registry.fill(HIST("debug/hCollisionIndexMod"), collision.globalIndex() % 100); + + if (fitBits.size() == 0) { + if (collision.globalIndex() < debugPrintFirst || + (debugPrintEvery > 0 && collision.globalIndex() % debugPrintEvery == 0)) { + LOGF(info, "collision %d: fitBits.size() = 0", collision.globalIndex()); } - ++nProcessed; + registry.fill(HIST("debug/hEventCounter"), 1.); + return; - // Use udhelpers' canonical packed type + builder - udhelpers::Bits256 w{}; - if (whichThr == Thr2Selector) { - w = udhelpers::makeBits256(row.thr2W0(), row.thr2W1(), row.thr2W2(), row.thr2W3()); - } else { - w = udhelpers::makeBits256(row.thr1W0(), row.thr1W1(), row.thr1W2(), row.thr1W3()); + } else if (fitBits.size() == 1) { + LOGF(debug, "collision %d: fitBits.size() = %d , as it should be", collision.globalIndex(), fitBits.size()); + registry.fill(HIST("debug/hEventCounter"), 2.); + + } else { // > 1 case + if (collision.globalIndex() < debugPrintFirst || + (debugPrintEvery > 0 && collision.globalIndex() % debugPrintEvery == 0)) { + LOGF(warn, "collision %d: fitBits.size() = %d (expected 0 or 1)", collision.globalIndex(), fitBits.size()); } + registry.fill(HIST("debug/hEventCounter"), 3.); + return; + } - // 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; - } + /* Checking collision level cuts */ + if (!collisionPassesCuts(collision)) { + return; + } - double phi = 0., eta = 0.; - const bool ok = udhelpers::getPhiEtaFromFitBit(ft0Det, bit, offsetFT0, iRunOffset, phi, eta); - if (!ok) { - continue; - } + /* Only one row per collision is expected. */ + auto row = fitBits.begin(); + + const auto w1 = udhelpers::makeBits256(row.thr1W0(), row.thr1W1(), row.thr1W2(), row.thr1W3()); + const auto w2 = udhelpers::makeBits256(row.thr2W0(), row.thr2W1(), row.thr2W2(), row.thr2W3()); - 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 int nFT0A = countParticlesInRange(w1, w2, 0, udhelpers::kFT0AChannels - 1); + const int nFT0C = countParticlesInRange(w1, w2, udhelpers::kFT0AChannels, udhelpers::kFT0Bits - 1); + const int nFV0A = countParticlesInRange(w1, w2, udhelpers::kFT0Bits, udhelpers::kTotalBits - 1); + + registry.fill(HIST("mult/hPnFT0A"), nFT0A); + registry.fill(HIST("mult/hPnFT0C"), nFT0C); + registry.fill(HIST("mult/hPnFV0A"), nFV0A); + registry.fill(HIST("mult/hNfiredA_vs_C"), nFT0A, nFT0C); + + if (collision.globalIndex() < debugPrintFirst || + (debugPrintEvery > 0 && collision.globalIndex() % debugPrintEvery == 0)) { + LOGF(info, + "collision %d: fitBits.size=%d nFT0A=%d nFT0C=%d nFV0A=%d", + collision.globalIndex(), fitBits.size(), nFT0A, nFT0C, nFV0A); + } + if (collision.globalIndex() < 5) { + for (int bit = 96; bit < 208; ++bit) { + if (udhelpers::testBit(w1, bit)) { + LOGF(info, "ANALYSIS sees one fired bit %d", bit); } + if (udhelpers::testBit(w2, bit)) { + LOGF(info, "ANALYSIS sees two fired bits %d", bit); + } + } + } + + /* Mapping for at least 1 fired channel only */ + for (int bit = 0; bit < udhelpers::kFT0Bits; ++bit) { + if (!udhelpers::testBit(w1, bit)) { + continue; + } + + double phi = 0., eta = 0.; + const bool ok = udhelpers::getPhiEtaFromFitBit(ft0Det, bit, offsetFT0, iRunOffset, phi, eta); + if (!ok) { + continue; + } + + auto pos = ft0Det.getChannelCenter(bit); + + const double x = pos.X() - offsetFT0[iRunOffset].getX(); + const double y = pos.Y() - offsetFT0[iRunOffset].getY(); + + if (bit < udhelpers::kFT0AChannels) { + // LOGF(info,"phi = %.4f, phiBin = %i",phi,phiBin); + registry.fill(HIST("debug/hFT0AChannelOccupancy"), bit); + registry.fill(HIST("map/hPhiA"), phi); + registry.fill(HIST("map/hEtaA"), eta); + registry.fill(HIST("map/hEtaPhiA"), eta, phi); + registry.fill(HIST("map/hXYA"), x, y); + } else { + registry.fill(HIST("debug/hFT0CChannelOccupancy"), bit); + registry.fill(HIST("map/hPhiC"), phi); + registry.fill(HIST("map/hEtaC"), eta); + registry.fill(HIST("map/hEtaPhiC"), eta, phi); + registry.fill(HIST("map/hXYC"), x, y); } } + + registry.fill(HIST("debug/hEventCounter"), 4.); } }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc, TaskName{"fitbitmapping"})}; } From c50212658db2e70e52a94815b102a55cc918162d Mon Sep 17 00:00:00 2001 From: sandor-lokos Date: Tue, 7 Jul 2026 22:56:59 +0200 Subject: [PATCH 2/8] Converter implemented for FITbit tables version 000 to 001; bug fix in the new FITbit table --- PWGUD/DataModel/UDTables.h | 2 +- PWGUD/TableProducer/Converters/CMakeLists.txt | 5 ++ .../Converters/UDFITbitConverter.cxx | 63 +++++++++++++++++++ PWGUD/Tasks/upcFITMultiplicity.cxx | 10 ++- 4 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 PWGUD/TableProducer/Converters/UDFITbitConverter.cxx diff --git a/PWGUD/DataModel/UDTables.h b/PWGUD/DataModel/UDTables.h index 3ab907da107..20c9e502e8d 100644 --- a/PWGUD/DataModel/UDTables.h +++ b/PWGUD/DataModel/UDTables.h @@ -436,7 +436,7 @@ DECLARE_SOA_TABLE(UDCollisionFITBits_000, "AOD", "UDCOLLFITBITS", udcollfitbits::Thr2W3 /// 2 MIP thresholds for FT0C ch 97 - 112 & FV0 0 - 47 ); -DECLARE_SOA_TABLE_VERSIONED(UDCollisionFITBits_001, "AOD", "UDCOLLFITBITS", 1 +DECLARE_SOA_TABLE_VERSIONED(UDCollisionFITBits_001, "AOD", "UDCOLLFITBITS", 1, o2::soa::Index<>, udcollfitbits::UDCollisionId, /// Link to the UDCOLLISION table <---- EXTRA udcollfitbits::Thr1W0, /// 1 MIP thresholds for FT0A ch 0 - ch 63 diff --git a/PWGUD/TableProducer/Converters/CMakeLists.txt b/PWGUD/TableProducer/Converters/CMakeLists.txt index 7d5a6297ba1..4216b1d79c4 100644 --- a/PWGUD/TableProducer/Converters/CMakeLists.txt +++ b/PWGUD/TableProducer/Converters/CMakeLists.txt @@ -34,3 +34,8 @@ o2physics_add_dpl_workflow(collisionselextras-converter-v003 SOURCES UDCollisionSelExtrasV003Converter.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(fit-bits-converter + SOURCES UDFITbitConverter.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGUD/TableProducer/Converters/UDFITbitConverter.cxx b/PWGUD/TableProducer/Converters/UDFITbitConverter.cxx new file mode 100644 index 00000000000..e685b497176 --- /dev/null +++ b/PWGUD/TableProducer/Converters/UDFITbitConverter.cxx @@ -0,0 +1,63 @@ +// 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 table from version 000 to 001 +/// +/// This task converts the UDCollisionFITBits table from version 000, +/// without an explicit UDCollisionId link, to version 001, which includes +/// the UDCollisionId column. +/// +/// The conversion assumes the version-000 table has one row per UDCollision +/// and follows the same ordering as the UDCollisions table. +/// +/// executable name o2-analysis-ud-fit-bits-converter +/// +/// \author Sandor Lokos + +#include "PWGUD/DataModel/UDTables.h" + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +// Converts UDCollisionFITBits from version 000 to 001 +struct UDCollisionFITBitsConverter { + Produces udCollisionFITBits_001; + + void process(o2::aod::UDCollisionFITBits_000 const& fitBits) + { + udCollisionFITBits_001.reserve(fitBits.size()); + + for (const auto& fitBit : fitBits) { + udCollisionFITBits_001(fitBit.globalIndex(), // UDCollisionId; version 000 has no explicit link + fitBit.thr1W0(), + fitBit.thr1W1(), + fitBit.thr1W2(), + fitBit.thr1W3(), + fitBit.thr2W0(), + fitBit.thr2W1(), + fitBit.thr2W2(), + fitBit.thr2W3()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} \ No newline at end of file diff --git a/PWGUD/Tasks/upcFITMultiplicity.cxx b/PWGUD/Tasks/upcFITMultiplicity.cxx index 25b1d5c910a..1640c8eef3d 100644 --- a/PWGUD/Tasks/upcFITMultiplicity.cxx +++ b/PWGUD/Tasks/upcFITMultiplicity.cxx @@ -99,6 +99,9 @@ struct UpcTestFITBitMapping { {"map/hEtaC_ntracks", "FT0C #eta with >2 tracks;#eta;counts", {HistType::kTH1F, {{5, -3.1, -2.1}}}}, {"map/hEtaPhiC_ntracks", "FT0C #eta vs #varphi with >2 tracks;#eta;#varphi", {HistType::kTH2F, {{5, -3.1, -2.1}, {9, 0, 2 * M_PI}}}}, + {"mult/hPnFT0A_raw", "P(n): FT0A fired-channel multiplicity;N_{fired}^{FT0A};events", {HistType::kTH1F, {{97, -0.5, 96.5}}}}, + {"mult/hPnFT0C_raw", "P(n): FT0C fired-channel multiplicity;N_{fired}^{FT0C};events", {HistType::kTH1F, {{50, -0.5, 49.5}}}}, + {"mult/hPnFV0A_raw", "P(n): FV0A fired-channel multiplicity;N_{fired}^{FV0A};events", {HistType::kTH1F, {{49, -0.5, 48.5}}}}, {"mult/hPnFT0A", "P(n): FT0A fired-channel multiplicity;N_{fired}^{FT0A};events", {HistType::kTH1F, {{97, -0.5, 96.5}}}}, {"mult/hPnFT0C", "P(n): FT0C fired-channel multiplicity;N_{fired}^{FT0C};events", {HistType::kTH1F, {{50, -0.5, 49.5}}}}, {"mult/hPnFV0A", "P(n): FV0A fired-channel multiplicity;N_{fired}^{FV0A};events", {HistType::kTH1F, {{49, -0.5, 48.5}}}}, @@ -338,11 +341,16 @@ struct UpcTestFITBitMapping { const float ft0TimeC = collision.timeFT0C(); const float ft0AmpA = collision.totalFT0AmplitudeA(); const float ft0AmpC = collision.totalFT0AmplitudeC(); + const float fv0AmpA = collision.totalFV0AmplitudeA(); + + registry.fill(HIST("mult/hPnFT0A_raw"), ft0AmpA); + registry.fill(HIST("mult/hPnFT0C_raw"), ft0AmpC); + registry.fill(HIST("mult/hPnFV0A_raw"), fv0AmpA); registry.fill(HIST("qaBeforeCuts/hFT0AAmplitudeVsTime"), ft0TimeA, ft0AmpA); registry.fill(HIST("qaBeforeCuts/hFT0CAmplitudeVsTime"), ft0TimeC, ft0AmpC); registry.fill(HIST("qaBeforeCuts/hZNEnergy"), collision.energyCommonZNA(), collision.energyCommonZNC()); - + /* Checking collision level cuts */ if (!collisionPassesCuts(collision)) { return; From 753b84c491a96dbe430fcb6ff8a102d4523dfca8 Mon Sep 17 00:00:00 2001 From: sandor-lokos Date: Wed, 8 Jul 2026 11:59:51 +0200 Subject: [PATCH 3/8] Converter implemented for FITbit tables version 000 to 001; bug fix in the new FITbit table; formatting issues fixed --- PWGUD/Core/UDHelpers.h | 14 +- PWGUD/DataModel/UDTables.h | 19 +- .../Converters/UDFITbitConverter.cxx | 123 ++- PWGUD/Tasks/upcFITMultiplicity.cxx | 974 +++++++++--------- 4 files changed, 564 insertions(+), 566 deletions(-) diff --git a/PWGUD/Core/UDHelpers.h b/PWGUD/Core/UDHelpers.h index 9e1384ecef2..814e395a77f 100644 --- a/PWGUD/Core/UDHelpers.h +++ b/PWGUD/Core/UDHelpers.h @@ -24,7 +24,7 @@ #include #include -#include "DataFormatsFT0/Digit.h" +#include #include #include #include @@ -544,12 +544,12 @@ bool FITveto(T const& bc, DGCutparHolder const& diffCuts) inline void setBit(uint64_t w[4], int bit, bool val) { - if (!val) { - return; - } - const int word = bit >> 6; - const int offs = bit & 63; - w[word] |= (static_cast(1) << offs); + if (!val) { + return; + } + const int word = bit >> 6; + const int offs = bit & 63; + w[word] |= (static_cast(1) << offs); } template diff --git a/PWGUD/DataModel/UDTables.h b/PWGUD/DataModel/UDTables.h index 20c9e502e8d..455ddcdbe52 100644 --- a/PWGUD/DataModel/UDTables.h +++ b/PWGUD/DataModel/UDTables.h @@ -410,7 +410,6 @@ DECLARE_SOA_TABLE(UDTracksLabels, "AOD", "UDTRACKLABEL", namespace udcollfitbits { - DECLARE_SOA_INDEX_COLUMN(UDCollision, udCollision); /// Link to the UDCOLLISION table, only in version 1 DECLARE_SOA_COLUMN(Thr1W0, thr1W0, uint64_t); /// 1 MIP thresholds for FT0A ch 0 - ch 63 @@ -425,15 +424,15 @@ DECLARE_SOA_COLUMN(Thr2W3, thr2W3, uint64_t); /// 2 MIP thresholds for FT0C ch 9 } // namespace 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 + 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 ); DECLARE_SOA_TABLE_VERSIONED(UDCollisionFITBits_001, "AOD", "UDCOLLFITBITS", 1, diff --git a/PWGUD/TableProducer/Converters/UDFITbitConverter.cxx b/PWGUD/TableProducer/Converters/UDFITbitConverter.cxx index e685b497176..915892cfa19 100644 --- a/PWGUD/TableProducer/Converters/UDFITbitConverter.cxx +++ b/PWGUD/TableProducer/Converters/UDFITbitConverter.cxx @@ -1,63 +1,62 @@ -// 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 table from version 000 to 001 -/// -/// This task converts the UDCollisionFITBits table from version 000, -/// without an explicit UDCollisionId link, to version 001, which includes -/// the UDCollisionId column. -/// -/// The conversion assumes the version-000 table has one row per UDCollision -/// and follows the same ordering as the UDCollisions table. -/// -/// executable name o2-analysis-ud-fit-bits-converter -/// -/// \author Sandor Lokos - -#include "PWGUD/DataModel/UDTables.h" - -#include -#include -#include -#include - -using namespace o2; -using namespace o2::framework; - -// Converts UDCollisionFITBits from version 000 to 001 -struct UDCollisionFITBitsConverter { - Produces udCollisionFITBits_001; - - void process(o2::aod::UDCollisionFITBits_000 const& fitBits) - { - udCollisionFITBits_001.reserve(fitBits.size()); - - for (const auto& fitBit : fitBits) { - udCollisionFITBits_001(fitBit.globalIndex(), // UDCollisionId; version 000 has no explicit link - fitBit.thr1W0(), - fitBit.thr1W1(), - fitBit.thr1W2(), - fitBit.thr1W3(), - fitBit.thr2W0(), - fitBit.thr2W1(), - fitBit.thr2W2(), - fitBit.thr2W3()); - } - } -}; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - return WorkflowSpec{ - adaptAnalysisTask(cfgc), - }; +// 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 table from version 000 to 001 +/// +/// This task converts the UDCollisionFITBits table from version 000, +/// without an explicit UDCollisionId link, to version 001, which includes +/// the UDCollisionId column. +/// +/// The conversion assumes the version-000 table has one row per UDCollision +/// and follows the same ordering as the UDCollisions table. +/// +/// executable name o2-analysis-ud-fit-bits-converter +/// +/// \author Sandor Lokos + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +// Converts UDCollisionFITBits from version 000 to 001 +struct UDCollisionFITBitsConverter { + Produces udCollisionFITBits_001; + + void process(o2::aod::UDCollisionFITBits_000 const& fitBits) + { + udCollisionFITBits_001.reserve(fitBits.size()); + + for (const auto& fitBit : fitBits) { + udCollisionFITBits_001(fitBit.globalIndex(), // UDCollisionId; version 000 has no explicit link + fitBit.thr1W0(), + fitBit.thr1W1(), + fitBit.thr1W2(), + fitBit.thr1W3(), + fitBit.thr2W0(), + fitBit.thr2W1(), + fitBit.thr2W2(), + fitBit.thr2W3()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; } \ No newline at end of file diff --git a/PWGUD/Tasks/upcFITMultiplicity.cxx b/PWGUD/Tasks/upcFITMultiplicity.cxx index 1640c8eef3d..60caaeaf500 100644 --- a/PWGUD/Tasks/upcFITMultiplicity.cxx +++ b/PWGUD/Tasks/upcFITMultiplicity.cxx @@ -1,487 +1,487 @@ -// 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. -// -// \brief FIT bits to phi, eta mapping + multiplicity from FIT bits -// \author Sandor Lokos, sandor.lokos@cern.ch -// \since March 2026 - -#include "PWGUD/Core/SGSelector.h" -#include "PWGUD/Core/UDHelpers.h" -#include "PWGUD/DataModel/UDTables.h" - -#include "FT0Base/Geometry.h" -#include "Framework/AnalysisTask.h" -#include "Framework/HistogramRegistry.h" -#include "Framework/Logger.h" -#include "Framework/runDataProcessing.h" - -#include -#include -#include - -using namespace o2; -using namespace o2::framework; - -using UDCollisionsFull = soa::Join< - aod::UDCollisions, - aod::UDCollisionsSels, - aod::UDCollisionSelExtras, - aod::UDZdcsReduced>; - -struct UpcTestFITBitMapping { - - SGSelector sgSelector; - - Configurable whichThr{"whichThr", 1, "Use 1=Thr1 bits or 2=Thr2 bits"}; - Configurable maxEvents{"maxEvents", -1, "Process at most this many collisions (-1 = all)"}; - Configurable debugPrintEvery{"debugPrintEvery", 1000, "Print every N events (-1 disables)"}; - Configurable debugPrintFirst{"debugPrintFirst", 10, "Always print first N events"}; - Configurable useRctFlag{"useRctFlag", true, "use RCT flags for event selection"}; - Configurable cutRctFlag{"cutRctFlag", 2, "0 = off, 1 = CBT, 2 = CBT+ZDC, 3 = CBThadron, 4 = CBThadron+ZDC"}; - - Configurable applyGapSideZDCVeto{"applyGapSideZDCVeto", true, "Apply ZDC neutron veto on the gap side"}; - Configurable cutZNAnoNeutron{"cutZNAnoNeutron", 1.f, "Maximum allowed ZNA energy for zero-neutron condition"}; - Configurable cutZNCnoNeutron{"cutZNCnoNeutron", 1.f, "Maximum allowed ZNC energy for zero-neutron condition"}; - - Configurable useFT0TimeCut{"useFT0TimeCut", true, "Apply direct FT0 timing cut"}; - Configurable cutFT0TimeA{"cutFT0TimeA", 2., "FT0-A time cut [ns]"}; - Configurable cutFT0TimeC{"cutFT0TimeC", 2., "FT0-C time cut [ns]"}; - - Configurable useFT0AVeto{"useFT0AVeto", true, "Apply FT0-A total amplitude veto"}; - Configurable cutFT0AmpAVeto{"cutFT0AmpAVeto", 100., "Maximum allowed FT0-A total amplitude for veto"}; - - Configurable useFV0AVeto{"useFV0AVeto", true, "Apply FV0-A total amplitude veto"}; - Configurable cutFV0AmpAVeto{"cutFV0AmpAVeto", 50., "Maximum allowed FV0-A total amplitude for veto"}; - - struct OffsetXYZ { - double x{0.}, y{0.}, z{0.}; - double getX() const { return x; } - double getY() const { return y; } - double getZ() const { return z; } - }; - - std::array offsetFT0{}; - int iRunOffset = 0; - - o2::ft0::Geometry ft0Det{}; - - HistogramRegistry registry{ - "registry", - {{"debug/hEventCounter", "Event counter;step;events", {HistType::kTH1F, {{20, -0.5, 19.5}}}}, - {"debug/hFitBitsSize", "fitBits.size() per collision;fitBits.size();events", {HistType::kTH1F, {{10, -0.5, 9.5}}}}, - {"debug/hCollisionIndexMod", "Collision index mod 100;collision.globalIndex() % 100;events", {HistType::kTH1F, {{100, -0.5, 99.5}}}}, - {"debug/hFT0AChannelOccupancy", "FT0A fired channel occupancy;FT0A channel;counts", {HistType::kTH1F, {{97, -0.5, 96.5}}}}, - {"debug/hFT0CChannelOccupancy", "FT0C fired channel occupancy;FT0C channel;counts", {HistType::kTH1F, {{112, 95.5, 207.5}}}}, - {"debug/hFT0CChannelOccupancy_2tracks", "FT0C fired channel occupancy with 2 tracks;FT0C channel;counts", {HistType::kTH1F, {{112, 95.5, 207.5}}}}, - {"debug/hFT0CChannelOccupancy_ntracks", "FT0C fired channel occupancy with >2 tracks;FT0C channel;counts", {HistType::kTH1F, {{112, 95.5, 207.5}}}}, - - {"map/hPhiA", "FT0A #varphi;#varphi;counts", {HistType::kTH1F, {{9, 0, 2 * M_PI}}}}, - {"map/hEtaA", "FT0A #eta;#eta;counts", {HistType::kTH1F, {{7, 3.4, 4.8}}}}, - {"map/hEtaPhiA", "FT0A #eta vs #varphi;#eta;#varphi", {HistType::kTH2F, {{7, 3.4, 4.8}, {9, 0, 2 * M_PI}}}}, - - {"map/hPhiC", "FT0C #varphi;#varphi;counts", {HistType::kTH1F, {{9, 0, 2 * M_PI}}}}, - {"map/hEtaC", "FT0C #eta;#eta;counts", {HistType::kTH1F, {{5, -3.1, -2.1}}}}, - {"map/hEtaPhiC", "FT0C #eta vs #varphi;#eta;#varphi", {HistType::kTH2F, {{5, -3.1, -2.1}, {9, 0, 2 * M_PI}}}}, - - {"map/hPhiC_2tracks", "FT0C #varphi with 2 tracks;#varphi;counts", {HistType::kTH1F, {{9, 0, 2 * M_PI}}}}, - {"map/hEtaC_2tracks", "FT0C #eta with 2 tracks;#eta;counts", {HistType::kTH1F, {{5, -3.1, -2.1}}}}, - {"map/hEtaPhiC_2tracks", "FT0C #eta vs #varphi with 2 tracks;#eta;#varphi", {HistType::kTH2F, {{5, -3.1, -2.1}, {9, 0, 2 * M_PI}}}}, - - {"map/hPhiC_ntracks", "FT0C #varphi with >2 tracks;#varphi;counts", {HistType::kTH1F, {{9, 0, 2 * M_PI}}}}, - {"map/hEtaC_ntracks", "FT0C #eta with >2 tracks;#eta;counts", {HistType::kTH1F, {{5, -3.1, -2.1}}}}, - {"map/hEtaPhiC_ntracks", "FT0C #eta vs #varphi with >2 tracks;#eta;#varphi", {HistType::kTH2F, {{5, -3.1, -2.1}, {9, 0, 2 * M_PI}}}}, - - {"mult/hPnFT0A_raw", "P(n): FT0A fired-channel multiplicity;N_{fired}^{FT0A};events", {HistType::kTH1F, {{97, -0.5, 96.5}}}}, - {"mult/hPnFT0C_raw", "P(n): FT0C fired-channel multiplicity;N_{fired}^{FT0C};events", {HistType::kTH1F, {{50, -0.5, 49.5}}}}, - {"mult/hPnFV0A_raw", "P(n): FV0A fired-channel multiplicity;N_{fired}^{FV0A};events", {HistType::kTH1F, {{49, -0.5, 48.5}}}}, - {"mult/hPnFT0A", "P(n): FT0A fired-channel multiplicity;N_{fired}^{FT0A};events", {HistType::kTH1F, {{97, -0.5, 96.5}}}}, - {"mult/hPnFT0C", "P(n): FT0C fired-channel multiplicity;N_{fired}^{FT0C};events", {HistType::kTH1F, {{50, -0.5, 49.5}}}}, - {"mult/hPnFV0A", "P(n): FV0A fired-channel multiplicity;N_{fired}^{FV0A};events", {HistType::kTH1F, {{49, -0.5, 48.5}}}}, - - {"mult/hNfiredA_vs_C", "FT0A vs FT0C fired channels;N_{fired}^{FT0A};N_{fired}^{FT0C}", {HistType::kTH2F, {{97, -0.5, 96.5}, {113, -0.5, 112.5}}}}, - - {"qaBeforeCuts/hFT0AAmplitudeVsTime", "FT0A amplitude vs time before cuts;FT0A time (ns);FT0A total amplitude", {HistType::kTH2F, {{200, -40., 40.}, {300, 0., 3000.}}}}, - {"qaBeforeCuts/hFT0CAmplitudeVsTime", "FT0C amplitude vs time before cuts;FT0C time (ns);FT0C total amplitude", {HistType::kTH2F, {{200, -40., 40.}, {300, 0., 3000.}}}}, - - {"qaBeforeCuts/hZNEnergy", "ZNA vs ZNC energy before cuts;ZNA energy;ZNC energy", {HistType::kTH2F, {{250, -5.0, 20.}, {250, -5.0, 20.}}}}, - - {"map/hXYA", "FT0A fired channels in x-y;x [cm];y [cm]", {HistType::kTH2F, {{12, -18., 18.}, {12, -18., 18.}}}}, - {"map/hXYC", "FT0C fired channels in x-y;x [cm];y [cm]", {HistType::kTH2F, {{12, -18., 18.}, {12, -18., 18.}}}}, - {"map/hXYC_2tracks", "FT0C fired channels in x-y with 2 tracks;x [cm];y [cm]", {HistType::kTH2F, {{12, -18., 18.}, {12, -18., 18.}}}}, - {"map/hXYC_ntracks", "FT0C fired channels in x-y with >2 tracks;x [cm];y [cm]", {HistType::kTH2F, {{12, -18., 18.}, {12, -18., 18.}}}}}}; - - int countParticlesInRange(udhelpers::Bits256 const& thr1, udhelpers::Bits256 const& thr2, int first, int last) - { - int n = 0; - for (int bit = first; bit <= last; ++bit) { - const bool aboveThr1 = udhelpers::testBit(thr1, bit); - const bool aboveThr2 = udhelpers::testBit(thr2, bit); - - if (aboveThr2) { - n += 2; - } else if (aboveThr1) { - n += 1; - } - } - return n; - } - - template - bool isGoodRctFlag(const C& collision) - { - switch (cutRctFlag) { - case 1: // CBT - return sgSelector.isCBTOk(collision); - case 2: // CBT + ZDC - return sgSelector.isCBTZdcOk(collision); - case 3: // CBT hadron - return sgSelector.isCBTHadronOk(collision); - case 4: // CBT hadron + ZDC - return sgSelector.isCBTHadronZdcOk(collision); - default: // no RCT cut applied - return true; - } - } - - template - bool isZeroNeutronA(const C& collision) - { - const float eZNA = collision.energyCommonZNA(); // exact accessor to be checked - return eZNA <= cutZNAnoNeutron; - } - - template - bool isZeroNeutronC(const C& collision) - { - const float eZNC = collision.energyCommonZNC(); // exact accessor to be checked - return eZNC <= cutZNCnoNeutron; - } - - template - bool collisionPassesCuts(const C& collision) - { - /* good vertex */ - if (!collision.vtxITSTPC()) { - return false; - } - registry.fill(HIST("debug/hEventCounter"), 4.); - - /* same bunch pile-up rejection */ - if (!collision.sbp()) { - return false; - } - registry.fill(HIST("debug/hEventCounter"), 5.); - - /* ITS ROF rejection */ - if (!collision.itsROFb()) { - return false; - } - registry.fill(HIST("debug/hEventCounter"), 6.); - - /* Timeframe border collision rejection */ - if (!collision.tfb()) { - return false; - } - registry.fill(HIST("debug/hEventCounter"), 7.); - - /* z-vertex cut */ - if (std::abs(collision.posZ()) > 10.0) { - return false; - } - registry.fill(HIST("debug/hEventCounter"), 8.); - - /* RCT flag check*/ - if (useRctFlag) { - if (!isGoodRctFlag(collision)) { - return false; - } - registry.fill(HIST("debug/hEventCounter"), 9.); // passed RCT flag - } - - /* FT0-A veto on total amplitude */ - if (useFT0AVeto) { - const float ampA = collision.totalFT0AmplitudeA(); - if (ampA > cutFT0AmpAVeto) { - return false; - } - registry.fill(HIST("debug/hEventCounter"), 10.); - } - - /* FV0-A veto on total amplitude */ - if (useFV0AVeto) { - const float ampA = collision.totalFV0AmplitudeA(); - if (ampA > cutFV0AmpAVeto) { - return false; - } - registry.fill(HIST("debug/hEventCounter"), 11.); - } - - /* direct FT0 timing cuts */ - if (useFT0TimeCut) { - const float tA = collision.timeFT0A(); - const float tC = collision.timeFT0C(); - - if (abs(tA) > cutFT0TimeA) { - return false; - } - if (abs(tC) > cutFT0TimeC) { - return false; - } - registry.fill(HIST("debug/hEventCounter"), 12.); - } - - int gapSide = 0; // Veto on A-side for now. - if (applyGapSideZDCVeto) { - if (gapSide == 0) { // gap-A - if (!isZeroNeutronA(collision)) { - return false; - } - } else if (gapSide == 1) { // gap-C - if (!isZeroNeutronC(collision)) { - return false; - } - } - registry.fill(HIST("debug/hEventCounter"), 13.); - } - - /* - if (applyGapSideZDCVeto) { - if (!isZeroNeutronA(collision) || !isZeroNeutronC(collision)) { - return false; - } - registry.fill(HIST("debug/hEventCounter"), 13.); - } - */ - return true; - } - - void init(InitContext&) - { - ft0Det.calculateChannelCenter(); - - LOGF(info, "UpcTestFITBitMapping initialized. whichThr=%d", static_cast(whichThr)); - - LOGF(info, "=== FT0A geometry dump ==="); - for (int ch = 0; ch < udhelpers::kFT0AChannels; ++ch) { - auto pos = ft0Det.getChannelCenter(ch); - - const double x = pos.X(); - const double y = pos.Y(); - const double z = pos.Z(); - - const double phi = RecoDecay::phi(x, y); - const double r = std::sqrt(x * x + y * y); - const double theta = std::atan2(r, z); - const double eta = -std::log(std::tan(0.5 * theta)); - - LOGF(info, "FT0A ch=%3d x=%+8.4f y=%+8.4f z=%+8.4f phi=%+8.4f eta=%+8.4f", - ch, x, y, z, phi, eta); - } - - LOGF(info, "=== FT0C geometry dump ==="); - for (int ch = udhelpers::kFT0AChannels; ch < udhelpers::kFT0Bits; ++ch) { - auto pos = ft0Det.getChannelCenter(ch); - - const double x = pos.X(); - const double y = pos.Y(); - const double z = pos.Z(); - - const double phi = RecoDecay::phi(x, y); - const double r = std::sqrt(x * x + y * y); - const double theta = std::atan2(r, z); - const double eta = -std::log(std::tan(0.5 * theta)); - - LOGF(info, "FT0C ch=%3d x=%+8.4f y=%+8.4f z=%+8.4f phi=%+8.4f eta=%+8.4f", - ch, x, y, z, phi, eta); - } - - LOGF(info, "=== end of FT0 geometry dump ==="); - } - - void process(UDCollisionsFull::iterator const& collision, aod::UDCollisionFITBits const& fitBits) - { - if (maxEvents >= 0 && collision.globalIndex() >= maxEvents) { - return; - } - - registry.fill(HIST("debug/hEventCounter"), 0.); - registry.fill(HIST("debug/hFitBitsSize"), fitBits.size()); - registry.fill(HIST("debug/hCollisionIndexMod"), collision.globalIndex() % 100); - - if (fitBits.size() == 0) { - if (collision.globalIndex() < debugPrintFirst || - (debugPrintEvery > 0 && collision.globalIndex() % debugPrintEvery == 0)) { - LOGF(info, "collision %d: fitBits.size() = 0", collision.globalIndex()); - } - registry.fill(HIST("debug/hEventCounter"), 1.); - return; - - } else if (fitBits.size() == 1) { - LOGF(debug, "collision %d: fitBits.size() = %d , as it should be", collision.globalIndex(), fitBits.size()); - registry.fill(HIST("debug/hEventCounter"), 2.); - - } else { // > 1 case - if (collision.globalIndex() < debugPrintFirst || - (debugPrintEvery > 0 && collision.globalIndex() % debugPrintEvery == 0)) { - LOGF(warn, "collision %d: fitBits.size() = %d (expected 0 or 1)", collision.globalIndex(), fitBits.size()); - } - registry.fill(HIST("debug/hEventCounter"), 3.); - return; - } - - const float ft0TimeA = collision.timeFT0A(); - const float ft0TimeC = collision.timeFT0C(); - const float ft0AmpA = collision.totalFT0AmplitudeA(); - const float ft0AmpC = collision.totalFT0AmplitudeC(); - const float fv0AmpA = collision.totalFV0AmplitudeA(); - - registry.fill(HIST("mult/hPnFT0A_raw"), ft0AmpA); - registry.fill(HIST("mult/hPnFT0C_raw"), ft0AmpC); - registry.fill(HIST("mult/hPnFV0A_raw"), fv0AmpA); - - registry.fill(HIST("qaBeforeCuts/hFT0AAmplitudeVsTime"), ft0TimeA, ft0AmpA); - registry.fill(HIST("qaBeforeCuts/hFT0CAmplitudeVsTime"), ft0TimeC, ft0AmpC); - registry.fill(HIST("qaBeforeCuts/hZNEnergy"), collision.energyCommonZNA(), collision.energyCommonZNC()); - - /* Checking collision level cuts */ - if (!collisionPassesCuts(collision)) { - return; - } - - /* Only one row per collision is expected. */ - auto row = fitBits.begin(); - - const auto w1 = udhelpers::makeBits256(row.thr1W0(), row.thr1W1(), row.thr1W2(), row.thr1W3()); - const auto w2 = udhelpers::makeBits256(row.thr2W0(), row.thr2W1(), row.thr2W2(), row.thr2W3()); - - const int nFT0A = countParticlesInRange(w1, w2, 0, udhelpers::kFT0AChannels - 1); - const int nFT0C = countParticlesInRange(w1, w2, udhelpers::kFT0AChannels, udhelpers::kFT0Bits - 1); - const int nFV0A = countParticlesInRange(w1, w2, udhelpers::kFT0Bits, udhelpers::kTotalBits - 1); - - registry.fill(HIST("mult/hPnFT0A"), nFT0A); - registry.fill(HIST("mult/hPnFT0C"), nFT0C); - registry.fill(HIST("mult/hPnFV0A"), nFV0A); - registry.fill(HIST("mult/hNfiredA_vs_C"), nFT0A, nFT0C); - - if (collision.globalIndex() < debugPrintFirst || (debugPrintEvery > 0 && collision.globalIndex() % debugPrintEvery == 0)) { - LOGF(info, - "collision %d: fitBits.size=%d nFT0A=%d nFT0C=%d nFV0A=%d", - collision.globalIndex(), fitBits.size(), nFT0A, nFT0C, nFV0A); - } - /// Only print the first 5 entries for debugging purpose - if (collision.globalIndex() < 5) { - for (int bit = 96; bit < 208; ++bit) { - if (udhelpers::testBit(w1, bit)) { - LOGF(info, "ANALYSIS sees one fired bit %d", bit); - } - if (udhelpers::testBit(w2, bit)) { - LOGF(info, "ANALYSIS sees two fired bits %d", bit); - } - } - } - - /// Checking for special event geometry of rho (resonances that decays into 2 tracks, in general) - if (nFT0C == 2) { - for (int bit = 0; bit < udhelpers::kFT0Bits; ++bit) { - if (!udhelpers::testBit(w1, bit)) { - continue; - } - - double phi = 0., eta = 0.; - const bool ok = udhelpers::getPhiEtaFromFitBit(ft0Det, bit, offsetFT0, iRunOffset, phi, eta); - if (!ok) { - continue; - } - - auto pos = ft0Det.getChannelCenter(bit); - - const double x = pos.X() - offsetFT0[iRunOffset].getX(); - const double y = pos.Y() - offsetFT0[iRunOffset].getY(); - - if (bit >= udhelpers::kFT0AChannels) { - registry.fill(HIST("debug/hFT0CChannelOccupancy_2tracks"), bit); - registry.fill(HIST("map/hPhiC_2tracks"), phi); - registry.fill(HIST("map/hEtaC_2tracks"), eta); - registry.fill(HIST("map/hEtaPhiC_2tracks"), eta, phi); - registry.fill(HIST("map/hXYC_2tracks"), x, y); - } - } - } - - if (nFT0C > 2) { - for (int bit = 0; bit < udhelpers::kFT0Bits; ++bit) { - if (!udhelpers::testBit(w1, bit)) { - continue; - } - - double phi = 0., eta = 0.; - const bool ok = udhelpers::getPhiEtaFromFitBit(ft0Det, bit, offsetFT0, iRunOffset, phi, eta); - if (!ok) { - continue; - } - - auto pos = ft0Det.getChannelCenter(bit); - - const double x = pos.X() - offsetFT0[iRunOffset].getX(); - const double y = pos.Y() - offsetFT0[iRunOffset].getY(); - - if (bit >= udhelpers::kFT0AChannels) { - registry.fill(HIST("debug/hFT0CChannelOccupancy_ntracks"), bit); - registry.fill(HIST("map/hPhiC_ntracks"), phi); - registry.fill(HIST("map/hEtaC_ntracks"), eta); - registry.fill(HIST("map/hEtaPhiC_ntracks"), eta, phi); - registry.fill(HIST("map/hXYC_ntracks"), x, y); - } - } - } - - /* Mapping for at least 1 fired channel only */ - for (int bit = 0; bit < udhelpers::kFT0Bits; ++bit) { - if (!udhelpers::testBit(w1, bit)) { - continue; - } - - double phi = 0., eta = 0.; - const bool ok = udhelpers::getPhiEtaFromFitBit(ft0Det, bit, offsetFT0, iRunOffset, phi, eta); - if (!ok) { - continue; - } - - auto pos = ft0Det.getChannelCenter(bit); - - const double x = pos.X() - offsetFT0[iRunOffset].getX(); - const double y = pos.Y() - offsetFT0[iRunOffset].getY(); - - if (bit < udhelpers::kFT0AChannels) { - // LOGF(info,"phi = %.4f, phiBin = %i",phi,phiBin); - registry.fill(HIST("debug/hFT0AChannelOccupancy"), bit); - registry.fill(HIST("map/hPhiA"), phi); - registry.fill(HIST("map/hEtaA"), eta); - registry.fill(HIST("map/hEtaPhiA"), eta, phi); - registry.fill(HIST("map/hXYA"), x, y); - } else { - registry.fill(HIST("debug/hFT0CChannelOccupancy"), bit); - registry.fill(HIST("map/hPhiC"), phi); - registry.fill(HIST("map/hEtaC"), eta); - registry.fill(HIST("map/hEtaPhiC"), eta, phi); - registry.fill(HIST("map/hXYC"), x, y); - } - } - - registry.fill(HIST("debug/hEventCounter"), 4.); - } -}; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - return WorkflowSpec{ - adaptAnalysisTask(cfgc, TaskName{"fitbitmapping"})}; -} +// 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. +// +// \brief FIT bits to phi, eta mapping + multiplicity from FIT bits +// \author Sandor Lokos, sandor.lokos@cern.ch +// \since March 2026 + +#include "PWGUD/Core/SGSelector.h" +#include "PWGUD/Core/UDHelpers.h" +#include "PWGUD/DataModel/UDTables.h" + +#include "FT0Base/Geometry.h" +#include "Framework/AnalysisTask.h" +#include "Framework/HistogramRegistry.h" +#include "Framework/Logger.h" +#include "Framework/runDataProcessing.h" + +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +using UDCollisionsFull = soa::Join< + aod::UDCollisions, + aod::UDCollisionsSels, + aod::UDCollisionSelExtras, + aod::UDZdcsReduced>; + +struct UpcTestFITBitMapping { + + SGSelector sgSelector; + + Configurable whichThr{"whichThr", 1, "Use 1=Thr1 bits or 2=Thr2 bits"}; + Configurable maxEvents{"maxEvents", -1, "Process at most this many collisions (-1 = all)"}; + Configurable debugPrintEvery{"debugPrintEvery", 1000, "Print every N events (-1 disables)"}; + Configurable debugPrintFirst{"debugPrintFirst", 10, "Always print first N events"}; + Configurable useRctFlag{"useRctFlag", true, "use RCT flags for event selection"}; + Configurable cutRctFlag{"cutRctFlag", 2, "0 = off, 1 = CBT, 2 = CBT+ZDC, 3 = CBThadron, 4 = CBThadron+ZDC"}; + + Configurable applyGapSideZDCVeto{"applyGapSideZDCVeto", true, "Apply ZDC neutron veto on the gap side"}; + Configurable cutZNAnoNeutron{"cutZNAnoNeutron", 1.f, "Maximum allowed ZNA energy for zero-neutron condition"}; + Configurable cutZNCnoNeutron{"cutZNCnoNeutron", 1.f, "Maximum allowed ZNC energy for zero-neutron condition"}; + + Configurable useFT0TimeCut{"useFT0TimeCut", true, "Apply direct FT0 timing cut"}; + Configurable cutFT0TimeA{"cutFT0TimeA", 2., "FT0-A time cut [ns]"}; + Configurable cutFT0TimeC{"cutFT0TimeC", 2., "FT0-C time cut [ns]"}; + + Configurable useFT0AVeto{"useFT0AVeto", true, "Apply FT0-A total amplitude veto"}; + Configurable cutFT0AmpAVeto{"cutFT0AmpAVeto", 100., "Maximum allowed FT0-A total amplitude for veto"}; + + Configurable useFV0AVeto{"useFV0AVeto", true, "Apply FV0-A total amplitude veto"}; + Configurable cutFV0AmpAVeto{"cutFV0AmpAVeto", 50., "Maximum allowed FV0-A total amplitude for veto"}; + + struct OffsetXYZ { + double x{0.}, y{0.}, z{0.}; + double getX() const { return x; } + double getY() const { return y; } + double getZ() const { return z; } + }; + + std::array offsetFT0{}; + int iRunOffset = 0; + + o2::ft0::Geometry ft0Det{}; + + HistogramRegistry registry{ + "registry", + {{"debug/hEventCounter", "Event counter;step;events", {HistType::kTH1F, {{20, -0.5, 19.5}}}}, + {"debug/hFitBitsSize", "fitBits.size() per collision;fitBits.size();events", {HistType::kTH1F, {{10, -0.5, 9.5}}}}, + {"debug/hCollisionIndexMod", "Collision index mod 100;collision.globalIndex() % 100;events", {HistType::kTH1F, {{100, -0.5, 99.5}}}}, + {"debug/hFT0AChannelOccupancy", "FT0A fired channel occupancy;FT0A channel;counts", {HistType::kTH1F, {{97, -0.5, 96.5}}}}, + {"debug/hFT0CChannelOccupancy", "FT0C fired channel occupancy;FT0C channel;counts", {HistType::kTH1F, {{112, 95.5, 207.5}}}}, + {"debug/hFT0CChannelOccupancy_2tracks", "FT0C fired channel occupancy with 2 tracks;FT0C channel;counts", {HistType::kTH1F, {{112, 95.5, 207.5}}}}, + {"debug/hFT0CChannelOccupancy_ntracks", "FT0C fired channel occupancy with >2 tracks;FT0C channel;counts", {HistType::kTH1F, {{112, 95.5, 207.5}}}}, + + {"map/hPhiA", "FT0A #varphi;#varphi;counts", {HistType::kTH1F, {{9, 0, 2 * M_PI}}}}, + {"map/hEtaA", "FT0A #eta;#eta;counts", {HistType::kTH1F, {{7, 3.4, 4.8}}}}, + {"map/hEtaPhiA", "FT0A #eta vs #varphi;#eta;#varphi", {HistType::kTH2F, {{7, 3.4, 4.8}, {9, 0, 2 * M_PI}}}}, + + {"map/hPhiC", "FT0C #varphi;#varphi;counts", {HistType::kTH1F, {{9, 0, 2 * M_PI}}}}, + {"map/hEtaC", "FT0C #eta;#eta;counts", {HistType::kTH1F, {{5, -3.1, -2.1}}}}, + {"map/hEtaPhiC", "FT0C #eta vs #varphi;#eta;#varphi", {HistType::kTH2F, {{5, -3.1, -2.1}, {9, 0, 2 * M_PI}}}}, + + {"map/hPhiC_2tracks", "FT0C #varphi with 2 tracks;#varphi;counts", {HistType::kTH1F, {{9, 0, 2 * M_PI}}}}, + {"map/hEtaC_2tracks", "FT0C #eta with 2 tracks;#eta;counts", {HistType::kTH1F, {{5, -3.1, -2.1}}}}, + {"map/hEtaPhiC_2tracks", "FT0C #eta vs #varphi with 2 tracks;#eta;#varphi", {HistType::kTH2F, {{5, -3.1, -2.1}, {9, 0, 2 * M_PI}}}}, + + {"map/hPhiC_ntracks", "FT0C #varphi with >2 tracks;#varphi;counts", {HistType::kTH1F, {{9, 0, 2 * M_PI}}}}, + {"map/hEtaC_ntracks", "FT0C #eta with >2 tracks;#eta;counts", {HistType::kTH1F, {{5, -3.1, -2.1}}}}, + {"map/hEtaPhiC_ntracks", "FT0C #eta vs #varphi with >2 tracks;#eta;#varphi", {HistType::kTH2F, {{5, -3.1, -2.1}, {9, 0, 2 * M_PI}}}}, + + {"mult/hPnFT0A_raw", "P(n): FT0A fired-channel multiplicity;N_{fired}^{FT0A};events", {HistType::kTH1F, {{97, -0.5, 96.5}}}}, + {"mult/hPnFT0C_raw", "P(n): FT0C fired-channel multiplicity;N_{fired}^{FT0C};events", {HistType::kTH1F, {{50, -0.5, 49.5}}}}, + {"mult/hPnFV0A_raw", "P(n): FV0A fired-channel multiplicity;N_{fired}^{FV0A};events", {HistType::kTH1F, {{49, -0.5, 48.5}}}}, + {"mult/hPnFT0A", "P(n): FT0A fired-channel multiplicity;N_{fired}^{FT0A};events", {HistType::kTH1F, {{97, -0.5, 96.5}}}}, + {"mult/hPnFT0C", "P(n): FT0C fired-channel multiplicity;N_{fired}^{FT0C};events", {HistType::kTH1F, {{50, -0.5, 49.5}}}}, + {"mult/hPnFV0A", "P(n): FV0A fired-channel multiplicity;N_{fired}^{FV0A};events", {HistType::kTH1F, {{49, -0.5, 48.5}}}}, + + {"mult/hNfiredA_vs_C", "FT0A vs FT0C fired channels;N_{fired}^{FT0A};N_{fired}^{FT0C}", {HistType::kTH2F, {{97, -0.5, 96.5}, {113, -0.5, 112.5}}}}, + + {"qaBeforeCuts/hFT0AAmplitudeVsTime", "FT0A amplitude vs time before cuts;FT0A time (ns);FT0A total amplitude", {HistType::kTH2F, {{200, -40., 40.}, {300, 0., 3000.}}}}, + {"qaBeforeCuts/hFT0CAmplitudeVsTime", "FT0C amplitude vs time before cuts;FT0C time (ns);FT0C total amplitude", {HistType::kTH2F, {{200, -40., 40.}, {300, 0., 3000.}}}}, + + {"qaBeforeCuts/hZNEnergy", "ZNA vs ZNC energy before cuts;ZNA energy;ZNC energy", {HistType::kTH2F, {{250, -5.0, 20.}, {250, -5.0, 20.}}}}, + + {"map/hXYA", "FT0A fired channels in x-y;x [cm];y [cm]", {HistType::kTH2F, {{12, -18., 18.}, {12, -18., 18.}}}}, + {"map/hXYC", "FT0C fired channels in x-y;x [cm];y [cm]", {HistType::kTH2F, {{12, -18., 18.}, {12, -18., 18.}}}}, + {"map/hXYC_2tracks", "FT0C fired channels in x-y with 2 tracks;x [cm];y [cm]", {HistType::kTH2F, {{12, -18., 18.}, {12, -18., 18.}}}}, + {"map/hXYC_ntracks", "FT0C fired channels in x-y with >2 tracks;x [cm];y [cm]", {HistType::kTH2F, {{12, -18., 18.}, {12, -18., 18.}}}}}}; + + int countParticlesInRange(udhelpers::Bits256 const& thr1, udhelpers::Bits256 const& thr2, int first, int last) + { + int n = 0; + for (int bit = first; bit <= last; ++bit) { + const bool aboveThr1 = udhelpers::testBit(thr1, bit); + const bool aboveThr2 = udhelpers::testBit(thr2, bit); + + if (aboveThr2) { + n += 2; + } else if (aboveThr1) { + n += 1; + } + } + return n; + } + + template + bool isGoodRctFlag(const C& collision) + { + switch (cutRctFlag) { + case 1: // CBT + return sgSelector.isCBTOk(collision); + case 2: // CBT + ZDC + return sgSelector.isCBTZdcOk(collision); + case 3: // CBT hadron + return sgSelector.isCBTHadronOk(collision); + case 4: // CBT hadron + ZDC + return sgSelector.isCBTHadronZdcOk(collision); + default: // no RCT cut applied + return true; + } + } + + template + bool isZeroNeutronA(const C& collision) + { + const float eZNA = collision.energyCommonZNA(); // exact accessor to be checked + return eZNA <= cutZNAnoNeutron; + } + + template + bool isZeroNeutronC(const C& collision) + { + const float eZNC = collision.energyCommonZNC(); // exact accessor to be checked + return eZNC <= cutZNCnoNeutron; + } + + template + bool collisionPassesCuts(const C& collision) + { + /* good vertex */ + if (!collision.vtxITSTPC()) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 4.); + + /* same bunch pile-up rejection */ + if (!collision.sbp()) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 5.); + + /* ITS ROF rejection */ + if (!collision.itsROFb()) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 6.); + + /* Timeframe border collision rejection */ + if (!collision.tfb()) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 7.); + + /* z-vertex cut */ + if (std::abs(collision.posZ()) > 10.0) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 8.); + + /* RCT flag check*/ + if (useRctFlag) { + if (!isGoodRctFlag(collision)) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 9.); // passed RCT flag + } + + /* FT0-A veto on total amplitude */ + if (useFT0AVeto) { + const float ampA = collision.totalFT0AmplitudeA(); + if (ampA > cutFT0AmpAVeto) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 10.); + } + + /* FV0-A veto on total amplitude */ + if (useFV0AVeto) { + const float ampA = collision.totalFV0AmplitudeA(); + if (ampA > cutFV0AmpAVeto) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 11.); + } + + /* direct FT0 timing cuts */ + if (useFT0TimeCut) { + const float tA = collision.timeFT0A(); + const float tC = collision.timeFT0C(); + + if (abs(tA) > cutFT0TimeA) { + return false; + } + if (abs(tC) > cutFT0TimeC) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 12.); + } + + int gapSide = 0; // Veto on A-side for now. + if (applyGapSideZDCVeto) { + if (gapSide == 0) { // gap-A + if (!isZeroNeutronA(collision)) { + return false; + } + } else if (gapSide == 1) { // gap-C + if (!isZeroNeutronC(collision)) { + return false; + } + } + registry.fill(HIST("debug/hEventCounter"), 13.); + } + + /* + if (applyGapSideZDCVeto) { + if (!isZeroNeutronA(collision) || !isZeroNeutronC(collision)) { + return false; + } + registry.fill(HIST("debug/hEventCounter"), 13.); + } + */ + return true; + } + + void init(InitContext&) + { + ft0Det.calculateChannelCenter(); + + LOGF(info, "UpcTestFITBitMapping initialized. whichThr=%d", static_cast(whichThr)); + + LOGF(info, "=== FT0A geometry dump ==="); + for (int ch = 0; ch < udhelpers::kFT0AChannels; ++ch) { + auto pos = ft0Det.getChannelCenter(ch); + + const double x = pos.X(); + const double y = pos.Y(); + const double z = pos.Z(); + + const double phi = RecoDecay::phi(x, y); + const double r = std::sqrt(x * x + y * y); + const double theta = std::atan2(r, z); + const double eta = -std::log(std::tan(0.5 * theta)); + + LOGF(info, "FT0A ch=%3d x=%+8.4f y=%+8.4f z=%+8.4f phi=%+8.4f eta=%+8.4f", + ch, x, y, z, phi, eta); + } + + LOGF(info, "=== FT0C geometry dump ==="); + for (int ch = udhelpers::kFT0AChannels; ch < udhelpers::kFT0Bits; ++ch) { + auto pos = ft0Det.getChannelCenter(ch); + + const double x = pos.X(); + const double y = pos.Y(); + const double z = pos.Z(); + + const double phi = RecoDecay::phi(x, y); + const double r = std::sqrt(x * x + y * y); + const double theta = std::atan2(r, z); + const double eta = -std::log(std::tan(0.5 * theta)); + + LOGF(info, "FT0C ch=%3d x=%+8.4f y=%+8.4f z=%+8.4f phi=%+8.4f eta=%+8.4f", + ch, x, y, z, phi, eta); + } + + LOGF(info, "=== end of FT0 geometry dump ==="); + } + + void process(UDCollisionsFull::iterator const& collision, aod::UDCollisionFITBits const& fitBits) + { + if (maxEvents >= 0 && collision.globalIndex() >= maxEvents) { + return; + } + + registry.fill(HIST("debug/hEventCounter"), 0.); + registry.fill(HIST("debug/hFitBitsSize"), fitBits.size()); + registry.fill(HIST("debug/hCollisionIndexMod"), collision.globalIndex() % 100); + + if (fitBits.size() == 0) { + if (collision.globalIndex() < debugPrintFirst || + (debugPrintEvery > 0 && collision.globalIndex() % debugPrintEvery == 0)) { + LOGF(info, "collision %d: fitBits.size() = 0", collision.globalIndex()); + } + registry.fill(HIST("debug/hEventCounter"), 1.); + return; + + } else if (fitBits.size() == 1) { + LOGF(debug, "collision %d: fitBits.size() = %d , as it should be", collision.globalIndex(), fitBits.size()); + registry.fill(HIST("debug/hEventCounter"), 2.); + + } else { // > 1 case + if (collision.globalIndex() < debugPrintFirst || + (debugPrintEvery > 0 && collision.globalIndex() % debugPrintEvery == 0)) { + LOGF(warn, "collision %d: fitBits.size() = %d (expected 0 or 1)", collision.globalIndex(), fitBits.size()); + } + registry.fill(HIST("debug/hEventCounter"), 3.); + return; + } + + const float ft0TimeA = collision.timeFT0A(); + const float ft0TimeC = collision.timeFT0C(); + const float ft0AmpA = collision.totalFT0AmplitudeA(); + const float ft0AmpC = collision.totalFT0AmplitudeC(); + const float fv0AmpA = collision.totalFV0AmplitudeA(); + + registry.fill(HIST("mult/hPnFT0A_raw"), ft0AmpA); + registry.fill(HIST("mult/hPnFT0C_raw"), ft0AmpC); + registry.fill(HIST("mult/hPnFV0A_raw"), fv0AmpA); + + registry.fill(HIST("qaBeforeCuts/hFT0AAmplitudeVsTime"), ft0TimeA, ft0AmpA); + registry.fill(HIST("qaBeforeCuts/hFT0CAmplitudeVsTime"), ft0TimeC, ft0AmpC); + registry.fill(HIST("qaBeforeCuts/hZNEnergy"), collision.energyCommonZNA(), collision.energyCommonZNC()); + + /* Checking collision level cuts */ + if (!collisionPassesCuts(collision)) { + return; + } + + /* Only one row per collision is expected. */ + auto row = fitBits.begin(); + + const auto w1 = udhelpers::makeBits256(row.thr1W0(), row.thr1W1(), row.thr1W2(), row.thr1W3()); + const auto w2 = udhelpers::makeBits256(row.thr2W0(), row.thr2W1(), row.thr2W2(), row.thr2W3()); + + const int nFT0A = countParticlesInRange(w1, w2, 0, udhelpers::kFT0AChannels - 1); + const int nFT0C = countParticlesInRange(w1, w2, udhelpers::kFT0AChannels, udhelpers::kFT0Bits - 1); + const int nFV0A = countParticlesInRange(w1, w2, udhelpers::kFT0Bits, udhelpers::kTotalBits - 1); + + registry.fill(HIST("mult/hPnFT0A"), nFT0A); + registry.fill(HIST("mult/hPnFT0C"), nFT0C); + registry.fill(HIST("mult/hPnFV0A"), nFV0A); + registry.fill(HIST("mult/hNfiredA_vs_C"), nFT0A, nFT0C); + + if (collision.globalIndex() < debugPrintFirst || (debugPrintEvery > 0 && collision.globalIndex() % debugPrintEvery == 0)) { + LOGF(info, + "collision %d: fitBits.size=%d nFT0A=%d nFT0C=%d nFV0A=%d", + collision.globalIndex(), fitBits.size(), nFT0A, nFT0C, nFV0A); + } + /// Only print the first 5 entries for debugging purpose + if (collision.globalIndex() < 5) { + for (int bit = 96; bit < 208; ++bit) { + if (udhelpers::testBit(w1, bit)) { + LOGF(info, "ANALYSIS sees one fired bit %d", bit); + } + if (udhelpers::testBit(w2, bit)) { + LOGF(info, "ANALYSIS sees two fired bits %d", bit); + } + } + } + + /// Checking for special event geometry of rho (resonances that decays into 2 tracks, in general) + if (nFT0C == 2) { + for (int bit = 0; bit < udhelpers::kFT0Bits; ++bit) { + if (!udhelpers::testBit(w1, bit)) { + continue; + } + + double phi = 0., eta = 0.; + const bool ok = udhelpers::getPhiEtaFromFitBit(ft0Det, bit, offsetFT0, iRunOffset, phi, eta); + if (!ok) { + continue; + } + + auto pos = ft0Det.getChannelCenter(bit); + + const double x = pos.X() - offsetFT0[iRunOffset].getX(); + const double y = pos.Y() - offsetFT0[iRunOffset].getY(); + + if (bit >= udhelpers::kFT0AChannels) { + registry.fill(HIST("debug/hFT0CChannelOccupancy_2tracks"), bit); + registry.fill(HIST("map/hPhiC_2tracks"), phi); + registry.fill(HIST("map/hEtaC_2tracks"), eta); + registry.fill(HIST("map/hEtaPhiC_2tracks"), eta, phi); + registry.fill(HIST("map/hXYC_2tracks"), x, y); + } + } + } + + if (nFT0C > 2) { + for (int bit = 0; bit < udhelpers::kFT0Bits; ++bit) { + if (!udhelpers::testBit(w1, bit)) { + continue; + } + + double phi = 0., eta = 0.; + const bool ok = udhelpers::getPhiEtaFromFitBit(ft0Det, bit, offsetFT0, iRunOffset, phi, eta); + if (!ok) { + continue; + } + + auto pos = ft0Det.getChannelCenter(bit); + + const double x = pos.X() - offsetFT0[iRunOffset].getX(); + const double y = pos.Y() - offsetFT0[iRunOffset].getY(); + + if (bit >= udhelpers::kFT0AChannels) { + registry.fill(HIST("debug/hFT0CChannelOccupancy_ntracks"), bit); + registry.fill(HIST("map/hPhiC_ntracks"), phi); + registry.fill(HIST("map/hEtaC_ntracks"), eta); + registry.fill(HIST("map/hEtaPhiC_ntracks"), eta, phi); + registry.fill(HIST("map/hXYC_ntracks"), x, y); + } + } + } + + /* Mapping for at least 1 fired channel only */ + for (int bit = 0; bit < udhelpers::kFT0Bits; ++bit) { + if (!udhelpers::testBit(w1, bit)) { + continue; + } + + double phi = 0., eta = 0.; + const bool ok = udhelpers::getPhiEtaFromFitBit(ft0Det, bit, offsetFT0, iRunOffset, phi, eta); + if (!ok) { + continue; + } + + auto pos = ft0Det.getChannelCenter(bit); + + const double x = pos.X() - offsetFT0[iRunOffset].getX(); + const double y = pos.Y() - offsetFT0[iRunOffset].getY(); + + if (bit < udhelpers::kFT0AChannels) { + // LOGF(info,"phi = %.4f, phiBin = %i",phi,phiBin); + registry.fill(HIST("debug/hFT0AChannelOccupancy"), bit); + registry.fill(HIST("map/hPhiA"), phi); + registry.fill(HIST("map/hEtaA"), eta); + registry.fill(HIST("map/hEtaPhiA"), eta, phi); + registry.fill(HIST("map/hXYA"), x, y); + } else { + registry.fill(HIST("debug/hFT0CChannelOccupancy"), bit); + registry.fill(HIST("map/hPhiC"), phi); + registry.fill(HIST("map/hEtaC"), eta); + registry.fill(HIST("map/hEtaPhiC"), eta, phi); + registry.fill(HIST("map/hXYC"), x, y); + } + } + + registry.fill(HIST("debug/hEventCounter"), 4.); + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc, TaskName{"fitbitmapping"})}; +} From 9f5c88db7b311efa5071418eec05b62e92b28821 Mon Sep 17 00:00:00 2001 From: sandor-lokos Date: Wed, 8 Jul 2026 12:15:31 +0200 Subject: [PATCH 4/8] Converter implemented for FITbit tables version 000 to 001; bug fix in the new FITbit table; formatting issues fixed --- PWGUD/DataModel/UDTables.h | 38 +++++++++---------- .../Converters/UDFITbitConverter.cxx | 5 ++- PWGUD/Tasks/CMakeLists.txt | 4 +- PWGUD/Tasks/upcFITMultiplicity.cxx | 14 +++---- PWGUD/Tasks/upcTestFitBitMapping.cxx | 10 ++--- 5 files changed, 36 insertions(+), 35 deletions(-) diff --git a/PWGUD/DataModel/UDTables.h b/PWGUD/DataModel/UDTables.h index 455ddcdbe52..cf802d317b5 100644 --- a/PWGUD/DataModel/UDTables.h +++ b/PWGUD/DataModel/UDTables.h @@ -424,28 +424,28 @@ DECLARE_SOA_COLUMN(Thr2W3, thr2W3, uint64_t); /// 2 MIP thresholds for FT0C ch 9 } // namespace 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 + 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 ); DECLARE_SOA_TABLE_VERSIONED(UDCollisionFITBits_001, "AOD", "UDCOLLFITBITS", 1, - o2::soa::Index<>, - udcollfitbits::UDCollisionId, /// Link to the UDCOLLISION table <---- EXTRA - 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 + o2::soa::Index<>, + udcollfitbits::UDCollisionId, /// Link to the UDCOLLISION table <---- EXTRA + 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 ); using UDCollisionFITBits = UDCollisionFITBits_001; diff --git a/PWGUD/TableProducer/Converters/UDFITbitConverter.cxx b/PWGUD/TableProducer/Converters/UDFITbitConverter.cxx index 915892cfa19..17e08c3a9f9 100644 --- a/PWGUD/TableProducer/Converters/UDFITbitConverter.cxx +++ b/PWGUD/TableProducer/Converters/UDFITbitConverter.cxx @@ -23,7 +23,8 @@ /// /// \author Sandor Lokos -#include +#include "PWGUD/DataModel/UDTables.h" + #include #include #include @@ -59,4 +60,4 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) return WorkflowSpec{ adaptAnalysisTask(cfgc), }; -} \ No newline at end of file +} diff --git a/PWGUD/Tasks/CMakeLists.txt b/PWGUD/Tasks/CMakeLists.txt index cc552d2edcd..632b4c86425 100644 --- a/PWGUD/Tasks/CMakeLists.txt +++ b/PWGUD/Tasks/CMakeLists.txt @@ -298,7 +298,7 @@ o2physics_add_dpl_workflow(upc-test-fit-bit-mapping SOURCES upcTestFitBitMapping.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) - + o2physics_add_dpl_workflow(fit-multiplicity SOURCES upcFITMultiplicity.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore @@ -307,4 +307,4 @@ o2physics_add_dpl_workflow(fit-multiplicity o2physics_add_dpl_workflow(pt-spectra-inclusive-upc SOURCES ptSpectraInclusiveUpc.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore - COMPONENT_NAME Analysis) + COMPONENT_NAME Analysis) \ No newline at end of file diff --git a/PWGUD/Tasks/upcFITMultiplicity.cxx b/PWGUD/Tasks/upcFITMultiplicity.cxx index 60caaeaf500..f0fbc2eae0d 100644 --- a/PWGUD/Tasks/upcFITMultiplicity.cxx +++ b/PWGUD/Tasks/upcFITMultiplicity.cxx @@ -17,11 +17,11 @@ #include "PWGUD/Core/UDHelpers.h" #include "PWGUD/DataModel/UDTables.h" -#include "FT0Base/Geometry.h" -#include "Framework/AnalysisTask.h" -#include "Framework/HistogramRegistry.h" -#include "Framework/Logger.h" -#include "Framework/runDataProcessing.h" +#include +#include +#include +#include +#include #include #include @@ -99,7 +99,7 @@ struct UpcTestFITBitMapping { {"map/hEtaC_ntracks", "FT0C #eta with >2 tracks;#eta;counts", {HistType::kTH1F, {{5, -3.1, -2.1}}}}, {"map/hEtaPhiC_ntracks", "FT0C #eta vs #varphi with >2 tracks;#eta;#varphi", {HistType::kTH2F, {{5, -3.1, -2.1}, {9, 0, 2 * M_PI}}}}, - {"mult/hPnFT0A_raw", "P(n): FT0A fired-channel multiplicity;N_{fired}^{FT0A};events", {HistType::kTH1F, {{97, -0.5, 96.5}}}}, + {"mult/hPnFT0A_raw", "P(n): FT0A fired-channel multiplicity;N_{fired}^{FT0A};events", {HistType::kTH1F, {{97, -0.5, 96.5}}}}, {"mult/hPnFT0C_raw", "P(n): FT0C fired-channel multiplicity;N_{fired}^{FT0C};events", {HistType::kTH1F, {{50, -0.5, 49.5}}}}, {"mult/hPnFV0A_raw", "P(n): FV0A fired-channel multiplicity;N_{fired}^{FV0A};events", {HistType::kTH1F, {{49, -0.5, 48.5}}}}, {"mult/hPnFT0A", "P(n): FT0A fired-channel multiplicity;N_{fired}^{FT0A};events", {HistType::kTH1F, {{97, -0.5, 96.5}}}}, @@ -343,7 +343,7 @@ struct UpcTestFITBitMapping { const float ft0AmpC = collision.totalFT0AmplitudeC(); const float fv0AmpA = collision.totalFV0AmplitudeA(); - registry.fill(HIST("mult/hPnFT0A_raw"), ft0AmpA); + registry.fill(HIST("mult/hPnFT0A_raw"), ft0AmpA); registry.fill(HIST("mult/hPnFT0C_raw"), ft0AmpC); registry.fill(HIST("mult/hPnFV0A_raw"), fv0AmpA); diff --git a/PWGUD/Tasks/upcTestFitBitMapping.cxx b/PWGUD/Tasks/upcTestFitBitMapping.cxx index 91f91e67411..b2f5bbd5ae0 100644 --- a/PWGUD/Tasks/upcTestFitBitMapping.cxx +++ b/PWGUD/Tasks/upcTestFitBitMapping.cxx @@ -17,11 +17,11 @@ #include "PWGUD/Core/UDHelpers.h" #include "PWGUD/DataModel/UDTables.h" -#include "FT0Base/Geometry.h" -#include "Framework/AnalysisTask.h" -#include "Framework/HistogramRegistry.h" -#include "Framework/Logger.h" -#include "Framework/runDataProcessing.h" +#include +#include +#include +#include +#include #include #include From 909ec4dca908b3a0bb3d84d963db26789d99c5bc Mon Sep 17 00:00:00 2001 From: sandor-lokos Date: Wed, 8 Jul 2026 12:19:54 +0200 Subject: [PATCH 5/8] Converter implemented for FITbit tables version 000 to 001; bug fix in the new FITbit table; formatting issues fixed --- PWGUD/TableProducer/Converters/UDFITbitConverter.cxx | 4 ++-- PWGUD/Tasks/upcTestFitBitMapping.cxx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PWGUD/TableProducer/Converters/UDFITbitConverter.cxx b/PWGUD/TableProducer/Converters/UDFITbitConverter.cxx index 17e08c3a9f9..c3cc023851b 100644 --- a/PWGUD/TableProducer/Converters/UDFITbitConverter.cxx +++ b/PWGUD/TableProducer/Converters/UDFITbitConverter.cxx @@ -23,7 +23,7 @@ /// /// \author Sandor Lokos -#include "PWGUD/DataModel/UDTables.h" +#include "PWGUD/DataModel/UDTables.h" #include #include @@ -60,4 +60,4 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) return WorkflowSpec{ adaptAnalysisTask(cfgc), }; -} +} diff --git a/PWGUD/Tasks/upcTestFitBitMapping.cxx b/PWGUD/Tasks/upcTestFitBitMapping.cxx index b2f5bbd5ae0..106eee63a83 100644 --- a/PWGUD/Tasks/upcTestFitBitMapping.cxx +++ b/PWGUD/Tasks/upcTestFitBitMapping.cxx @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include From 825c41aec9dcadeb15bc6a7da71295b9223fec0b Mon Sep 17 00:00:00 2001 From: sandor-lokos Date: Wed, 8 Jul 2026 12:27:37 +0200 Subject: [PATCH 6/8] Converter implemented for FITbit tables version 000 to 001; bug fix in the new FITbit table; formatting issues fixed --- PWGUD/Tasks/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGUD/Tasks/CMakeLists.txt b/PWGUD/Tasks/CMakeLists.txt index 632b4c86425..6fcbaf6493c 100644 --- a/PWGUD/Tasks/CMakeLists.txt +++ b/PWGUD/Tasks/CMakeLists.txt @@ -307,4 +307,4 @@ o2physics_add_dpl_workflow(fit-multiplicity o2physics_add_dpl_workflow(pt-spectra-inclusive-upc SOURCES ptSpectraInclusiveUpc.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore - COMPONENT_NAME Analysis) \ No newline at end of file + COMPONENT_NAME Analysis) From 530f6468b002881be3b9ab4a4d1fb359fad9d1f8 Mon Sep 17 00:00:00 2001 From: sandor-lokos Date: Wed, 8 Jul 2026 13:07:29 +0200 Subject: [PATCH 7/8] Converter implemented for FITbit tables version 000 to 001; bug fix in the new FITbit table; formatting issues fixed --- PWGUD/Core/UDHelpers.h | 10 +++-- PWGUD/Tasks/upcFITMultiplicity.cxx | 66 ---------------------------- PWGUD/Tasks/upcTestFitBitMapping.cxx | 30 ++++++++----- 3 files changed, 27 insertions(+), 79 deletions(-) diff --git a/PWGUD/Core/UDHelpers.h b/PWGUD/Core/UDHelpers.h index 814e395a77f..544622fae1d 100644 --- a/PWGUD/Core/UDHelpers.h +++ b/PWGUD/Core/UDHelpers.h @@ -561,6 +561,10 @@ inline void buildFT0FV0Words(TFT0 const& ft0, TFV0A const& fv0a, thr1[0] = thr1[1] = thr1[2] = thr1[3] = 0ull; thr2[0] = thr2[1] = thr2[2] = thr2[3] = 0ull; + constexpr int nChFT0A = 96; + constexpr int nChFT0C = 112; + constexpr int nChFV0A = 48; + constexpr int kFT0AOffset = 0; constexpr int kFT0COffset = 96; constexpr int kFV0Offset = 208; @@ -573,7 +577,7 @@ inline void buildFT0FV0Words(TFT0 const& ft0, TFV0A const& fv0a, const auto a = ampsA[i]; const int c = chanA[i]; - if (c < 0 || c >= 96) { + if (c < 0 || c >= nChFT0A) { continue; } @@ -589,7 +593,7 @@ inline void buildFT0FV0Words(TFT0 const& ft0, TFV0A const& fv0a, const auto a = ampsC[i]; const int c = chanC[i]; // physical FT0C channel id - if (c < 0 || c >= 112) { + if (c < 0 || c >= nChFT0C) { continue; } @@ -605,7 +609,7 @@ inline void buildFT0FV0Words(TFT0 const& ft0, TFV0A const& fv0a, const auto a = ampsV[i]; const int c = chanV[i]; - if (c < 0 || c >= 48) { + if (c < 0 || c >= nChFV0A) { continue; } diff --git a/PWGUD/Tasks/upcFITMultiplicity.cxx b/PWGUD/Tasks/upcFITMultiplicity.cxx index f0fbc2eae0d..3f4d7e759e0 100644 --- a/PWGUD/Tasks/upcFITMultiplicity.cxx +++ b/PWGUD/Tasks/upcFITMultiplicity.cxx @@ -376,72 +376,6 @@ struct UpcTestFITBitMapping { "collision %d: fitBits.size=%d nFT0A=%d nFT0C=%d nFV0A=%d", collision.globalIndex(), fitBits.size(), nFT0A, nFT0C, nFV0A); } - /// Only print the first 5 entries for debugging purpose - if (collision.globalIndex() < 5) { - for (int bit = 96; bit < 208; ++bit) { - if (udhelpers::testBit(w1, bit)) { - LOGF(info, "ANALYSIS sees one fired bit %d", bit); - } - if (udhelpers::testBit(w2, bit)) { - LOGF(info, "ANALYSIS sees two fired bits %d", bit); - } - } - } - - /// Checking for special event geometry of rho (resonances that decays into 2 tracks, in general) - if (nFT0C == 2) { - for (int bit = 0; bit < udhelpers::kFT0Bits; ++bit) { - if (!udhelpers::testBit(w1, bit)) { - continue; - } - - double phi = 0., eta = 0.; - const bool ok = udhelpers::getPhiEtaFromFitBit(ft0Det, bit, offsetFT0, iRunOffset, phi, eta); - if (!ok) { - continue; - } - - auto pos = ft0Det.getChannelCenter(bit); - - const double x = pos.X() - offsetFT0[iRunOffset].getX(); - const double y = pos.Y() - offsetFT0[iRunOffset].getY(); - - if (bit >= udhelpers::kFT0AChannels) { - registry.fill(HIST("debug/hFT0CChannelOccupancy_2tracks"), bit); - registry.fill(HIST("map/hPhiC_2tracks"), phi); - registry.fill(HIST("map/hEtaC_2tracks"), eta); - registry.fill(HIST("map/hEtaPhiC_2tracks"), eta, phi); - registry.fill(HIST("map/hXYC_2tracks"), x, y); - } - } - } - - if (nFT0C > 2) { - for (int bit = 0; bit < udhelpers::kFT0Bits; ++bit) { - if (!udhelpers::testBit(w1, bit)) { - continue; - } - - double phi = 0., eta = 0.; - const bool ok = udhelpers::getPhiEtaFromFitBit(ft0Det, bit, offsetFT0, iRunOffset, phi, eta); - if (!ok) { - continue; - } - - auto pos = ft0Det.getChannelCenter(bit); - - const double x = pos.X() - offsetFT0[iRunOffset].getX(); - const double y = pos.Y() - offsetFT0[iRunOffset].getY(); - - if (bit >= udhelpers::kFT0AChannels) { - registry.fill(HIST("debug/hFT0CChannelOccupancy_ntracks"), bit); - registry.fill(HIST("map/hPhiC_ntracks"), phi); - registry.fill(HIST("map/hEtaC_ntracks"), eta); - registry.fill(HIST("map/hEtaPhiC_ntracks"), eta, phi); - registry.fill(HIST("map/hXYC_ntracks"), x, y); - } - } - } /* Mapping for at least 1 fired channel only */ for (int bit = 0; bit < udhelpers::kFT0Bits; ++bit) { diff --git a/PWGUD/Tasks/upcTestFitBitMapping.cxx b/PWGUD/Tasks/upcTestFitBitMapping.cxx index 106eee63a83..d7a4988a0e1 100644 --- a/PWGUD/Tasks/upcTestFitBitMapping.cxx +++ b/PWGUD/Tasks/upcTestFitBitMapping.cxx @@ -42,6 +42,14 @@ struct UpcTestFITBitMapping { SGSelector sgSelector; + constexpr int nChFT0A = 96; + constexpr int nChFT0C = 112; + constexpr int nChFV0A = 48; + + constexpr int kFT0AOffset = 0; + constexpr int kFT0COffset = 96; + constexpr int kFV0Offset = 208; + Configurable whichThr{"whichThr", 1, "Use 1=Thr1 bits or 2=Thr2 bits"}; Configurable maxEvents{"maxEvents", -1, "Process at most this many collisions (-1 = all)"}; Configurable debugPrintEvery{"debugPrintEvery", 1000, "Print every N events (-1 disables)"}; @@ -62,6 +70,8 @@ struct UpcTestFITBitMapping { Configurable useFV0AVeto{"useFV0AVeto", true, "Apply FV0-A total amplitude veto"}; Configurable cutFV0AmpAVeto{"cutFV0AmpAVeto", 50., "Maximum allowed FV0-A total amplitude for veto"}; + + Configurable cutZvertex{"cutZvertex", 10., "z-vertex cut"}; struct OffsetXYZ { double x{0.}, y{0.}, z{0.}; @@ -83,13 +93,13 @@ struct UpcTestFITBitMapping { {"debug/hFT0AChannelOccupancy", "FT0A fired channel occupancy;FT0A channel;counts", {HistType::kTH1F, {{97, -0.5, 96.5}}}}, {"debug/hFT0CChannelOccupancy", "FT0C fired channel occupancy;FT0C channel;counts", {HistType::kTH1F, {{112, 95.5, 207.5}}}}, - {"map/hPhiA", "FT0A #varphi;#varphi;counts", {HistType::kTH1F, {{9, 0, 2 * M_PI}}}}, + {"map/hPhiA", "FT0A #varphi;#varphi;counts", {HistType::kTH1F, {{9, 0, 2 * o2::constants::math::PI}}}}, {"map/hEtaA", "FT0A #eta;#eta;counts", {HistType::kTH1F, {{7, 3.4, 4.8}}}}, - {"map/hEtaPhiA", "FT0A #eta vs #varphi;#eta;#varphi", {HistType::kTH2F, {{7, 3.4, 4.8}, {9, 0, 2 * M_PI}}}}, + {"map/hEtaPhiA", "FT0A #eta vs #varphi;#eta;#varphi", {HistType::kTH2F, {{7, 3.4, 4.8}, {9, 0, 2 * o2::constants::math::PI}}}}, - {"map/hPhiC", "FT0C #varphi;#varphi;counts", {HistType::kTH1F, {{9, 0, 2 * M_PI}}}}, + {"map/hPhiC", "FT0C #varphi;#varphi;counts", {HistType::kTH1F, {{9, 0, 2 * o2::constants::math::PI}}}}, {"map/hEtaC", "FT0C #eta;#eta;counts", {HistType::kTH1F, {{5, -3.1, -2.1}}}}, - {"map/hEtaPhiC", "FT0C #eta vs #varphi;#eta;#varphi", {HistType::kTH2F, {{5, -3.1, -2.1}, {9, 0, 2 * M_PI}}}}, + {"map/hEtaPhiC", "FT0C #eta vs #varphi;#eta;#varphi", {HistType::kTH2F, {{5, -3.1, -2.1}, {9, 0, 2 * o2::constants::math::PI}}}}, {"mult/hPnFT0A", "P(n): FT0A fired-channel multiplicity;N_{fired}^{FT0A};events", {HistType::kTH1F, {{97, -0.5, 96.5}}}}, {"mult/hPnFT0C", "P(n): FT0C fired-channel multiplicity;N_{fired}^{FT0C};events", {HistType::kTH1F, {{50, -0.5, 49.5}}}}, @@ -178,7 +188,7 @@ struct UpcTestFITBitMapping { registry.fill(HIST("debug/hEventCounter"), 7.); /* z-vertex cut */ - if (std::abs(collision.posZ()) > 10.0) { + if (std::abs(collision.posZ()) > cutZvertex) { return false; } registry.fill(HIST("debug/hEventCounter"), 8.); @@ -214,10 +224,10 @@ struct UpcTestFITBitMapping { const float tA = collision.timeFT0A(); const float tC = collision.timeFT0C(); - if (abs(tA) > cutFT0TimeA) { + if (std::abs(tA) > cutFT0TimeA) { return false; } - if (abs(tC) > cutFT0TimeC) { + if (std::abs(tC) > cutFT0TimeC) { return false; } registry.fill(HIST("debug/hEventCounter"), 12.); @@ -350,12 +360,12 @@ struct UpcTestFITBitMapping { collision.globalIndex(), fitBits.size(), nFT0A, nFT0C, nFV0A); } if (collision.globalIndex() < 5) { - for (int bit = 96; bit < 208; ++bit) { + for (int bit = kFT0COffset; bit < nChFT0C + kFT0COffset; ++bit) { if (udhelpers::testBit(w1, bit)) { - LOGF(info, "ANALYSIS sees one fired bit %d", bit); + LOGF(info, "ANALYSIS sees one fired bit %d in FT0C", bit); } if (udhelpers::testBit(w2, bit)) { - LOGF(info, "ANALYSIS sees two fired bits %d", bit); + LOGF(info, "ANALYSIS sees two fired bits %d in FT0C", bit); } } } From f23402b064bde126875135f93d097cb7f1fcbe5b Mon Sep 17 00:00:00 2001 From: sandor-lokos Date: Wed, 8 Jul 2026 13:11:44 +0200 Subject: [PATCH 8/8] Converter implemented for FITbit tables version 000 to 001; bug fix in the new FITbit table; formatting issues fixed --- PWGUD/Core/UDHelpers.h | 2 +- PWGUD/Tasks/upcTestFitBitMapping.cxx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/PWGUD/Core/UDHelpers.h b/PWGUD/Core/UDHelpers.h index 544622fae1d..86d2aaa402e 100644 --- a/PWGUD/Core/UDHelpers.h +++ b/PWGUD/Core/UDHelpers.h @@ -563,7 +563,7 @@ inline void buildFT0FV0Words(TFT0 const& ft0, TFV0A const& fv0a, constexpr int nChFT0A = 96; constexpr int nChFT0C = 112; - constexpr int nChFV0A = 48; + constexpr int nChFV0A = 48; constexpr int kFT0AOffset = 0; constexpr int kFT0COffset = 96; diff --git a/PWGUD/Tasks/upcTestFitBitMapping.cxx b/PWGUD/Tasks/upcTestFitBitMapping.cxx index d7a4988a0e1..54ed876c0a7 100644 --- a/PWGUD/Tasks/upcTestFitBitMapping.cxx +++ b/PWGUD/Tasks/upcTestFitBitMapping.cxx @@ -44,7 +44,7 @@ struct UpcTestFITBitMapping { constexpr int nChFT0A = 96; constexpr int nChFT0C = 112; - constexpr int nChFV0A = 48; + constexpr int nChFV0A = 48; constexpr int kFT0AOffset = 0; constexpr int kFT0COffset = 96; @@ -70,8 +70,8 @@ struct UpcTestFITBitMapping { Configurable useFV0AVeto{"useFV0AVeto", true, "Apply FV0-A total amplitude veto"}; Configurable cutFV0AmpAVeto{"cutFV0AmpAVeto", 50., "Maximum allowed FV0-A total amplitude for veto"}; - - Configurable cutZvertex{"cutZvertex", 10., "z-vertex cut"}; + + Configurable cutZvertex{"cutZvertex", 10., "z-vertex cut"}; struct OffsetXYZ { double x{0.}, y{0.}, z{0.};