Skip to content

Commit 2afc38e

Browse files
Dushmanta SahuDushmanta Sahu
authored andcommitted
New flattenicity analysis
1 parent 54129d4 commit 2afc38e

2 files changed

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

0 commit comments

Comments
 (0)