Skip to content

Commit 90eb719

Browse files
author
Marta Razza
committed
[PWGHF] Add task for H2 from Lb analysis
1 parent 7dbf61f commit 90eb719

2 files changed

Lines changed: 339 additions & 0 deletions

File tree

PWGHF/D2H/Tasks/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,11 @@ o2physics_add_dpl_workflow(task-hidden-charm
168168
SOURCES taskHiddenCharm.cxx
169169
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore
170170
COMPONENT_NAME Analysis)
171+
172+
o2physics_add_dpl_workflow(task-h2-from-lb
173+
SOURCES taskH2fromLb.cxx
174+
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils
175+
COMPONENT_NAME Analysis)
176+
177+
178+

PWGHF/D2H/Tasks/taskH2fromLb.cxx

Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
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+
/// \brief A filter task for non prompt deuterons
13+
/// \author Marta Razza marta.razza@cern.ch
14+
/// \author Francesca Ercolessi francesca.ercolessi@cern.ch
15+
/// \since Dec 17, 2025
16+
17+
#include "Common/Core/Zorro.h"
18+
#include "Common/Core/ZorroSummary.h"
19+
#include "Common/Core/trackUtilities.h"
20+
#include "Common/DataModel/CollisionAssociationTables.h"
21+
#include "Common/DataModel/EventSelection.h"
22+
#include "Common/DataModel/PIDResponseTOF.h"
23+
#include "Common/DataModel/PIDResponseTPC.h"
24+
#include "Common/DataModel/TrackSelectionTables.h"
25+
26+
#include <CCDB/BasicCCDBManager.h>
27+
#include <DataFormatsParameters/GRPMagField.h>
28+
#include <DetectorsBase/Propagator.h>
29+
#include <Framework/AnalysisDataModel.h>
30+
#include <Framework/AnalysisHelpers.h>
31+
#include <Framework/AnalysisTask.h>
32+
#include <Framework/Configurable.h>
33+
#include <Framework/HistogramRegistry.h>
34+
#include <Framework/HistogramSpec.h>
35+
#include <Framework/InitContext.h>
36+
#include <Framework/OutputObjHeader.h>
37+
#include <Framework/runDataProcessing.h>
38+
#include <ReconstructionDataFormats/Track.h>
39+
#include <ReconstructionDataFormats/TrackParametrization.h>
40+
41+
#include <TH1.h>
42+
43+
#include <array>
44+
#include <chrono>
45+
#include <cmath>
46+
47+
using namespace o2;
48+
using namespace o2::framework;
49+
using namespace o2::framework::expressions;
50+
51+
struct H2fromLb {
52+
53+
Zorro zorro;
54+
OutputObj<ZorroSummary> zorroSummary{"zorroSummary"};
55+
56+
o2::base::Propagator::MatCorrType noMatCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE;
57+
58+
// Define a histograms and registries
59+
HistogramRegistry QAHistos{"QAHistos", {}, OutputObjHandlingPolicy::AnalysisObject, false, true};
60+
OutputObj<TH1D> hProcessedEvents{TH1D("hProcessedEvents", "Event filtered;; Number of events", 4, 0., 4.)};
61+
62+
Configurable<float> cutzvertex{"cutzvertex", 10.0f, "Accepted z-vertex range (cm)"};
63+
Configurable<bool> applySkimming{"applySkimming", false, "Skimmed dataset processing"};
64+
Configurable<std::string> cfgSkimming{"cfgSkimming", "fH2fromLb", "Configurable for skimming"};
65+
Configurable<bool> sel8{"sel8", true, "sel8 event selection"};
66+
Configurable<bool> separateAntideuterons{"separateAntideuterons", true, "Fill FromLb histos for primary deuterons whose mother is Lb, Primary histos for all others"};
67+
Configurable<float> cfgEta{"cfgEta", 0.8f, "Track eta selection"};
68+
Configurable<float> cfgTPCNclsFound{"cfgTPCNclsFound", 100, "Minimum TPC clusters found"};
69+
Configurable<float> cfgTPCChi2Ncl{"cfgTPCChi2Ncl", 4.0f, "Maximum TPC chi2 per N clusters"};
70+
Configurable<float> cfgITSChi2Ncl{"cfgITSChi2Ncl", 36.0f, "Maximum ITS chi2 per N clusters"};
71+
Configurable<float> cfgITScls{"cfgITScls", 2, "Minimum ITS clusters"};
72+
Configurable<float> cfgMaxPt{"cfgMaxPt", 5.0f, "Maximum pT cut"};
73+
Configurable<float> cfgMinPt{"cfgMinPt", 0.5f, "Minimum pT cut"};
74+
Configurable<float> cfgTPCNsigma{"cfgTPCNsigma", 4.0f, "TPC n sigma for deuteron PID"};
75+
Configurable<float> cfgTOFNsigma_min{"cfgTOFNsigma_min", 3.0f, "TOF n sigma min for deuteron PID"};
76+
Configurable<float> cfgTOFNsigma_max{"cfgTOFNsigma_max", 4.0f, "TOF n sigma max for deuteron PID"};
77+
Configurable<float> ptThresholdPid{"ptThresholdPid", 1.0f, "pT threshold to switch between 4 and 3 sigmas for TOF PID"};
78+
// PDG codes
79+
Configurable<int> pdgCodeMother{"pdgCodeMother", -5122, "PDG code of the mother particle (default: anti-Lambda_b)"};
80+
Configurable<int> pdgCodeDaughter{"pdgCodeDaughter", -1000010020, "PDG code of the daughter particle (default: anti-deuteron)"};
81+
82+
int mRunNumber = 0;
83+
float d_bz = 0.f;
84+
HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject};
85+
86+
framework::Service<ccdb::BasicCCDBManager> ccdb;
87+
void init(framework::InitContext&)
88+
{
89+
90+
ccdb->setURL("http://alice-ccdb.cern.ch"); // Set CCDB URL to get magnetic field
91+
ccdb->setCaching(true);
92+
ccdb->setLocalObjectValidityChecking();
93+
ccdb->setCreatedNotAfter(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
94+
95+
if (applySkimming) {
96+
zorroSummary.setObject(zorro.getZorroSummary());
97+
}
98+
99+
ConfigurableAxis ptAxis{"ptAxis", {100, 0., 10.f}, "p_{T} GeV/c"};
100+
ConfigurableAxis nSigmaAxis{"nSigmaAxis", {200, -10.f, 10.f}, "nSigma"};
101+
ConfigurableAxis DCAxyAxis{"DCAxyAxis", {1000, -0.2f, 0.2f}, "DCA xy (cm)"};
102+
ConfigurableAxis DCAzAxis{"DCAzAxis", {1000, -0.2f, 0.2f}, "DCA z (cm)"};
103+
104+
// general QA histograms
105+
QAHistos.add("hVtxZ", "Z-Vertex distribution after selection;Z (cm)", HistType::kTH1F, {{100, -50, 50}});
106+
QAHistos.add("ptGeneratedLb", "ptGeneratedLb", HistType::kTH1F, {ptAxis});
107+
QAHistos.add("ptAntiDeuteronPrimary", "ptAntiDeuteronPrimaryReco", HistType::kTH1F, {ptAxis});
108+
QAHistos.add("ptAntiDeuteronFromLb", "ptAntiDeuteronFromLbReco", HistType::kTH1F, {ptAxis});
109+
QAHistos.add("hDCAxy-Primary", "DCAxy-Primary", {HistType::kTH1D, {{400, -0.2f, 0.2f, "DCA xy (cm)"}}});
110+
QAHistos.add("hDCAxy-FromLb", "DCAxy-FromLb", {HistType::kTH1D, {{400, -0.2f, 0.2f, "DCA xy (cm)"}}});
111+
QAHistos.add("hDCAxyVsPt", "DCAxy #bar{d} vs p_{T}", {HistType::kTH2D, {ptAxis, DCAxyAxis}});
112+
QAHistos.add("hDCAzVsPt", "DCAz #bar{d} vs p_{T}", {HistType::kTH2D, {ptAxis, DCAzAxis}});
113+
QAHistos.add("hnSigmaTPCVsPt", "n#sigma TPC vs p_{T} for #bar{d} hypothesis; p_{T} (GeV/c); n#sigma TPC", {HistType::kTH2D, {ptAxis, nSigmaAxis}});
114+
QAHistos.add("hnSigmaTOFVsPt", "n#sigma TOF vs p_{T} for #bar{d} hypothesis; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}});
115+
QAHistos.add("ptAntiDeuteron", "ptAntiDeuteron", {HistType::kTH1F, {ptAxis}});
116+
QAHistos.add("etaAntideuteron", "etaAntideuteron", {HistType::kTH1F, {{100, -1.0f, 1.0f, "eta #bar{d}"}}});
117+
118+
// processed events
119+
hProcessedEvents->GetXaxis()->SetBinLabel(1, "Events processed");
120+
hProcessedEvents->GetXaxis()->SetBinLabel(2, "ZORRO");
121+
hProcessedEvents->GetXaxis()->SetBinLabel(3, "sel8");
122+
hProcessedEvents->GetXaxis()->SetBinLabel(4, "z-vertex");
123+
}
124+
void initCCDB(o2::aod::BCsWithTimestamps::iterator const& bc) // inspired by PWGLF/TableProducer/lambdakzerobuilder.cxx
125+
{
126+
if (mRunNumber == bc.runNumber()) {
127+
return;
128+
}
129+
d_bz = 0.f;
130+
131+
if (applySkimming) {
132+
zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), cfgSkimming.value);
133+
zorro.populateHistRegistry(registry, bc.runNumber());
134+
}
135+
}
136+
using CollisionCandidates = o2::soa::Join<o2::aod::Collisions, o2::aod::EvSels>;
137+
// Tables for MC processing
138+
using MCTrackCandidates = o2::soa::Join<o2::aod::TracksIU, o2::aod::TracksExtra, o2::aod::TracksDCA, o2::aod::McTrackLabels>;
139+
using MCCollisionCandidates = o2::soa::Join<o2::aod::Collisions, o2::aod::EvSels, o2::aod::McCollisionLabels>;
140+
// Tables for Data processing
141+
using TrackCandidates = o2::soa::Join<o2::aod::Tracks, o2::aod::TracksCov, o2::aod::TracksExtra, o2::aod::TracksDCA, o2::aod::TrackSelection, o2::aod::pidTPCFullDe, o2::aod::pidTOFFullDe>;
142+
143+
// Single-Track Selection
144+
template <typename T1>
145+
bool passedSingleTrackSelection(const T1& track)
146+
{
147+
// Single-Track Selections
148+
if (std::abs(track.eta()) > cfgEta)
149+
return false;
150+
if (!track.hasITS())
151+
return false;
152+
if (!track.hasTPC())
153+
return false;
154+
if (!track.hasTOF())
155+
return false;
156+
if (track.tpcNClsFound() < cfgTPCNclsFound)
157+
return false;
158+
if (track.tpcChi2NCl() > cfgTPCChi2Ncl)
159+
return false;
160+
if (track.itsChi2NCl() > cfgITSChi2Ncl)
161+
return false;
162+
if (track.itsNCls() < cfgITScls)
163+
return false;
164+
if (track.pt() > cfgMaxPt)
165+
return false;
166+
if (track.pt() < cfgMinPt)
167+
return false;
168+
if (track.sign() > 0)
169+
return false;
170+
171+
return true;
172+
}
173+
Preslice<o2::aod::TrackAssoc> trackIndicesPerCollision = o2::aod::track_association::collisionId;
174+
int mCurrentRun = -1;
175+
void processData(CollisionCandidates const& collisions,
176+
o2::aod::TrackAssoc const& trackIndices,
177+
TrackCandidates const& tracks,
178+
o2::aod::BCsWithTimestamps const&)
179+
{
180+
for (const auto& collision : collisions) // start loop over collisions
181+
{
182+
if (mCurrentRun != collision.bc_as<o2::aod::BCsWithTimestamps>().runNumber()) { // If the run is new then we need to initialize the propagator field
183+
o2::parameters::GRPMagField* grpo = ccdb->getForTimeStamp<o2::parameters::GRPMagField>("GLO/Config/GRPMagField", collision.bc_as<o2::aod::BCsWithTimestamps>().timestamp());
184+
o2::base::Propagator::initFieldFromGRP(grpo);
185+
mCurrentRun = collision.bc_as<o2::aod::BCsWithTimestamps>().runNumber();
186+
}
187+
188+
const auto& bc = collision.bc_as<o2::aod::BCsWithTimestamps>();
189+
initCCDB(bc);
190+
hProcessedEvents->Fill(0.5);
191+
if (applySkimming) {
192+
if (!zorro.isSelected(bc.globalBC())) {
193+
continue;
194+
}
195+
}
196+
hProcessedEvents->Fill(1.5);
197+
if (sel8 && !collision.sel8()) {
198+
continue;
199+
}
200+
hProcessedEvents->Fill(2.5);
201+
if (std::abs(collision.posZ()) > cutzvertex) {
202+
continue;
203+
}
204+
hProcessedEvents->Fill(3.5);
205+
QAHistos.fill(HIST("hVtxZ"), collision.posZ());
206+
207+
const auto& trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex());
208+
209+
for (const auto& trackId : trackIdsThisCollision) { // start loop over tracks
210+
211+
const auto& track = tracks.rawIteratorAt(trackId.trackId());
212+
213+
std::array<float, 2> dca{track.dcaXY(), track.dcaZ()};
214+
215+
if (track.collisionId() != collision.globalIndex()) {
216+
auto trackPar = getTrackParCov(track);
217+
o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, trackPar, 2.f, noMatCorr, &dca);
218+
}
219+
220+
if (!passedSingleTrackSelection(track)) {
221+
continue;
222+
}
223+
224+
const bool isTPCDe = std::abs(track.tpcNSigmaDe()) < cfgTPCNsigma;
225+
const bool isTOFDe_min = std::abs(track.tofNSigmaDe()) > cfgTOFNsigma_min;
226+
const bool isTOFDe_max = std::abs(track.tofNSigmaDe()) < cfgTOFNsigma_max;
227+
228+
if (track.pt() < ptThresholdPid) {
229+
if (isTPCDe && isTOFDe_max) {
230+
QAHistos.fill(HIST("ptAntiDeuteron"), track.pt());
231+
QAHistos.fill(HIST("etaAntideuteron"), track.eta());
232+
QAHistos.fill(HIST("hDCAxyVsPt"), track.pt(), dca[0]);
233+
QAHistos.fill(HIST("hDCAzVsPt"), track.pt(), dca[1]);
234+
QAHistos.fill(HIST("hnSigmaTPCVsPt"), track.pt(), track.tpcNSigmaDe());
235+
QAHistos.fill(HIST("hnSigmaTOFVsPt"), track.pt(), track.tofNSigmaDe());
236+
}
237+
} else {
238+
if (isTPCDe && isTOFDe_min && isTOFDe_max) {
239+
QAHistos.fill(HIST("ptAntiDeuteron"), track.pt());
240+
QAHistos.fill(HIST("etaAntideuteron"), track.eta());
241+
QAHistos.fill(HIST("hDCAxyVsPt"), track.pt(), dca[0]);
242+
QAHistos.fill(HIST("hDCAzVsPt"), track.pt(), dca[1]);
243+
QAHistos.fill(HIST("hnSigmaTPCVsPt"), track.pt(), track.tpcNSigmaDe());
244+
QAHistos.fill(HIST("hnSigmaTOFVsPt"), track.pt(), track.tofNSigmaDe());
245+
}
246+
}
247+
}
248+
}
249+
}
250+
PROCESS_SWITCH(H2fromLb, processData, "processData", false);
251+
252+
void processMC(MCCollisionCandidates::iterator const&, MCTrackCandidates const& tracks, o2::aod::McParticles const&)
253+
{
254+
for (const auto& track : tracks) {
255+
if (!passedSingleTrackSelection(track)) {
256+
continue;
257+
}
258+
259+
if (!track.has_mcParticle()) {
260+
continue;
261+
}
262+
263+
auto mcParticle = track.mcParticle();
264+
if (mcParticle.pdgCode() == pdgCodeDaughter) {
265+
if (std::abs(mcParticle.y()) > 0.5) {
266+
continue;
267+
}
268+
if (mcParticle.isPhysicalPrimary()) {
269+
bool isFromLb = false;
270+
if (separateAntideuterons) {
271+
for (auto& mom : mcParticle.mothers_as<o2::aod::McParticles>()) {
272+
if (mom.pdgCode() == pdgCodeMother) { // Lambda_b
273+
isFromLb = true;
274+
275+
break;
276+
}
277+
}
278+
}
279+
if (isFromLb) {
280+
QAHistos.fill(HIST("hDCAxy-FromLb"), track.dcaXY());
281+
QAHistos.fill(HIST("ptAntiDeuteronFromLb"), track.pt());
282+
} else {
283+
QAHistos.fill(HIST("hDCAxy-Primary"), track.dcaXY());
284+
QAHistos.fill(HIST("ptAntiDeuteronPrimary"), track.pt());
285+
}
286+
}
287+
}
288+
}
289+
}
290+
PROCESS_SWITCH(H2fromLb, processMC, "processMC", true);
291+
292+
void processGen(o2::aod::McCollision const&, o2::aod::McParticles const& mcParticles)
293+
{
294+
hProcessedEvents->Fill(0.5);
295+
for (const auto& mcParticle : mcParticles) {
296+
if (mcParticle.pdgCode() == pdgCodeMother) {
297+
if (std::abs(mcParticle.y()) <= 0.5) {
298+
QAHistos.fill(HIST("ptGeneratedLb"), mcParticle.pt()); // rinominato
299+
}
300+
}
301+
302+
if (mcParticle.pdgCode() == pdgCodeDaughter) {
303+
if (std::abs(mcParticle.y()) > 0.5)
304+
continue;
305+
306+
bool isFromLb = false;
307+
if (mcParticle.has_mothers()) {
308+
for (auto& mom : mcParticle.mothers_as<o2::aod::McParticles>()) {
309+
if (mom.pdgCode() == pdgCodeMother) {
310+
isFromLb = true;
311+
break;
312+
}
313+
}
314+
}
315+
316+
if (isFromLb) {
317+
QAHistos.fill(HIST("ptAntiDeuteronFromLb"), mcParticle.pt());
318+
} else if (mcParticle.isPhysicalPrimary()) {
319+
QAHistos.fill(HIST("ptAntiDeuteronPrimary"), mcParticle.pt());
320+
}
321+
}
322+
}
323+
}
324+
PROCESS_SWITCH(H2fromLb, processGen, "processGen", false);
325+
};
326+
327+
WorkflowSpec defineDataProcessing(ConfigContext const& cfgc)
328+
{
329+
return WorkflowSpec{
330+
adaptAnalysisTask<H2fromLb>(cfgc, TaskName{"task-h2-from-lb"})};
331+
}

0 commit comments

Comments
 (0)