99// granted to it by virtue of its status as an Intergovernmental Organization
1010// or submit itself to any jurisdiction.
1111//
12- // \FIT bits to phi, eta mapping
12+ // \brief FIT bits to phi, eta mapping + multiplicity from FIT bits
1313// \author Sandor Lokos, sandor.lokos@cern.ch
14- // \since March 2026
14+ // \since March 2026
1515
16- #include " PWGUD/Core/UDHelpers.h" // udhelpers::Bits256, makeBits256, testBit, getPhiEtaFromFitBit
17- #include " PWGUD/DataModel/UDTables.h" // aod::UDCollisionFITBits
16+ #include " PWGUD/Core/SGSelector.h"
17+ #include " PWGUD/Core/UDHelpers.h"
18+ #include " PWGUD/DataModel/UDTables.h"
1819
19- #include " FT0Base/Geometry.h" // o2::ft0::Geometry
20+ #include " FT0Base/Geometry.h"
2021#include " Framework/AnalysisTask.h"
2122#include " Framework/HistogramRegistry.h"
23+ #include " Framework/Logger.h"
2224#include " Framework/runDataProcessing.h"
2325
2426#include < array>
2527#include < cmath>
28+ #include < cstdint>
2629
2730using namespace o2 ;
2831using namespace o2 ::framework;
2932
33+ // using UDCollisionsFull = soa::Join<
34+ // aod::UDCollisions,
35+ // aod::UDCollisionsSels,
36+ // aod::UDCollisionSelExtras>;
37+
38+ using UDCollisionsFull = aod::UDCollisions;
39+
3040struct UpcTestFITBitMapping {
31- Configurable<int > whichThr{" whichThr" , 1 , " Use 1=Thr1 bits or 2=Thr2 bits" };
32- Configurable<int > maxEvents{" maxEvents" , -1 , " Process at most this many rows (-1 = all)" };
41+
42+ SGSelector sgSelector;
43+
44+ Configurable<int > whichThr{" whichThr" , 1 , " Use 1=Thr1 bits or 2=Thr2 bits" };
45+ Configurable<int > maxEvents{" maxEvents" , -1 , " Process at most this many collisions (-1 = all)" };
46+ Configurable<int > debugPrintEvery{" debugPrintEvery" , 1000 , " Print every N events (-1 disables)" };
47+ Configurable<int > debugPrintFirst{" debugPrintFirst" , 10 , " Always print first N events" };
48+ Configurable<bool > useRctFlag{" useRctFlag" , false , " use RCT flags for event selection" };
49+ Configurable<int > cutRctFlag{" cutRctFlag" , 0 , " 0 = off, 1 = CBT, 2 = CBT+ZDC, 3 = CBThadron, 4 = CBThadron+ZDC" };
3350
34- // Minimal offset container compatible with UDHelpers.h expectations: getX/getY/getZ
3551 struct OffsetXYZ {
36- double x{0 }, y{0 }, z{0 };
52+ double x{0 . }, y{0 . }, z{0 . };
3753 double getX () const { return x; }
3854 double getY () const { return y; }
3955 double getZ () const { return z; }
4056 };
4157
42- std::array<OffsetXYZ, 1 > offsetFT0{}; // iRunOffset = 0 for now
58+ std::array<OffsetXYZ, 1 > offsetFT0{};
4359 int iRunOffset = 0 ;
4460
4561 o2::ft0::Geometry ft0Det{};
4662
4763 HistogramRegistry registry{
4864 " registry" ,
4965 {
50- {" hPhiA" , " FT0A #varphi;#varphi;counts" , {HistType::kTH1F , {{18 , 0.0 , 2 . * M_PI }}}},
51- {" hEtaA" , " FT0A #eta;#eta;counts" , {HistType::kTH1F , {{8 , 3.5 , 5.0 }}}},
52- {" hEtaPhiA" , " FT0A #eta vs #varphi;#eta;#varphi" , {HistType::kTH2F , {{8 , 3.5 , 5.0 }, {18 , 0.0 , 2 . * M_PI }}}},
66+ {" debug/hEventCounter" , " Event counter;step;events" , {HistType::kTH1F , {{6 , -0.5 , 5.5 }}}},
67+ {" debug/hFitBitsSize" , " fitBits.size() per collision;fitBits.size();events" , {HistType::kTH1F , {{10 , -0.5 , 9.5 }}}},
68+ {" debug/hCollisionIndexMod" , " Collision index mod 100;collision.globalIndex() % 100;events" , {HistType::kTH1F , {{100 , -0.5 , 99.5 }}}},
69+
70+ {" map/hPhiA" , " FT0A #varphi;#varphi;counts" , {HistType::kTH1F , {{18 , 0.0 , 2 . * M_PI }}}},
71+ {" map/hEtaA" , " FT0A #eta;#eta;counts" , {HistType::kTH1F , {{8 , 3.5 , 5.0 }}}},
72+ {" map/hEtaPhiA" , " FT0A #eta vs #varphi;#eta;#varphi" , {HistType::kTH2F , {{8 , 3.5 , 5.0 }, {18 , 0.0 , 2 . * M_PI }}}},
5373
54- {" hPhiC" , " FT0C #varphi;#varphi;counts" , {HistType::kTH1F , {{18 , 0.0 , 2 . * M_PI }}}},
55- {" hEtaC" , " FT0C #eta;#eta;counts" , {HistType::kTH1F , {{8 , -3.5 , -2.0 }}}},
56- {" hEtaPhiC" , " FT0C #eta vs #varphi;#eta;#varphi" , {HistType::kTH2F , {{8 , -3.5 , -2.0 }, {18 , 0.0 , 2 . * M_PI }}}},
74+ {" map/hPhiC" , " FT0C #varphi;#varphi;counts" , {HistType::kTH1F , {{18 , 0.0 , 2 . * M_PI }}}},
75+ {" map/hEtaC" , " FT0C #eta;#eta;counts" , {HistType::kTH1F , {{8 , -3.5 , -2.0 }}}},
76+ {" map/hEtaPhiC" , " FT0C #eta vs #varphi;#eta;#varphi" , {HistType::kTH2F , {{8 , -3.5 , -2.0 }, {18 , 0.0 , 2 . * M_PI }}}},
77+
78+ {" mult/hPnFT0A" , " P(n): FT0A fired-channel multiplicity;N_{fired}^{FT0A};events" , {HistType::kTH1F , {{97 , -0.5 , 96.5 }}}},
79+ {" mult/hPnFT0C" , " P(n): FT0C fired-channel multiplicity;N_{fired}^{FT0C};events" , {HistType::kTH1F , {{113 , -0.5 , 112.5 }}}},
80+ {" mult/hPnFT0" , " P(n): FT0 fired-channel multiplicity;N_{fired}^{FT0};events" , {HistType::kTH1F , {{209 , -0.5 , 208.5 }}}},
81+ {" mult/hPnFV0A" , " P(n): FV0A fired-channel multiplicity;N_{fired}^{FV0A};events" , {HistType::kTH1F , {{49 , -0.5 , 48.5 }}}},
82+ {" mult/hPnFIT" , " P(n): FIT fired-channel multiplicity;N_{fired}^{FIT};events" , {HistType::kTH1F , {{257 , -0.5 , 256.5 }}}},
83+
84+ {" mult/hNfiredA_vs_C" , " FT0A vs FT0C fired channels;N_{fired}^{FT0A};N_{fired}^{FT0C}" ,
85+ {HistType::kTH2F , {{97 , -0.5 , 96.5 }, {113 , -0.5 , 112.5 }}}}
5786 }};
5887
88+ int countBitsInRange (udhelpers::Bits256 const & bits, int first, int last) const
89+ {
90+ int n = 0 ;
91+ for (int i = first; i <= last; ++i) {
92+ if (udhelpers::testBit (bits, i)) {
93+ ++n;
94+ }
95+ }
96+ return n;
97+ }
98+
99+ // template <typename C>
100+ // bool isGoodRctFlag(const C& collision)
101+ // {
102+ // switch (cutRctFlag) {
103+ // case 1: // CBT
104+ // return sgSelector.isCBTOk(collision);
105+ // case 2: // CBT + ZDC
106+ // return sgSelector.isCBTZdcOk(collision);
107+ // case 3: // CBT hadron
108+ // return sgSelector.isCBTHadronOk(collision);
109+ // case 4: // CBT hadron + ZDC
110+ // return sgSelector.isCBTHadronZdcOk(collision);
111+ // default: // no RCT cut applied
112+ // return true;
113+ // }
114+ // }
115+
116+
117+ // template <typename C>
118+ // bool collisionPassesCuts(const C& collision)
119+ // {
120+ // /* good vertex */
121+ // if (!collision.vtxITSTPC()) {
122+ // return false;
123+ // }
124+ // registry.fill(HIST("debug/hEventCounter"), 4.);
125+
126+ // /* same bunch pile-up rejection */
127+ // if (!collision.sbp()) {
128+ // return false;
129+ // }
130+ // registry.fill(HIST("debug/hEventCounter"), 5.);
131+
132+ // /* ITS ROF rejection */
133+ // if (!collision.itsROFb()) {
134+ // return false;
135+ // }
136+ // registry.fill(HIST("debug/hEventCounter"), 6.);
137+
138+ // /* Timeframe border collision rejection */
139+ // if (!collision.tfb()) {
140+ // return false;
141+ // }
142+ // registry.fill(HIST("debug/hEventCounter"), 7.);
143+
144+ // /* z-vertex cut */
145+ // if (std::abs(collision.posZ()) > 10.0) {
146+ // return false;
147+ // }
148+ // registry.fill(HIST("debug/hEventCounter"), 8.);
149+
150+ // /* RCT flag check*/
151+ // if (useRctFlag) {
152+ // if (!isGoodRctFlag(collision)) {
153+ // return false;
154+ // }
155+ // registry.fill(HIST("debug/hEventCounter"), 9.); // passed RCT flag
156+ // }
157+ // return true;
158+ // }
159+
59160 void init (InitContext&)
60161 {
61- // UDHelpers calls calculateChannelCenter() inside, but doing it once here is fine.
62162 ft0Det.calculateChannelCenter ();
163+ LOGF (info, " UpcTestFITBitMapping initialized. whichThr=%d" , static_cast <int >(whichThr));
63164 }
64165
65- void process (aod::UDCollisionFITBits const & bitsTable )
166+ void process (UDCollisionsFull::iterator const & collision, aod::UDCollisionFITBits const & fitBits )
66167 {
67- int64_t nProcessed = 0 ;
168+ if (maxEvents >= 0 && collision.globalIndex () >= maxEvents) {
169+ return ;
170+ }
171+
172+ registry.fill (HIST (" debug/hEventCounter" ), 0 .);
173+ registry.fill (HIST (" debug/hFitBitsSize" ), fitBits.size ());
174+ registry.fill (HIST (" debug/hCollisionIndexMod" ), collision.globalIndex () % 100 );
68175
69- for (auto const & row : bitsTable) {
70- if (maxEvents >= 0 && nProcessed >= maxEvents) {
71- break ;
176+ if (fitBits.size () == 0 ) {
177+ if (collision.globalIndex () < debugPrintFirst ||
178+ (debugPrintEvery > 0 && collision.globalIndex () % debugPrintEvery == 0 )) {
179+ LOGF (info, " collision %d: fitBits.size() = 0" , collision.globalIndex ());
72180 }
73- ++nProcessed;
181+ registry.fill (HIST (" debug/hEventCounter" ), 1 .);
182+ return ;
183+
184+ } else if (fitBits.size () == 1 ) {
185+ LOGF (debug, " collision %d: fitBits.size() = %d , as it should be" , collision.globalIndex (), fitBits.size ());
186+ registry.fill (HIST (" debug/hEventCounter" ), 2 .);
187+
188+ } else { // > 1 case
189+ if (collision.globalIndex () < debugPrintFirst ||
190+ (debugPrintEvery > 0 && collision.globalIndex () % debugPrintEvery == 0 )) {
191+ LOGF (warn, " collision %d: fitBits.size() = %d (expected 0 or 1)" , collision.globalIndex (), fitBits.size ());
192+ }
193+ registry.fill (HIST (" debug/hEventCounter" ), 3 .);
194+ return ;
195+ }
74196
75- // Use udhelpers' canonical packed type + builder
76- udhelpers::Bits256 w{};
77- if (whichThr == 2 ) {
78- w = udhelpers::makeBits256 (row.thr2W0 (), row.thr2W1 (), row.thr2W2 (), row.thr2W3 ());
79- } else {
80- w = udhelpers::makeBits256 (row.thr1W0 (), row.thr1W1 (), row.thr1W2 (), row.thr1W3 ());
197+ /* Checking collision level cuts */
198+ // if (!collisionPassesCuts(collision)) {
199+ // return;
200+ // }
201+
202+ /* Only one row per collision is expected. */
203+ // auto const& row = *fitBits.begin();
204+ auto row = fitBits.begin ();
205+
206+ udhelpers::Bits256 w{};
207+ if (whichThr == 2 ) {
208+ w = udhelpers::makeBits256 (row.thr2W0 (), row.thr2W1 (), row.thr2W2 (), row.thr2W3 ());
209+ } else {
210+ w = udhelpers::makeBits256 (row.thr1W0 (), row.thr1W1 (), row.thr1W2 (), row.thr1W3 ());
211+ }
212+
213+ const int nFT0A = countBitsInRange (w, 0 , udhelpers::kFT0AChannels - 1 );
214+ const int nFT0C = countBitsInRange (w, udhelpers::kFT0AChannels , udhelpers::kFT0Bits - 1 );
215+ const int nFT0 = nFT0A + nFT0C;
216+ const int nFV0A = countBitsInRange (w, udhelpers::kFT0Bits , udhelpers::kTotalBits - 1 );
217+ const int nFIT = nFT0 + nFV0A;
218+
219+ registry.fill (HIST (" mult/hPnFT0A" ), nFT0A);
220+ registry.fill (HIST (" mult/hPnFT0C" ), nFT0C);
221+ registry.fill (HIST (" mult/hPnFT0" ), nFT0);
222+ registry.fill (HIST (" mult/hPnFV0A" ), nFV0A);
223+ registry.fill (HIST (" mult/hPnFIT" ), nFIT);
224+ registry.fill (HIST (" mult/hNfiredA_vs_C" ), nFT0A, nFT0C);
225+
226+ if (collision.globalIndex () < debugPrintFirst ||
227+ (debugPrintEvery > 0 && collision.globalIndex () % debugPrintEvery == 0 )) {
228+ LOGF (info,
229+ " collision %d: fitBits.size=%d nFT0A=%d nFT0C=%d nFT0=%d nFV0A=%d nFIT=%d" ,
230+ collision.globalIndex (), fitBits.size (), nFT0A, nFT0C, nFT0, nFV0A, nFIT);
231+ }
232+
233+ for (int bit = 0 ; bit < udhelpers::kFT0Bits ; ++bit) {
234+ if (!udhelpers::testBit (w, bit)) {
235+ continue ;
81236 }
82237
83- // Loop FT0 bits only (0..207). FV0 starts at 208 but ignored here.
84- for (int bit = 0 ; bit < udhelpers::kFT0Bits ; ++bit) {
85- if (!udhelpers::testBit (w, bit)) {
86- continue ;
87- }
88-
89- double phi = 0 ., eta = 0 .;
90- const bool ok = udhelpers::getPhiEtaFromFitBit (ft0Det, bit, offsetFT0, iRunOffset, phi, eta);
91- if (!ok) {
92- continue ;
93- }
94-
95- if (bit < udhelpers::kFT0AChannels ) {
96- registry.fill (HIST (" hPhiA" ), phi);
97- registry.fill (HIST (" hEtaA" ), eta);
98- registry.fill (HIST (" hEtaPhiA" ), eta, phi);
99- } else {
100- registry.fill (HIST (" hPhiC" ), phi);
101- registry.fill (HIST (" hEtaC" ), eta);
102- registry.fill (HIST (" hEtaPhiC" ), eta, phi);
103- }
238+ double phi = 0 ., eta = 0 .;
239+ const bool ok = udhelpers::getPhiEtaFromFitBit (ft0Det, bit, offsetFT0, iRunOffset, phi, eta);
240+ if (!ok) {
241+ continue ;
242+ }
243+
244+ if (bit < udhelpers::kFT0AChannels ) {
245+ registry.fill (HIST (" map/hPhiA" ), phi);
246+ registry.fill (HIST (" map/hEtaA" ), eta);
247+ registry.fill (HIST (" map/hEtaPhiA" ), eta, phi);
248+ } else {
249+ registry.fill (HIST (" map/hPhiC" ), phi);
250+ registry.fill (HIST (" map/hEtaC" ), eta);
251+ registry.fill (HIST (" map/hEtaPhiC" ), eta, phi);
104252 }
105253 }
254+
255+ registry.fill (HIST (" debug/hEventCounter" ), 4 .);
106256 }
107257};
108258
109259WorkflowSpec defineDataProcessing (ConfigContext const & cfgc)
110260{
111261 return WorkflowSpec{
112262 adaptAnalysisTask<UpcTestFITBitMapping>(cfgc, TaskName{" fitbit-mapping" })};
113- }
263+ }
0 commit comments