diff --git a/PWGUD/Core/CMakeLists.txt b/PWGUD/Core/CMakeLists.txt index 7686edd8eea..3a7fe0950cf 100644 --- a/PWGUD/Core/CMakeLists.txt +++ b/PWGUD/Core/CMakeLists.txt @@ -64,3 +64,11 @@ o2physics_add_library(decayTree o2physics_target_root_dictionary(decayTree HEADERS decayTree.h LINKDEF decayTreeLinkDef.h) + +o2physics_add_library(FITCutParHolder + SOURCES FITCutParHolder.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore) + +o2physics_target_root_dictionary(FITCutParHolder + HEADERS FITCutParHolder.h + LINKDEF FITCutParHolderLinkDef.h) \ No newline at end of file diff --git a/PWGUD/Core/FITCutParHolder.cxx b/PWGUD/Core/FITCutParHolder.cxx new file mode 100644 index 00000000000..d57e2c8e12f --- /dev/null +++ b/PWGUD/Core/FITCutParHolder.cxx @@ -0,0 +1,55 @@ +// 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. +// +// \FIT bit thresholds +// \author Sandor Lokos, sandor.lokos@cern.ch +// \since March 2026 + +#include "FITCutParHolder.h" + +// setter +void FITCutParHolder::SetSaveFITbitsets(bool saveFITbitsets) +{ + mSaveFITbitsets = saveFITbitsets; +} +void FITCutParHolder::SetThr1FV0A(float thr1_FV0A) +{ + mThr1FV0A = thr1_FV0A; +} +void FITCutParHolder::SetThr1FT0A(float thr1_FT0A) +{ + mThr1FT0A = thr1_FT0A; +} +void FITCutParHolder::SetThr1FT0C(float thr1_FT0C) +{ + mThr1FT0C = thr1_FT0C; +} +void FITCutParHolder::SetThr2FV0A(float thr2_FV0A) +{ + mThr2FV0A = thr2_FV0A; +} +void FITCutParHolder::SetThr2FT0A(float thr2_FT0A) +{ + mThr2FT0A = thr2_FT0A; +} +void FITCutParHolder::SetThr2FT0C(float thr2_FT0C) +{ + mThr2FT0C = thr2_FT0C; +} + +// getter +bool FITCutParHolder::saveFITbitsets() const { return mSaveFITbitsets; } +float FITCutParHolder::thr1_FV0A() const { return mThr1FV0A; } +float FITCutParHolder::thr1_FT0A() const { return mThr1FT0A; } +float FITCutParHolder::thr1_FT0C() const { return mThr1FT0C; } +float FITCutParHolder::thr2_FV0A() const { return mThr2FV0A; } +float FITCutParHolder::thr2_FT0A() const { return mThr2FT0A; } +float FITCutParHolder::thr2_FT0C() const { return mThr2FT0C; } diff --git a/PWGUD/Core/FITCutParHolder.h b/PWGUD/Core/FITCutParHolder.h new file mode 100644 index 00000000000..e03c4161c27 --- /dev/null +++ b/PWGUD/Core/FITCutParHolder.h @@ -0,0 +1,75 @@ +// 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. +// +// \FIT bit thresholds +// \author Sandor Lokos, sandor.lokos@cern.ch +// \since March 2026 + +#ifndef PWGUD_CORE_FITCUTPARHOLDER_H_ +#define PWGUD_CORE_FITCUTPARHOLDER_H_ + +#include + +// object to hold customizable FIT bit thresholds +class FITCutParHolder +{ + public: + // constructor + FITCutParHolder(bool saveFITbitsets = true, + float thr1_FV0A = 8., + float thr1_FT0A = 8., + float thr1_FT0C = 8., + float thr2_FV0A = 20., + float thr2_FT0A = 20., + float thr2_FT0C = 20.) + : mSaveFITbitsets{saveFITbitsets}, + mThr1FV0A{thr1_FV0A}, + mThr1FT0A{thr1_FT0A}, + mThr1FT0C{thr1_FT0C}, + mThr2FV0A{thr2_FV0A}, + mThr2FT0A{thr2_FT0A}, + mThr2FT0C{thr2_FT0C} + { + } + + // setters + void SetSaveFITbitsets(bool); + void SetThr1FV0A(float); + void SetThr1FT0A(float); + void SetThr1FT0C(float); + void SetThr2FV0A(float); + void SetThr2FT0A(float); + void SetThr2FT0C(float); + + // getters + bool saveFITbitsets() const; + float thr1_FV0A() const; + float thr1_FT0A() const; + float thr1_FT0C() const; + float thr2_FV0A() const; + float thr2_FT0A() const; + float thr2_FT0C() const; + + private: + bool mSaveFITbitsets; + + float mThr1FV0A; + float mThr1FT0A; + float mThr1FT0C; + + float mThr2FV0A; + float mThr2FT0A; + float mThr2FT0C; + + ClassDefNV(FITCutParHolder, 1); +}; + +#endif // PWGUD_CORE_FITCUTPARHOLDER_H_ diff --git a/PWGUD/Core/FITCutParHolderLinkDef.h b/PWGUD/Core/FITCutParHolderLinkDef.h new file mode 100644 index 00000000000..3194a6754b2 --- /dev/null +++ b/PWGUD/Core/FITCutParHolderLinkDef.h @@ -0,0 +1,24 @@ +// 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. +// +// \FIT bit thresholds +// \author Sandor Lokos, sandor.lokos@cern.ch +// \since March 2026 + +#ifndef PWGUD_CORE_FITCUTPARHOLDERLINKDEF_H_ +#define PWGUD_CORE_FITCUTPARHOLDERLINKDEF_H_ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; +#pragma link C++ class FITCutParHolder + ; + +#endif // PWGUD_CORE_FITCUTPARHOLDERLINKDEF_H_ diff --git a/PWGUD/Core/SGCutParHolderLinkDef.h b/PWGUD/Core/SGCutParHolderLinkDef.h index f9216fa0c37..5dcadadc69e 100644 --- a/PWGUD/Core/SGCutParHolderLinkDef.h +++ b/PWGUD/Core/SGCutParHolderLinkDef.h @@ -8,6 +8,7 @@ // 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. + #ifndef PWGUD_CORE_SGCUTPARHOLDERLINKDEF_H_ #define PWGUD_CORE_SGCUTPARHOLDERLINKDEF_H_ diff --git a/PWGUD/Core/UDHelpers.h b/PWGUD/Core/UDHelpers.h index 1248164dd0f..73a3efcd775 100644 --- a/PWGUD/Core/UDHelpers.h +++ b/PWGUD/Core/UDHelpers.h @@ -19,6 +19,7 @@ #include "PWGUD/Core/DGCutparHolder.h" #include "PWGUD/Core/UPCHelpers.h" +#include "Common/Core/RecoDecay.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -29,6 +30,7 @@ #include "TLorentzVector.h" +#include #include #include @@ -533,6 +535,160 @@ bool FITveto(T const& bc, DGCutparHolder const& diffCuts) return false; } +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); +} + +template +inline void buildFT0FV0Words(TFT0 const& ft0, TFV0A const& fv0a, + uint64_t thr1[4], uint64_t thr2[4], + float thr1_FT0A = 25., float thr1_FT0C = 50., float thr1_FV0A = 50., + float thr2_FT0A = 50., float thr2_FT0C = 100., float thr2_FV0A = 100.) +{ + thr1[0] = thr1[1] = thr1[2] = thr1[3] = 0ull; + thr2[0] = thr2[1] = thr2[2] = thr2[3] = 0ull; + + constexpr int kFT0AOffset = 0; + constexpr int kFT0COffset = 96; + constexpr int kFV0Offset = 208; + + auto ampsA = ft0.amplitudeA(); + const int nA = std::min(ampsA.size(), 96); + 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); + } + + auto ampsC = ft0.amplitudeC(); + const int nC = std::min(ampsC.size(), 112); + 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); + } + + auto ampsV = fv0a.amplitude(); + const int nV = std::min(ampsV.size(), 48); + 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); + } +} + +// ----------------------------------------------------------------------------- +// return eta and phi of a given FIT channel based on the bitset +// Bit layout contract: +constexpr int kFT0Bits = 208; // FT0 total channels +constexpr int kFV0Bits = 48; // FV0A channels +constexpr int kTotalBits = 256; // 4*64 + +// FT0 side split +constexpr int kFT0AChannels = 96; // FT0A channels are [0..95] +constexpr int kFT0CChannels = 112; // FT0C channels are [96..207] +static_assert(kFT0AChannels + kFT0CChannels == kFT0Bits); + +using Bits256 = std::array; + +inline Bits256 makeBits256(uint64_t w0, uint64_t w1, uint64_t w2, uint64_t w3) +{ + return {w0, w1, w2, w3}; +} + +inline bool testBit(Bits256 const& w, int bit) +{ + if (bit < 0 || bit >= kTotalBits) { + return false; + } + return (w[bit >> 6] >> (bit & 63)) & 1ULL; +} + +struct FitBitRef { + enum class Det : uint8_t { FT0, + FV0, + Unknown }; + Det det = Det::Unknown; + int ch = -1; // FT0: 0..207, FV0: 0..47 + bool isC = false; // only meaningful for FT0 +}; + +inline FitBitRef decodeFitBit(int bit) +{ + FitBitRef out; + if (bit >= 0 && bit < kFT0Bits) { + out.det = FitBitRef::Det::FT0; + out.ch = bit; // FT0 channel id + out.isC = (bit >= kFT0AChannels); // C side if in upper range + return out; + } + if (bit >= kFT0Bits && bit < kTotalBits) { + out.det = FitBitRef::Det::FV0; + out.ch = bit - kFT0Bits; // FV0A channel id 0..47 + return out; + } + return out; +} + +template +inline double getPhiFT0_fromChannel(FT0DetT& ft0Det, int ft0Ch, OffsetsT const& offsetFT0, int i) +{ + ft0Det.calculateChannelCenter(); + auto chPos = ft0Det.getChannelCenter(ft0Ch); + + const double x = chPos.X() + offsetFT0[i].getX(); + const double y = chPos.Y() + offsetFT0[i].getY(); + + return RecoDecay::phi(x, y); +} + +template +inline double getEtaFT0_fromChannel(FT0DetT& ft0Det, int ft0Ch, OffsetsT const& offsetFT0, int i) +{ + ft0Det.calculateChannelCenter(); + auto chPos = ft0Det.getChannelCenter(ft0Ch); + + double x = chPos.X() + offsetFT0[i].getX(); + 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) + const bool isC = (ft0Ch >= kFT0AChannels); + if (isC) { + z = -z; + } + + const double r = std::sqrt(x * x + y * y); + const double theta = std::atan2(r, z); + return -std::log(std::tan(0.5 * theta)); +} + +template +inline bool getPhiEtaFromFitBit(FT0DetT& ft0Det, + int bit, + OffsetsT const& offsetFT0, + int iRunOffset, + double& phi, + double& eta) +{ + auto ref = decodeFitBit(bit); + if (ref.det != FitBitRef::Det::FT0) { + return false; + } + + // FT0A: 0..95, FT0C: 96..207 + const int ft0Ch = bit; + phi = getPhiFT0_fromChannel(ft0Det, ft0Ch, offsetFT0, iRunOffset); + eta = getEtaFT0_fromChannel(ft0Det, ft0Ch, offsetFT0, iRunOffset); + return true; +} + // ----------------------------------------------------------------------------- template diff --git a/PWGUD/DataModel/UDTables.h b/PWGUD/DataModel/UDTables.h index 65fdc63eb4b..774bb31751a 100644 --- a/PWGUD/DataModel/UDTables.h +++ b/PWGUD/DataModel/UDTables.h @@ -407,6 +407,31 @@ DECLARE_SOA_TABLE(UDTracksFlags, "AOD", "UDTRACKFLAG", DECLARE_SOA_TABLE(UDTracksLabels, "AOD", "UDTRACKLABEL", udtrack::TrackId); +namespace udcollfitbits +{ +DECLARE_SOA_COLUMN(Thr1W0, thr1W0, uint64_t); /// 1 MIP thresholds for FT0A ch 0 - ch 63 +DECLARE_SOA_COLUMN(Thr1W1, thr1W1, uint64_t); /// 1 MIP thresholds for FT0A ch 64 - ch 96 & FT0C ch 0 - ch 31 +DECLARE_SOA_COLUMN(Thr1W2, thr1W2, uint64_t); /// 1 MIP thresholds for FT0C ch 32 - ch 96 +DECLARE_SOA_COLUMN(Thr1W3, thr1W3, uint64_t); /// 1 MIP thresholds for FT0C ch 97 - 112 & FV0 0 - 47 + +DECLARE_SOA_COLUMN(Thr2W0, thr2W0, uint64_t); /// 2 MIP thresholds for FT0A ch 0 - ch 63 +DECLARE_SOA_COLUMN(Thr2W1, thr2W1, uint64_t); /// 2 MIP thresholds for FT0A ch 63 - ch 96 & FT0C ch 0 - ch 31 +DECLARE_SOA_COLUMN(Thr2W2, thr2W2, uint64_t); /// 2 MIP thresholds for FT0C ch 32 - ch 96 +DECLARE_SOA_COLUMN(Thr2W3, thr2W3, uint64_t); /// 2 MIP thresholds for FT0C ch 97 - 112 & FV0 0 - 47 +} // namespace udcollfitbits + +DECLARE_SOA_TABLE(UDCollisionFITBits, "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 +); + using UDTrack = UDTracks::iterator; using UDTrackCov = UDTracksCov::iterator; using UDTrackExtra = UDTracksExtra::iterator; diff --git a/PWGUD/TableProducer/CMakeLists.txt b/PWGUD/TableProducer/CMakeLists.txt index fb3bd5e2275..c16870f0603 100644 --- a/PWGUD/TableProducer/CMakeLists.txt +++ b/PWGUD/TableProducer/CMakeLists.txt @@ -18,7 +18,7 @@ o2physics_add_dpl_workflow(dg-cand-producer o2physics_add_dpl_workflow(sgcand-producer SOURCES SGCandProducer.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::SGCutParHolder O2Physics::AnalysisCCDB + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::SGCutParHolder O2Physics::AnalysisCCDB O2Physics::FITCutParHolder COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(dgbccand-producer diff --git a/PWGUD/TableProducer/SGCandProducer.cxx b/PWGUD/TableProducer/SGCandProducer.cxx index 888dbfb608e..b2702b54546 100644 --- a/PWGUD/TableProducer/SGCandProducer.cxx +++ b/PWGUD/TableProducer/SGCandProducer.cxx @@ -12,12 +12,13 @@ /// \file SGCandProducer.cxx /// \brief Produces PWGUD derived table from standard tables /// -/// \author Alexander Bylinkin , Uniersity of Bergen -/// \since 23.11.2023 +/// \author Alexander Bylinkin , University of Bergen +/// \since 23.11.2023 /// \author Adam Matyja , INP PAN Krakow, Poland -/// \since May 2025 +/// \since May 2025 // +#include "PWGUD/Core/FITCutParHolder.h" #include "PWGUD/Core/SGSelector.h" #include "PWGUD/Core/UPCHelpers.h" #include "PWGUD/DataModel/UDTables.h" @@ -41,6 +42,7 @@ #include "Framework/runDataProcessing.h" #include "ReconstructionDataFormats/Vertex.h" +#include #include #include #include @@ -75,6 +77,8 @@ struct SGCandProducer { // get an SGCutparHolder SGCutParHolder sameCuts = SGCutParHolder(); // SGCutparHolder Configurable SGCuts{"SGCuts", {}, "SG event cuts"}; + FITCutParHolder fitCuts = FITCutParHolder(); + Configurable FITCuts{"FITCuts", {}, "FIT bitset cuts"}; Configurable verboseInfo{"verboseInfo", false, "Print general info to terminal; default it false."}; Configurable saveAllTracks{"saveAllTracks", true, "save only PV contributors or all tracks associated to a collision"}; Configurable savenonPVCITSOnlyTracks{"savenonPVCITSOnlyTracks", false, "save non PV contributors with ITS only information"}; @@ -87,6 +91,8 @@ struct SGCandProducer { Configurable storeSG{"storeSG", true, "Store SG events in the output"}; Configurable storeDG{"storeDG", true, "Store DG events in the output"}; + Configurable saveFITbitsets{"saveFITbitsets", true, "Write FT0 and FV0 bitset tables to output"}; + Configurable isGoodRCTCollision{"isGoodRCTCollision", true, "Check RCT flags for FT0,ITS,TPC and tracking"}; Configurable isGoodRCTZdc{"isGoodRCTZdc", false, "Check RCT flags for ZDC if present in run"}; @@ -94,7 +100,7 @@ struct SGCandProducer { Configurable fillTrackTables{"fillTrackTables", true, "Fill track tables"}; Configurable fillFwdTrackTables{"fillFwdTrackTables", true, "Fill forward track tables"}; - // SG selector + // SG selector SGSelector sgSelector; ctpRateFetcher mRateFetcher; @@ -119,6 +125,7 @@ struct SGCandProducer { Produces outputFwdTracks; Produces outputFwdTracksExtra; Produces outputTracksLabel; + Produces outputFITBits; // initialize histogram registry HistogramRegistry registry{ @@ -205,78 +212,6 @@ struct SGCandProducer { outputTracksLabel(track.globalIndex()); } - // function to process trigger counters, accounting for BC selection bits - void processCountersTrg(BCs const& bcs, aod::FT0s const&, aod::Zdcs const&) - { - const auto& firstBc = bcs.iteratorAt(0); - if (runNumber != firstBc.runNumber()) - runNumber = firstBc.runNumber(); - - auto hCountersTrg = getHist(TH1, "reco/hCountersTrg"); - auto hCountersTrgBcSel = getHist(TH1, "reco/hCountersTrgBcSel"); - auto hLumi = getHist(TH1, "reco/hLumi"); - auto hLumiBcSel = getHist(TH1, "reco/hLumiBcSel"); - - // Cross sections in ub. Using dummy -1 if lumi estimator is not reliable - float csTCE = 10.36e6; - const float csZEM = 415.2e6; // see AN: https://alice-notes.web.cern.ch/node/1515 - const float csZNC = 214.5e6; // see AN: https://alice-notes.web.cern.ch/node/1515 - if (runNumber > 543437 && runNumber < 543514) { - csTCE = 8.3e6; - } - if (runNumber >= 543514) { - csTCE = 4.10e6; // see AN: https://alice-notes.web.cern.ch/node/1515 - } - - for (const auto& bc : bcs) { - bool hasFT0 = bc.has_foundFT0(); - bool hasZDC = bc.has_foundZDC(); - if (!hasFT0 && !hasZDC) - continue; - bool isSelectedBc = true; - if (rejectAtTFBoundary && !bc.selection_bit(aod::evsel::kNoTimeFrameBorder)) - isSelectedBc = false; - if (noITSROFrameBorder && !bc.selection_bit(aod::evsel::kNoITSROFrameBorder)) - isSelectedBc = false; - if (hasFT0) { - auto ft0TrgMask = bc.ft0().triggerMask(); - if (TESTBIT(ft0TrgMask, o2::fit::Triggers::bitVertex)) { - hCountersTrg->Fill("TVX", 1); - if (isSelectedBc) - hCountersTrgBcSel->Fill("TVX", 1); - } - if (TESTBIT(ft0TrgMask, o2::fit::Triggers::bitVertex) && TESTBIT(ft0TrgMask, o2::fit::Triggers::bitCen)) { - hCountersTrg->Fill("TCE", 1); - hLumi->Fill("TCE", 1. / csTCE); - if (isSelectedBc) { - hCountersTrgBcSel->Fill("TCE", 1); - hLumiBcSel->Fill("TCE", 1. / csTCE); - } - } - } - if (hasZDC) { - if (bc.selection_bit(aod::evsel::kIsBBZNA) || bc.selection_bit(aod::evsel::kIsBBZNC)) { - hCountersTrg->Fill("ZEM", 1); - hLumi->Fill("ZEM", 1. / csZEM); - if (isSelectedBc) { - hCountersTrgBcSel->Fill("ZEM", 1); - hLumiBcSel->Fill("ZEM", 1. / csZEM); - } - } - if (bc.selection_bit(aod::evsel::kIsBBZNC)) { - hCountersTrg->Fill("ZNC", 1); - hLumi->Fill("ZNC", 1. / csZNC); - if (isSelectedBc) { - hCountersTrgBcSel->Fill("ZNC", 1); - hLumiBcSel->Fill("ZNC", 1. / csZNC); - } - } - } - } - } - - PROCESS_SWITCH(SGCandProducer, processCountersTrg, "Produce trigger counters and luminosity histograms", true); - // function to process reconstructed data template void processReco(std::string histdir, TCol const& collision, BCs const& bcs, @@ -284,8 +219,7 @@ struct SGCandProducer { aod::FV0As const& fv0as, aod::FT0s const& ft0s, aod::FDDs const& fdds) { if (verboseInfo) - LOGF(debug, " collision %d", collision.globalIndex()); - + LOGF(debug, " collision %d", collision.globalIndex()); getHist(TH1, histdir + "/Stat")->Fill(0., 1.); // reject collisions at TF boundaries if (rejectAtTFBoundary && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { @@ -393,6 +327,23 @@ struct SGCandProducer { if (verboseInfo) LOGF(info, "%s Coll GID %d", histdir, collision.globalIndex()); outputCollsLabels(collision.globalIndex()); + + uint64_t w1[4] = {0ull, 0ull, 0ull, 0ull}; + uint64_t w2[4] = {0ull, 0ull, 0ull, 0ull}; + + if (fitCuts.saveFITbitsets() && newbc.has_foundFT0() && newbc.has_fv0a()) { + udhelpers::buildFT0FV0Words(newbc.ft0(), newbc.fv0a(), w1, w2, + fitCuts.thr1_FT0A(), + fitCuts.thr1_FT0C(), + fitCuts.thr1_FV0A(), + fitCuts.thr2_FT0A(), + fitCuts.thr2_FT0C(), + fitCuts.thr2_FV0A()); + } + + outputFITBits(w1[0], w1[1], w1[2], w1[3], + w2[0], w2[1], w2[2], w2[3]); + if (newbc.has_zdc()) { auto zdc = newbc.zdc(); udZdcsReduced(outputCollisions.lastIndex(), zdc.timeZNA(), zdc.timeZNC(), zdc.energyCommonZNA(), zdc.energyCommonZNC()); @@ -408,7 +359,7 @@ struct SGCandProducer { } else if (saveAllTracks) { if (track.itsClusterSizes() && track.itsChi2NCl() > 0 && ((track.tpcNClsFindable() == 0 && savenonPVCITSOnlyTracks) || track.tpcNClsFindable() > 50)) updateUDTrackTables(outputCollisions.lastIndex(), track, bc.globalBC()); - // if (track.isPVContributor()) updateUDTrackTables(outputCollisions.lastIndex(), track, bc.globalBC()); + // if (track.isPVContributor()) updateUDTrackTables(outputCollisions.lastIndex(), track, bc.globalBC()); } } } @@ -431,6 +382,7 @@ struct SGCandProducer { ccdb->setCaching(true); ccdb->setFatalWhenNull(false); sameCuts = (SGCutParHolder)SGCuts; + fitCuts = (FITCutParHolder)FITCuts; // add histograms for the different process functions histPointers.clear(); @@ -592,7 +544,7 @@ struct McSGCandProducer { for (const auto& oldmid : oldmids) { auto m = McParts.rawIteratorAt(oldmid); if (verboseInfoMC) - LOGF(debug, " m %d", m.globalIndex()); + LOGF(debug, "m %d", m.globalIndex()); if (mcPartIsSaved.find(oldmid) != mcPartIsSaved.end()) { newval = mcPartIsSaved[oldmid]; } else { @@ -741,7 +693,7 @@ struct McSGCandProducer { if (mcsgId >= 0 && mcOfInterest) { if (mcColIsSaved.find(mcsgId) == mcColIsSaved.end()) { if (verboseInfoMC) - LOGF(info, " Saving McCollision %d", mcsgId); + LOGF(info, "Saving McCollision %d", mcsgId); // update UDMcCollisions auto sgcandMcCol = sgcand.collision_as().mcCollision(); updateUDMcCollisions(sgcandMcCol, globBC); @@ -762,7 +714,7 @@ struct McSGCandProducer { // If the sgcand has no associated McCollision then only the McParticles which are associated // with the tracks of the sgcand are saved if (verboseInfoMC) - LOGF(info, " Saving McCollision %d", -1); + LOGF(info, "Saving McCollision %d", -1); // update UDMcColsLabels (for each UDCollision -> UDMcCollisions) outputMcCollsLabels(-1); @@ -804,7 +756,7 @@ struct McSGCandProducer { // but only consider generated events of interest if (mcOfInterest && mcColIsSaved.find(mccolId) == mcColIsSaved.end()) { if (verboseInfoMC) - LOGF(info, " Saving McCollision %d", mccolId); + LOGF(info, "Saving McCollision %d", mccolId); // update UDMcCollisions updateUDMcCollisions(mccol, globBC); mcColIsSaved[mccolId] = outputMcCollisions.lastIndex(); @@ -848,7 +800,7 @@ struct McSGCandProducer { // update UDMcCollisions and UDMcParticles if (mcColIsSaved.find(mccolId) == mcColIsSaved.end()) { if (verboseInfoMC) - LOGF(info, " Saving McCollision %d", mccolId); + LOGF(info, "Saving McCollision %d", mccolId); // update UDMcCollisions updateUDMcCollisions(mccol, globBC); diff --git a/PWGUD/Tasks/CMakeLists.txt b/PWGUD/Tasks/CMakeLists.txt index 62c6abc732f..d8aa575b906 100644 --- a/PWGUD/Tasks/CMakeLists.txt +++ b/PWGUD/Tasks/CMakeLists.txt @@ -268,3 +268,8 @@ o2physics_add_dpl_workflow(sg-exclusive-jpsi-midrapidity SOURCES sgExclusiveJpsiMidrapidity.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(fitbit-mapping + SOURCES upcTestFITBitMapping.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) \ No newline at end of file diff --git a/PWGUD/Tasks/upcTestFITBitMapping.cxx b/PWGUD/Tasks/upcTestFITBitMapping.cxx new file mode 100644 index 00000000000..8456c46dc85 --- /dev/null +++ b/PWGUD/Tasks/upcTestFITBitMapping.cxx @@ -0,0 +1,113 @@ +// 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. +// +// \FIT bits to phi, eta mapping +// \author Sandor Lokos, sandor.lokos@cern.ch +// \since March 2026 + +#include "PWGUD/Core/UDHelpers.h" // udhelpers::Bits256, makeBits256, testBit, getPhiEtaFromFitBit +#include "PWGUD/DataModel/UDTables.h" // aod::UDCollisionFITBits + +#include "FT0Base/Geometry.h" // o2::ft0::Geometry +#include "Framework/AnalysisTask.h" +#include "Framework/HistogramRegistry.h" +#include "Framework/runDataProcessing.h" + +#include +#include + +using namespace o2; +using namespace o2::framework; + +struct UpcTestFITBitMapping { + Configurable whichThr{"whichThr", 1, "Use 1=Thr1 bits or 2=Thr2 bits"}; + Configurable maxEvents{"maxEvents", -1, "Process at most this many rows (-1 = all)"}; + + // Minimal offset container compatible with UDHelpers.h expectations: getX/getY/getZ + 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{}; // iRunOffset = 0 for now + int iRunOffset = 0; + + o2::ft0::Geometry ft0Det{}; + + HistogramRegistry registry{ + "registry", + { + {"hPhiA", "FT0A #varphi;#varphi;counts", {HistType::kTH1F, {{18, 0.0, 2. * M_PI}}}}, + {"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, 2. * M_PI}}}}, + + {"hPhiC", "FT0C #varphi;#varphi;counts", {HistType::kTH1F, {{18, 0.0, 2. * M_PI}}}}, + {"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, 2. * M_PI}}}}, + }}; + + void init(InitContext&) + { + // UDHelpers calls calculateChannelCenter() inside, but doing it once here is fine. + ft0Det.calculateChannelCenter(); + } + + void process(aod::UDCollisionFITBits const& bitsTable) + { + int64_t nProcessed = 0; + + for (auto const& row : bitsTable) { + if (maxEvents >= 0 && nProcessed >= maxEvents) { + break; + } + ++nProcessed; + + // Use udhelpers' canonical packed type + builder + udhelpers::Bits256 w{}; + if (whichThr == 2) { + w = udhelpers::makeBits256(row.thr2W0(), row.thr2W1(), row.thr2W2(), row.thr2W3()); + } else { + w = udhelpers::makeBits256(row.thr1W0(), row.thr1W1(), row.thr1W2(), row.thr1W3()); + } + + // Loop FT0 bits only (0..207). FV0 starts at 208 but ignored here. + for (int bit = 0; bit < udhelpers::kFT0Bits; ++bit) { + if (!udhelpers::testBit(w, bit)) { + continue; + } + + double phi = 0., eta = 0.; + const bool ok = udhelpers::getPhiEtaFromFitBit(ft0Det, bit, offsetFT0, iRunOffset, phi, eta); + if (!ok) { + continue; + } + + if (bit < udhelpers::kFT0AChannels) { + registry.fill(HIST("hPhiA"), phi); + registry.fill(HIST("hEtaA"), eta); + registry.fill(HIST("hEtaPhiA"), eta, phi); + } else { + registry.fill(HIST("hPhiC"), phi); + registry.fill(HIST("hEtaC"), eta); + registry.fill(HIST("hEtaPhiC"), eta, phi); + } + } + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc, TaskName{"fitbit-mapping"})}; +}