Skip to content

Commit 822b50a

Browse files
committed
annalysis task for the v0 pt of the charm-bulk
1 parent 3dac2aa commit 822b50a

2 files changed

Lines changed: 340 additions & 0 deletions

File tree

PWGHF/D2H/Tasks/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,8 @@ o2physics_add_dpl_workflow(task-hidden-charm
158158
SOURCES taskHiddenCharm.cxx
159159
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore
160160
COMPONENT_NAME Analysis)
161+
162+
o2physics_add_dpl_workflow(task-v0-pt-charm-bulk
163+
SOURCES taskV0PtCharmBulk.cxx
164+
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils
165+
COMPONENT_NAME Analysis)
Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
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+
/// \file taskV0PtCharmBulk.cxx
13+
/// \brief v0 pt for the charm-bulk correlation analysis task
14+
/// \author Wu Chuntai, UNIPD, CCNU, and INFN Padova
15+
/// \author Andrea Rossi, INFN Padova
16+
17+
#include "PWGHF/Core/CentralityEstimation.h"
18+
#include "PWGHF/Core/HfHelper.h"
19+
#include "PWGHF/DataModel/AliasTables.h"
20+
#include "PWGHF/DataModel/CandidateReconstructionTables.h"
21+
#include "PWGHF/DataModel/CandidateSelectionTables.h"
22+
#include "PWGHF/Utils/utilsEvSelHf.h"
23+
24+
#include "Common/Core/RecoDecay.h"
25+
#include "Common/DataModel/Centrality.h"
26+
#include "Common/DataModel/EventSelection.h"
27+
#include "Common/DataModel/Multiplicity.h"
28+
#include "Common/DataModel/TrackSelectionTables.h"
29+
30+
#include <CCDB/BasicCCDBManager.h>
31+
#include <CommonConstants/MathConstants.h>
32+
#include <CommonConstants/PhysicsConstants.h>
33+
#include <Framework/ASoA.h>
34+
#include <Framework/AnalysisDataModel.h>
35+
#include <Framework/AnalysisHelpers.h>
36+
#include <Framework/AnalysisTask.h>
37+
#include <Framework/Configurable.h>
38+
#include <Framework/HistogramRegistry.h>
39+
#include <Framework/HistogramSpec.h>
40+
#include <Framework/InitContext.h>
41+
#include <Framework/Logger.h>
42+
#include <Framework/runDataProcessing.h>
43+
44+
#include <THnSparse.h>
45+
#include <TF1.h>
46+
47+
#include <algorithm>
48+
#include <array>
49+
#include <numeric>
50+
#include <string>
51+
#include <type_traits>
52+
#include <vector>
53+
54+
using namespace o2;
55+
using namespace o2::framework;
56+
using namespace o2::framework::expressions;
57+
using namespace o2::hf_centrality;
58+
using namespace o2::hf_evsel;
59+
60+
enum DecayChannel {
61+
D0ToPiK = 0,
62+
D0ToKPi
63+
};
64+
65+
struct HfTaskV0PtCharmBulk
66+
{
67+
// General configuration
68+
Configurable<float> etaAMin{"etaAMin", -0.8, "eta min for A subevent"};
69+
Configurable<float> etaAMax{"etaAMax", -0.2, "eta max for A subevent"};
70+
Configurable<float> etaBMin{"etaBMin", 0.2, "eta min for B subevent"};
71+
Configurable<float> etaBMax{"etaBMax", 0.8, "eta max for B subevent"};
72+
Configurable<int> centEstimator{"centEstimator", 2, "Centrality estimation (FT0A: 1, FT0C: 2, FT0M: 3, FV0A: 4)"};
73+
Configurable<std::string> ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"};
74+
Configurable<std::vector<int>> classMl{"classMl", {0, 2}, "Indexes of BDT scores to be stored. Two indexes max."};
75+
76+
// Track configuration
77+
Configurable<int> tpcNClsCrossedRowsMin{"tpcNClsCrossedRowsMin", 70, "min. TPC crossed rows for associated tracks"};
78+
Configurable<float> etaTrkMax{"etaTrkMax", 1., "max. track eta"};
79+
Configurable<float> ptTrkMin{"ptTrkMin", 0.2, "min. track pT"};
80+
Configurable<float> ptTrkMax{"ptTrkMax", 5., "max. track pT"};
81+
Configurable<float> dcaXYTrkMax{"dcaXYTrkMax", 1., "max. track DCA XY"};
82+
Configurable<float> dcaZTrkMax{"dcaZTrkMax", 1., "max. track DCA Z"};
83+
Configurable<bool> usePtDiffDcaXYCut{"usePtDiffDcaXYCut", true, "Use pt-differential DCAxy cut for associated tracks"};
84+
Configurable<float> dcaXYTrkNSigmaMax{"dcaXYTrkNSigmaMax", 7, "Cut on number of sigma deviations from expected DCA in the transverse direction"};
85+
Configurable<std::string> dcaXYPtPrimTrkFunc{"dcaXYPtPrimTrkFunc", "(0.0026+0.005/(x^1.01))", "Functional form of pt-dependent DCAxy cut"};
86+
87+
// Candidate configuration
88+
Configurable<int> selectionFlag{"selectionFlag", 1, "Selection Flag for hadron (ML score tables are required to run the task)"};
89+
90+
// Event configuration
91+
Configurable<bool> forceCharmInCollision{"forceCharmInCollision", true, "Flag to force charm in collision"};
92+
HfEventSelection hfEvSel; // event selection and monitoring
93+
94+
o2::framework::Service<o2::ccdb::BasicCCDBManager> ccdb{};
95+
SliceCache cache;
96+
TF1* funcDcaXYPtCutPrimTrk = nullptr;
97+
98+
// Subcribe and join the tables
99+
using TracksTable = soa::Filtered<soa::Join<aod::TracksWDca, aod::TrackSelection, aod::TracksExtra>>;
100+
using D0CandTable = soa::Filtered<soa::Join<aod::HfCand2Prong, aod::HfSelD0, aod::HfMlD0>>;
101+
using CollsWithCentMult = soa::Join<aod::Collisions, aod::EvSels, aod::FT0Mults, aod::FV0Mults, aod::CentFT0Ms, aod::CentFT0As, aod::CentFT0Cs, aod::CentFV0As>;
102+
103+
// Select tracks and candidates
104+
Filter filterSelectTracks = (nabs(aod::track::eta) < etaTrkMax) && (aod::track::pt > ptTrkMin) && (aod::track::pt < ptTrkMax) && (nabs(aod::track::dcaXY) < dcaXYTrkMax) && (nabs(aod::track::dcaZ) < dcaZTrkMax);
105+
Filter filterSelectD0Candidates = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlag || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlag;
106+
107+
// pre-slice by collision for tracks
108+
Preslice<TracksTable> tracksTablePerColl = aod::track::collisionId;
109+
Preslice<D0CandTable> candD0TablePerColl = aod::hf_cand::collisionId;
110+
111+
// Partitions for selected candidates
112+
Partition<D0CandTable> selectedD0ToPiK = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlag;
113+
Partition<D0CandTable> selectedD0ToKPi = aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlag;
114+
115+
// Partition<TracksTable> selectedTracks = (nabs(aod::track::eta) < etaTrkMax) && (aod::track::pt > ptTrkMin) && (aod::track::pt < ptTrkMax) && (nabs(aod::track::dcaXY) < dcaXYTrkMax) && (nabs(aod::track::dcaZ) < dcaZTrkMax);
116+
117+
// THnSparse configuration
118+
ConfigurableAxis thnConfigAxisCent{"thnConfigAxisCent", {100, 0, 100}, ""};
119+
ConfigurableAxis thnConfigAxisCandMass{"thnConfigAxisCandMass", {200, 1.68, 2.08}, ""};
120+
ConfigurableAxis thnConfigAxisCandPt{"thnConfigAxisCandPt", {10, 0., 10.}, ""};
121+
ConfigurableAxis thnConfigAxisCandEta{"thnConfigAxisCandEta", {200, -1., 1.}, ""};
122+
ConfigurableAxis thnConfigAxisMPtTrkA{"thnConfigAxisMPtTrkA", {100, 0., 5.}, ""};
123+
ConfigurableAxis thnConfigAxisMPtTrkB{"thnConfigAxisMPtTrkB", {100, 0., 5.}, ""};
124+
ConfigurableAxis thnConfigAxisMlOne{"thnConfigAxisMlOne", {100, 0., 1.}, ""};
125+
ConfigurableAxis thnConfigAxisMlTwo{"thnConfigAxisMlTwo", {100, 0., 1.}, ""};
126+
127+
HistogramRegistry registry{"registry", {}};
128+
129+
void init(InitContext&) {
130+
std::array<bool, 14> doprocess{doprocessD0WCentMult};
131+
if ((std::accumulate(doprocess.begin(), doprocess.end(), 0)) == 0) {
132+
LOGP(fatal, "At least one process function should be enabled at a time.");
133+
}
134+
135+
hfEvSel.addHistograms(registry); // collision monitoring
136+
ccdb->setURL(ccdbUrl);
137+
ccdb->setCaching(true);
138+
ccdb->setLocalObjectValidityChecking();
139+
140+
// Define the axes for the THnSparse
141+
const AxisSpec axisCent = {thnConfigAxisCent, "Centrality"};
142+
const AxisSpec axisCandMass = {thnConfigAxisCandMass, "Inv. mass (GeV/#it{c}^{2})"};
143+
const AxisSpec axisCandPt = {thnConfigAxisCandPt, "#it{p}_{T} (GeV/#it{c})"};
144+
const AxisSpec axisCandEta = {thnConfigAxisCandEta, "Eta"};
145+
const AxisSpec axisMPtTrkA = {thnConfigAxisMPtTrkA, "Mean pT of tracks in subevent A (GeV/#it{c})"};
146+
const AxisSpec axisMPtTrkB = {thnConfigAxisMPtTrkB, "Mean pT of tracks in subevent B (GeV/#it{c})"};
147+
const AxisSpec axisMlOne = {thnConfigAxisMlOne, "ML score 1"};
148+
const AxisSpec axisMlTwo = {thnConfigAxisMlTwo, "ML score 2"};
149+
150+
std::vector<AxisSpec> axes = {axisCent, axisCandMass, axisCandPt, axisCandEta, axisMPtTrkA, axisMPtTrkB, axisMlOne, axisMlTwo};
151+
registry.add("hSparseMeanTrkPtCharm", "THn for mean track pT", HistType::kTHnSparseF, axes);
152+
153+
registry.add("hMeanTrkPtAVsCent", "Mean track pT A vs centrality", HistType::kTH2F, {axisCent, axisMPtTrkA});
154+
registry.add("hMeanTrkPtBVsCent", "Mean track pT B vs centrality", HistType::kTH2F, {axisCent, axisMPtTrkB});
155+
156+
if (usePtDiffDcaXYCut) {
157+
funcDcaXYPtCutPrimTrk = new TF1("funcDcaXYPtCutPrimTrk", Form("[0]*%s", dcaXYPtPrimTrkFunc.value.data()), 0.001, 100);
158+
funcDcaXYPtCutPrimTrk->SetParameter(0, dcaXYTrkNSigmaMax);
159+
LOGF(info, "DCAxy pt-dependence function: %s", Form("[0]*%s", dcaXYPtPrimTrkFunc.value.data()));
160+
}
161+
} // End init
162+
163+
/// Function to calculate mean pT of tracks in a given eta range (subevent) for a specific collision
164+
/// \param tracks are the tracks to be used for mean pT calculation
165+
/// \param candidates are the D0 candidates to be analyzed
166+
/// \return a pair of pairs: {{meanPtA, countA}, {meanPtB, countB}}
167+
template <typename CandT>
168+
std::pair<float, float> calculateMeanPt(TracksTable const& tracks, CandT const& candidates)
169+
{
170+
float sumPtA = 0.f;
171+
int countA = 0;
172+
float sumPtB = 0.f;
173+
int countB = 0;
174+
std::vector<int> candProngsA;
175+
std::vector<int> candProngsB;
176+
177+
// Gather the global indices of the candidate daughters
178+
if constexpr (std::is_same_v<CandT, D0CandTable>)
179+
{
180+
for (const auto& cand : candidates) {
181+
if (cand.eta() > etaAMin && cand.eta() < etaAMax) {
182+
candProngsA.push_back(cand.prong0Id());
183+
candProngsA.push_back(cand.prong1Id());
184+
} else if (cand.eta() > etaBMin && cand.eta() < etaBMax) {
185+
candProngsB.push_back(cand.prong0Id());
186+
candProngsB.push_back(cand.prong1Id());
187+
}
188+
}
189+
}
190+
191+
// Loop over tracks and calculate sum of pT and count for subevent A and B, excluding candidate daughters
192+
for (const auto& track : tracks)
193+
{
194+
float eta = track.eta();
195+
float pt = track.pt();
196+
197+
// Select only global tracks with DCA information or with sufficient TPC crossed rows
198+
if (track.isGlobalTrackWoDCA() || track.tpcNClsCrossedRows() < tpcNClsCrossedRowsMin) {
199+
continue;
200+
}
201+
202+
// Apply DCA cuts
203+
if (usePtDiffDcaXYCut)
204+
{
205+
float const dcaXYTrkCut = funcDcaXYPtCutPrimTrk->Eval(pt);
206+
if (std::fabs(track.dcaXY()) > dcaXYTrkCut)
207+
{
208+
continue;
209+
}
210+
}
211+
212+
int const trackGlobalIndex = track.globalIndex();
213+
// A side
214+
if (eta < etaAMax && eta > etaAMin)
215+
{
216+
if (std::find(candProngsB.begin(), candProngsB.end(), trackGlobalIndex) != candProngsB.end())
217+
{
218+
continue; // skip tracks that are daughters of the candidate in the opposite subevent
219+
/*TODO: Considering remove the overlapped daughter tracks by recalculating the mean pT
220+
excluding the candidate daughters on the fly for each candidate*/
221+
}
222+
sumPtA += pt;
223+
countA++;
224+
}
225+
226+
// B side
227+
if (eta < etaBMax && eta > etaBMin)
228+
{
229+
if (std::find(candProngsA.begin(), candProngsA.end(), trackGlobalIndex) != candProngsA.end())
230+
{
231+
continue; // skip tracks that are daughters of the candidate in the opposite subevent
232+
/*TODO: Considering remove the overlapped daughter tracks by recalculating the mean pT
233+
excluding the candidate daughters on the fly for each candidate*/
234+
}
235+
sumPtB += pt;
236+
countB++;
237+
}
238+
}
239+
240+
if (countA == 0 && countB == 0)
241+
{
242+
return {std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()};
243+
// return NaN if no tracks in either subevent
244+
} else if (countA == 0)
245+
{
246+
return {std::numeric_limits<float>::quiet_NaN(), sumPtB / countB}; // NaN for subevent A if no tracks
247+
} else if (countB == 0)
248+
{
249+
return {sumPtA / countA, std::numeric_limits<float>::quiet_NaN()}; // NaN for subevent B if no tracks
250+
}
251+
return {sumPtA / countA, sumPtB / countB};
252+
}
253+
254+
/// Calculate mean pT of tracks in subevent A and B, and fill the THnSparse
255+
/// \param collision is the collision with the centrality and multiplicity information
256+
/// \param tracks are the tracks to be used for mean pT calculation
257+
/// \param candidates are the D0 candidates to be analyzed
258+
template <DecayChannel Channel, typename CandT>
259+
void runCharmBulkAnalysis(CandT const& candidates, TracksTable const& tracks, float cent)
260+
{
261+
auto [meanPtA, meanPtB] = calculateMeanPt(tracks, candidates);
262+
// Loop over candidates and fill the THnSparse
263+
for (const auto& cand : candidates)
264+
{
265+
float invMass = 0.f;
266+
std::vector<float> outputMl = {-999., -999.};
267+
if constexpr (std::is_same_v<CandT, D0CandTable>) {
268+
switch (Channel) {
269+
case DecayChannel::D0ToPiK:
270+
invMass = HfHelper::invMassD0ToPiK(cand);
271+
for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) {
272+
outputMl[iclass] = cand.mlProbD0()[classMl->at(iclass)];
273+
}
274+
break;
275+
case DecayChannel::D0ToKPi:
276+
invMass = HfHelper::invMassD0barToKPi(cand);
277+
for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) {
278+
outputMl[iclass] = cand.mlProbD0bar()[classMl->at(iclass)];
279+
}
280+
break;
281+
}
282+
}
283+
registry.fill(HIST("hSparseMeanTrkPtCharm"), cent, invMass, cand.pt(), cand.eta(), meanPtA, meanPtB, outputMl[0], outputMl[1]);
284+
registry.fill(HIST("hMeanTrkPtAVsCent"), cent, meanPtA);
285+
registry.fill(HIST("hMeanTrkPtBVsCent"), cent, meanPtB);
286+
}
287+
}
288+
289+
/// Check event selections for collision and fill event selection histograms
290+
/// \param collision is the collision
291+
template <typename Coll>
292+
bool isSelectedHfCollision(Coll const& collision, float& cent)
293+
{
294+
o2::hf_evsel::HfCollisionRejectionMask collRejMask{};
295+
if (centEstimator == CentralityEstimator::FT0A)
296+
{
297+
collRejMask = hfEvSel.getHfCollisionRejectionMask<true, CentralityEstimator::FT0A, aod::BCsWithTimestamps>(collision, cent, ccdb, registry);
298+
} else if (centEstimator == CentralityEstimator::FT0C)
299+
{
300+
collRejMask = hfEvSel.getHfCollisionRejectionMask<true, CentralityEstimator::FT0C, aod::BCsWithTimestamps>(collision, cent, ccdb, registry);
301+
} else if (centEstimator == CentralityEstimator::FT0M)
302+
{
303+
collRejMask = hfEvSel.getHfCollisionRejectionMask<true, CentralityEstimator::FT0M, aod::BCsWithTimestamps>(collision, cent, ccdb, registry);
304+
} else if (centEstimator == CentralityEstimator::FV0A)
305+
{
306+
collRejMask = hfEvSel.getHfCollisionRejectionMask<true, CentralityEstimator::FV0A, aod::BCsWithTimestamps>(collision, cent, ccdb, registry);
307+
} else
308+
{
309+
LOG(fatal) << "Centrality estimator not recognized for collision selection";
310+
std::abort();
311+
}
312+
hfEvSel.fillHistograms(collision, collRejMask, cent);
313+
return collRejMask == 0;
314+
}
315+
316+
void processD0WCentMult(CollsWithCentMult::iterator const& collision, TracksTable const& tracks, D0CandTable const& /* D0 candidates */) {
317+
auto tableD0ToPiK = selectedD0ToPiK->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache);
318+
auto tableD0ToKPi = selectedD0ToKPi->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache);
319+
if (forceCharmInCollision && tableD0ToPiK.size() < 1 && tableD0ToKPi.size() < 1) {
320+
return;
321+
}
322+
float cent = -1.f;
323+
if (!isSelectedHfCollision(collision, cent)) {
324+
return;
325+
}
326+
runCharmBulkAnalysis<DecayChannel::D0ToPiK>(tableD0ToPiK, tracks, cent);
327+
runCharmBulkAnalysis<DecayChannel::D0ToKPi>(tableD0ToKPi, tracks, cent);
328+
}
329+
PROCESS_SWITCH(HfTaskV0PtCharmBulk, processD0WCentMult, "Process D0 candidates for tracks' mean-pT analysis", true);
330+
}; // End struct HfTaskV0PtCharmBulk
331+
332+
WorkflowSpec defineDataProcessing(ConfigContext const& cfgc)
333+
{
334+
return WorkflowSpec{adaptAnalysisTask<HfTaskV0PtCharmBulk>(cfgc)};
335+
}

0 commit comments

Comments
 (0)