Skip to content

Commit 2c2e8e9

Browse files
dushmanta97Dushmanta Sahu
andauthored
[PWGLF] New flattenicity analysis (#17739)
Co-authored-by: Dushmanta Sahu <dushmanta@Dushmantas-MacBook-Pro.local>
1 parent 66aeab8 commit 2c2e8e9

2 files changed

Lines changed: 316 additions & 0 deletions

File tree

PWGLF/Tasks/Nuspex/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,11 @@ o2physics_add_dpl_workflow(multiplicity-pt
201201
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore
202202
COMPONENT_NAME Analysis)
203203

204+
o2physics_add_dpl_workflow(flattenicity-pt-spectra
205+
SOURCES flattenicityPtSpectra.cxx
206+
PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore
207+
COMPONENT_NAME Analysis)
208+
204209
o2physics_add_dpl_workflow(deuteron-in-jets-trg-pt
205210
SOURCES deuteronInJetsTrgPt.cxx
206211
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils
Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2+
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3+
// All rights not expressly granted are reserved.
4+
//
5+
// This software is distributed under the terms of the GNU General Public
6+
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7+
//
8+
// In applying this license CERN does not waive the privileges and immunities
9+
// granted to it by virtue of its status as an Intergovernmental Organization
10+
// or submit itself to any jurisdiction.
11+
///
12+
/// \author Dushmanta Sahu (dushmanta.sahus@cern.ch)
13+
/// \since September 1, 2026
14+
/// \file flattenicityPtSpectra.cxx
15+
/// \brief Analysis to do flattenicity pt spectra
16+
17+
#include "Common/DataModel/EventSelection.h"
18+
#include "Common/DataModel/FT0Corrected.h"
19+
#include "Common/DataModel/Multiplicity.h"
20+
#include "Common/DataModel/TrackSelectionTables.h"
21+
22+
#include <CommonConstants/MathConstants.h>
23+
#include <Framework/AnalysisDataModel.h>
24+
#include <Framework/AnalysisTask.h>
25+
#include <Framework/runDataProcessing.h>
26+
27+
#include <TH1F.h>
28+
#include <TH2F.h>
29+
#include <TH3F.h>
30+
#include <TMath.h>
31+
32+
#include <algorithm>
33+
#include <array>
34+
#include <cmath>
35+
#include <vector>
36+
37+
using namespace o2;
38+
using namespace o2::framework;
39+
40+
using MyTracks = soa::Join<aod::Tracks, aod::TracksExtra, aod::TracksDCA>;
41+
42+
using MyCollisions = soa::Join<aod::Collisions,
43+
aod::Mults,
44+
aod::FT0sCorrected,
45+
aod::EvSels>;
46+
47+
struct FlattenicityPtSpectra {
48+
49+
Configurable<float> etaMin{"etaMin", -0.8f, "Min eta for mid-rapidity tracks"};
50+
Configurable<float> etaMax{"etaMax", 0.8f, "Max eta for mid-rapidity tracks"};
51+
Configurable<float> ptMin{"ptMin", 0.15f, "Min pT (GeV/c)"};
52+
Configurable<float> ptMax{"ptMax", 20.0f, "Max pT (GeV/c)"};
53+
Configurable<int> minTPCnClsFound{"minTPCnClsFound", 70, "Min TPC found clusters"};
54+
Configurable<float> maxDCAz{"maxDCAz", 2.0f, "Max |DCAz| (cm)"};
55+
Configurable<float> maxVtxZ{"maxVtxZ", 10.0f, "Max |vtx z| (cm)"};
56+
57+
Configurable<int> minActiveFT0Ch{"minActiveFT0Ch", 4, "Min active FT0 channels for flattenicity"};
58+
59+
static constexpr int NChA = 96;
60+
static constexpr int NChC = 96;
61+
static constexpr int NCells = NChA + NChC; // 192 total
62+
63+
HistogramRegistry registry{
64+
"registry",
65+
{},
66+
OutputObjHandlingPolicy::AnalysisObject,
67+
true,
68+
true};
69+
70+
template <typename TrackType>
71+
bool isGoodTrack(TrackType const& track) const
72+
{
73+
if (!track.hasTPC())
74+
return false;
75+
if (track.tpcNClsFound() < minTPCnClsFound.value)
76+
return false;
77+
if (track.eta() < etaMin.value || track.eta() > etaMax.value)
78+
return false;
79+
if (std::abs(track.dcaZ()) > maxDCAz.value)
80+
return false;
81+
if (track.pt() < ptMin.value || track.pt() > ptMax.value)
82+
return false;
83+
return true;
84+
}
85+
86+
float calculateFlattenicityFT0(aod::FT0 const& ft0, int minActive) const
87+
{
88+
std::array<float, NCells> signals{};
89+
signals.fill(0.f);
90+
91+
int nActive = 0;
92+
93+
int chIdx = 0;
94+
for (const auto& a : ft0.amplitudeA()) {
95+
if (chIdx < NChA) {
96+
float amp = static_cast<float>(a);
97+
if (amp > 0.f) {
98+
signals[chIdx] = amp;
99+
++nActive;
100+
}
101+
}
102+
++chIdx;
103+
}
104+
105+
chIdx = 0;
106+
for (const auto& a : ft0.amplitudeC()) {
107+
if (chIdx < NChC) {
108+
float amp = static_cast<float>(a);
109+
if (amp > 0.f) {
110+
signals[NChA + chIdx] = amp;
111+
++nActive;
112+
}
113+
}
114+
++chIdx;
115+
}
116+
117+
if (nActive < minActive)
118+
return -1.f;
119+
120+
float mRho = 0.f;
121+
for (int i = 0; i < NCells; ++i) {
122+
mRho += signals[i];
123+
}
124+
mRho /= static_cast<float>(NCells);
125+
if (mRho <= 0.f)
126+
return -1.f;
127+
128+
float sRhoTmp = 0.f;
129+
for (int i = 0; i < NCells; ++i) {
130+
if (signals[i] > 0.f) {
131+
float d = signals[i] - mRho;
132+
sRhoTmp += d * d;
133+
}
134+
}
135+
sRhoTmp /= static_cast<float>(NCells) * static_cast<float>(NCells);
136+
float sRho = std::sqrt(sRhoTmp);
137+
138+
// flattenicity = 1 - sigma/mean, clamped to [0, 1]
139+
float cv = sRho / mRho;
140+
return std::max(0.f, std::min(1.f, 1.f - cv));
141+
}
142+
143+
void init(InitContext const&)
144+
{
145+
AxisSpec ptAxis{200, 0.0f, 20.0f, "#it{p}_{T} (GeV/c)"};
146+
AxisSpec flatAxis{102, -0.01f, 1.01f, "Flattenicity"};
147+
AxisSpec multAxis{300, 0.f, 3000.f, "FT0M amplitude (a.u.)"};
148+
AxisSpec nchAxis{500, -0.5f, 499.5f, "Raw N_{ch} (|#eta|<0.8, p_{T}>0.15)"};
149+
AxisSpec vtxzAxis{200, -20.f, 20.f, "v_{z} (cm)"};
150+
151+
registry.add("hEventCounter",
152+
"Event counter",
153+
HistType::kTH1F, {{5, 0.5, 5.5}});
154+
registry.get<TH1>(HIST("hEventCounter"))->GetXaxis()->SetBinLabel(1, "All");
155+
registry.get<TH1>(HIST("hEventCounter"))->GetXaxis()->SetBinLabel(2, "vtxZ");
156+
registry.get<TH1>(HIST("hEventCounter"))->GetXaxis()->SetBinLabel(3, "hasFT0");
157+
registry.get<TH1>(HIST("hEventCounter"))->GetXaxis()->SetBinLabel(4, "FT0signal");
158+
registry.get<TH1>(HIST("hEventCounter"))->GetXaxis()->SetBinLabel(5, "goodFlat");
159+
160+
registry.add("hVtxZ",
161+
"Primary vertex z",
162+
HistType::kTH1F, {vtxzAxis});
163+
164+
registry.add("hFT0MMultiplicity",
165+
"FT0M amplitude sum",
166+
HistType::kTH1F, {multAxis});
167+
168+
registry.add("hFlattenicityDistribution",
169+
"Flattenicity (Gyula method, 192 FT0 ch)",
170+
HistType::kTH1F, {flatAxis});
171+
172+
registry.add("hFT0AAmplitude",
173+
"Total FT0A amplitude",
174+
HistType::kTH1F, {{200, 0.f, 2000.f, "Amplitude (a.u.)"}});
175+
176+
registry.add("hFT0CAmplitude",
177+
"Total FT0C amplitude",
178+
HistType::kTH1F, {{200, 0.f, 2000.f, "Amplitude (a.u.)"}});
179+
180+
// QA: active channel count — use this to tune minActiveFT0Ch
181+
registry.add("hNActiveFT0Channels",
182+
"Active FT0 channels per event (amp > 0)",
183+
HistType::kTH1F, {{193, -0.5f, 192.5f, "N active channels"}});
184+
185+
registry.add("hEtaDistribution",
186+
"Eta distribution (selected tracks)",
187+
HistType::kTH1F, {{100, -1.0f, 1.0f, "#eta"}});
188+
189+
registry.add("hPhiDistribution",
190+
"Phi distribution (selected tracks)",
191+
HistType::kTH1F, {{100, 0.f, o2::constants::math::TwoPI, "#phi"}});
192+
193+
registry.add("hPtSpectrum_All",
194+
"pT spectrum (all selected events)",
195+
HistType::kTH1F, {ptAxis});
196+
197+
registry.add("hFT0MMultVsFlattenicity",
198+
"FT0M multiplicity vs Flattenicity;"
199+
"FT0M amplitude (a.u.);Flattenicity",
200+
HistType::kTH2F, {multAxis, flatAxis});
201+
202+
registry.add("hFT0MMultVsFlattenicityVsPt",
203+
"FT0M multiplicity vs Flattenicity vs #it{p}_{T};"
204+
"FT0M amplitude (a.u.);Flattenicity;#it{p}_{T} (GeV/c)",
205+
HistType::kTH3F, {multAxis, flatAxis, ptAxis});
206+
207+
registry.add("hNchDistribution",
208+
"Raw charged particle multiplicity",
209+
HistType::kTH1F, {nchAxis});
210+
211+
registry.add("hNchVsFlattenicity",
212+
"Raw N_{ch} vs Flattenicity;"
213+
"Raw N_{ch};Flattenicity",
214+
HistType::kTH2F, {nchAxis, flatAxis});
215+
216+
registry.add("hNchVsFlattenicityVsPt",
217+
"Raw N_{ch} vs Flattenicity vs #it{p}_{T};"
218+
"Raw N_{ch};Flattenicity;#it{p}_{T} (GeV/c)",
219+
HistType::kTH3F, {nchAxis, flatAxis, ptAxis});
220+
221+
LOG(info) << "FlattenicityPtSpectra::init — done"
222+
<< " NCells=" << NCells
223+
<< " minActiveFT0Ch=" << minActiveFT0Ch.value
224+
<< " Flattenicity method: Gyula (fixed denominator = total channels)";
225+
}
226+
227+
void process(MyCollisions::iterator const& collision,
228+
MyTracks const& tracks,
229+
aod::FT0s const& /*ft0s*/)
230+
{
231+
registry.fill(HIST("hEventCounter"), 1.f);
232+
233+
if (std::abs(collision.posZ()) > maxVtxZ.value)
234+
return;
235+
registry.fill(HIST("hEventCounter"), 2.f);
236+
registry.fill(HIST("hVtxZ"), collision.posZ());
237+
238+
if (!collision.has_foundFT0())
239+
return;
240+
registry.fill(HIST("hEventCounter"), 3.f);
241+
242+
auto ft0 = collision.foundFT0();
243+
244+
float sumA = 0.f, sumC = 0.f;
245+
int nActive = 0;
246+
for (const auto& a : ft0.amplitudeA()) {
247+
if (a > 0.f) {
248+
sumA += a;
249+
++nActive;
250+
}
251+
}
252+
for (const auto& a : ft0.amplitudeC()) {
253+
if (a > 0.f) {
254+
sumC += a;
255+
++nActive;
256+
}
257+
}
258+
259+
registry.fill(HIST("hFT0AAmplitude"), sumA);
260+
registry.fill(HIST("hFT0CAmplitude"), sumC);
261+
registry.fill(HIST("hNActiveFT0Channels"), static_cast<float>(nActive));
262+
263+
if (sumA + sumC <= 0.f)
264+
return;
265+
registry.fill(HIST("hEventCounter"), 4.f);
266+
267+
float flattenicity = calculateFlattenicityFT0(ft0, minActiveFT0Ch.value);
268+
if (flattenicity < 0.f)
269+
return;
270+
registry.fill(HIST("hEventCounter"), 5.f);
271+
272+
float multFT0M = collision.multFT0M();
273+
274+
registry.fill(HIST("hFT0MMultiplicity"), multFT0M);
275+
registry.fill(HIST("hFlattenicityDistribution"), flattenicity);
276+
registry.fill(HIST("hFT0MMultVsFlattenicity"), multFT0M, flattenicity);
277+
278+
std::vector<typename MyTracks::iterator> goodTracks;
279+
goodTracks.reserve(1000);
280+
281+
for (const auto& track : tracks) {
282+
if (!isGoodTrack(track))
283+
continue;
284+
goodTracks.push_back(track);
285+
}
286+
287+
int rawNch = goodTracks.size();
288+
289+
// Fill per-event histograms with Nch
290+
registry.fill(HIST("hNchDistribution"), rawNch);
291+
registry.fill(HIST("hNchVsFlattenicity"), rawNch, flattenicity);
292+
293+
// Fill per-track histograms using the collected good tracks
294+
for (const auto& track : goodTracks) {
295+
registry.fill(HIST("hEtaDistribution"), track.eta());
296+
registry.fill(HIST("hPhiDistribution"), track.phi());
297+
registry.fill(HIST("hPtSpectrum_All"), track.pt());
298+
registry.fill(HIST("hFT0MMultVsFlattenicityVsPt"),
299+
multFT0M, flattenicity, track.pt());
300+
301+
// NEW: Fill Nch-based 3D histogram
302+
registry.fill(HIST("hNchVsFlattenicityVsPt"),
303+
rawNch, flattenicity, track.pt());
304+
}
305+
}
306+
};
307+
308+
WorkflowSpec defineDataProcessing(ConfigContext const& cfgc)
309+
{
310+
return WorkflowSpec{adaptAnalysisTask<FlattenicityPtSpectra>(cfgc)};
311+
}

0 commit comments

Comments
 (0)