Skip to content

Commit c7bc7c3

Browse files
author
Francesco Mazzaschi
committed
Add task for lambda1405 search
1 parent 92796d1 commit c7bc7c3

File tree

2 files changed

+255
-0
lines changed

2 files changed

+255
-0
lines changed

PWGLF/Tasks/Resonances/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ o2physics_add_dpl_workflow(k892pmanalysis
3939
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore
4040
COMPONENT_NAME Analysis)
4141

42+
o2physics_add_dpl_workflow(lambda1405analysis
43+
SOURCES lambda1405analysis.cxx
44+
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore
45+
COMPONENT_NAME Analysis)
46+
4247
o2physics_add_dpl_workflow(lambda1520analysis
4348
SOURCES lambda1520analysis.cxx
4449
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
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 lambda1405analysis.cxx
13+
/// \brief Analysis task for lambda1405 via sigma kink decay
14+
/// \author Francesco Mazzaschi <francesco.mazzaschi@cern.ch>
15+
16+
#include "PWGLF/DataModel/LFKinkDecayTables.h"
17+
18+
#include "Common/DataModel/EventSelection.h"
19+
#include "Common/DataModel/PIDResponse.h"
20+
21+
#include "Framework/AnalysisTask.h"
22+
#include "Framework/runDataProcessing.h"
23+
#include "ReconstructionDataFormats/PID.h"
24+
25+
using namespace o2;
26+
using namespace o2::framework;
27+
using namespace o2::framework::expressions;
28+
29+
using TracksFull = soa::Join<aod::TracksIU, aod::TracksExtra, aod::TracksCovIU, aod::pidTPCPi>;
30+
using CollisionsFull = soa::Join<aod::Collisions, aod::EvSel>;
31+
using CollisionsFullMC = soa::Join<aod::Collisions, aod::McCollisionLabels, aod::EvSels>;
32+
33+
struct lambda1405candidate {
34+
// Columns for Lambda(1405) candidate
35+
float mass = -1; // Invariant mass of the Lambda(1405) candidate
36+
float sigmaMass = -1; // Invariant mass of the Sigma candidate
37+
float pt = -1; // pT of the Lambda(1405) candidate
38+
int sigmaSign = 0; // Sign of the Sigma candidate: 1 for matter, -1 for antimatter
39+
float sigmaPt = -1; // pT of the Sigma daughter
40+
float piPt = -1; // pT of the pion daughter
41+
float nSigmaTPCPi = -1; // Number of sigmas for the pion candidate
42+
int piFromSigmaID = 0; // ID of the pion from Sigma decay in MC
43+
int sigmaID = 0; // ID of the Sigma candidate in MC
44+
int piID = 0; // ID of the pion candidate in MC
45+
};
46+
47+
struct lambda1405analysis {
48+
int lambda1405PdgCode = 102132; // PDG code for Lambda(1405)
49+
lambda1405candidate lambda1405Cand; // Lambda(1405) candidate structure
50+
// Histograms are defined with HistogramRegistry
51+
HistogramRegistry rEventSelection{"eventSelection", {}, OutputObjHandlingPolicy::AnalysisObject, true, true};
52+
HistogramRegistry rLambda1405{"lambda1405", {}, OutputObjHandlingPolicy::AnalysisObject, true, true};
53+
// Configurable for event selection
54+
Configurable<float> cutzvertex{"cutZVertex", 10.0f, "Accepted z-vertex range (cm)"};
55+
Configurable<float> cutEtaDaught{"cutEtaDaughter", 0.8f, "Eta cut for daughter tracks"};
56+
Configurable<float> cutDCAtoPVSigma{"cutDCAtoPVSigma", 0.1f, "DCA to primary vertex for Sigma candidates (cm)"};
57+
Configurable<float> cutSigmaRadius{"cutSigmaRadius", 20.f, "Minimum radius for Sigma candidates (cm)"};
58+
Configurable<float> cutSigmaMass{"cutSigmaMass", 0.1, "Sigma mass window (MeV/c^2)"};
59+
Configurable<float> cutNITSClusPi{"cutNITSClusPi", 5, "Minimum number of ITS clusters for pion candidate"};
60+
Configurable<float> cutNTPCClusPi{"cutNTPCClusPi", 90, "Minimum number of TPC clusters for pion candidate"};
61+
Configurable<float> cutNSigmaPi{"cutNSigmaPi", 3, "NSigmaTPCPion"};
62+
63+
void init(InitContext const&)
64+
{
65+
// Axes
66+
const AxisSpec ptAxis{50, -5, 5, "#it{p}_{T} (GeV/#it{c})"};
67+
const AxisSpec ptPiAxis{50, -2, 2, "#it{p}_{T}^{#pi} (GeV/#it{c})"};
68+
const AxisSpec ptResolutionAxis{100, -0.5, 0.5, "#it{p}_{T}^{rec} - #it{p}_{T}^{gen} (GeV/#it{c})"};
69+
const AxisSpec massAxis{100, 1.3, 1.5, "m (GeV/#it{c}^{2})"};
70+
const AxisSpec massResolutionAxis{100, -0.1, 0.1, "m_{rec} - m_{gen} (GeV/#it{c}^{2})"};
71+
const AxisSpec nSigmaPiAxis{100, -5, 5, "n#sigma_{#pi}"};
72+
const AxisSpec sigmaMassAxis{100, 1.1, 1.4, "m (GeV/#it{c}^{2})"};
73+
const AxisSpec vertexZAxis{100, -15., 15., "vrtx_{Z} [cm]"};
74+
rEventSelection.add("hVertexZRec", "hVertexZRec", {HistType::kTH1F, {vertexZAxis}});
75+
rLambda1405.add("h2PtMass", "h2PtMass", {HistType::kTH2F, {ptAxis, massAxis}});
76+
rLambda1405.add("h2PtMassSigmaBeforeCuts", "h2PtMassSigmaBeforeCuts", {HistType::kTH2F, {ptAxis, sigmaMassAxis}});
77+
rLambda1405.add("h2PtMassSigma", "h2PtMassSigma", {HistType::kTH2F, {ptAxis, sigmaMassAxis}});
78+
rLambda1405.add("h2SigmaMassVsMass", "h2SigmaMassVsMass", {HistType::kTH2F, {massAxis, sigmaMassAxis}});
79+
rLambda1405.add("h2PtPiNSigma", "h2PtPiNSigma", {HistType::kTH2F, {ptPiAxis, nSigmaPiAxis}});
80+
81+
if (doprocessMC) {
82+
// Add MC histograms if needed
83+
rLambda1405.add("h2MassResolution", "h2MassResolution", {HistType::kTH2F, {massAxis, massResolutionAxis}});
84+
rLambda1405.add("h2PtResolution", "h2PtResolution", {HistType::kTH2F, {ptAxis, ptResolutionAxis}});
85+
rLambda1405.add("h2PtMassMC", "h2PtMassMC", {HistType::kTH2F, {ptAxis, massAxis}});
86+
}
87+
}
88+
89+
template <typename Ttrack>
90+
bool selectPiTrack(const Ttrack& candidate, bool piFromSigma)
91+
{
92+
if (std::abs(candidate.tpcNSigmaPi()) > cutNSigmaPi || candidate.tpcNClsFound() < cutNTPCClusPi || std::abs(candidate.eta()) > cutEtaDaught) {
93+
return false;
94+
}
95+
if (!piFromSigma && candidate.itsNCls() < cutNITSClusPi) {
96+
return false;
97+
}
98+
return true;
99+
}
100+
101+
bool selectCandidate(aod::KinkCands::iterator const& sigmaCand, TracksFull const& tracks)
102+
{
103+
auto piKinkTrack = sigmaCand.trackDaug_as<TracksFull>();
104+
if (!selectPiTrack(piKinkTrack, true)) {
105+
return false;
106+
}
107+
if (sigmaCand.mSigmaMinus() < o2::constants::physics::MassSigmaMinus - cutSigmaMass || sigmaCand.mSigmaMinus() > o2::constants::physics::MassSigmaMinus + cutSigmaMass) {
108+
return false;
109+
}
110+
float sigmaRad = std::hypot(sigmaCand.xDecVtx(), sigmaCand.yDecVtx());
111+
if (sigmaCand.dcaMothPv() > cutDCAtoPVSigma || sigmaRad < cutSigmaRadius) {
112+
return false;
113+
}
114+
rLambda1405.fill(HIST("h2PtMassSigmaBeforeCuts"), sigmaCand.mothSign() * sigmaCand.ptMoth(), sigmaCand.mSigmaMinus());
115+
for (const auto& piTrack : tracks) {
116+
if (piTrack.sign() == sigmaCand.mothSign() || !selectPiTrack(piTrack, false)) {
117+
continue; // Skip if the pion has the same sign as the Sigma or does not pass selection
118+
}
119+
auto sigmaMom = std::array{sigmaCand.pxMoth(), sigmaCand.pyMoth(), sigmaCand.pzMoth()};
120+
auto piMom = std::array{piTrack.px(), piTrack.py(), piTrack.pz()};
121+
float pt = std::hypot(sigmaMom[0] + piMom[0], sigmaMom[1] + piMom[1]);
122+
float invMass = RecoDecay::m(std::array{sigmaMom, piMom}, std::array{o2::constants::physics::MassSigmaMinus, o2::constants::physics::MassPiPlus});
123+
if (invMass < 1.3 || invMass > 1.5) {
124+
continue;
125+
}
126+
lambda1405Cand.piFromSigmaID = piKinkTrack.globalIndex();
127+
lambda1405Cand.sigmaID = sigmaCand.globalIndex();
128+
lambda1405Cand.piID = piTrack.globalIndex();
129+
lambda1405Cand.mass = invMass;
130+
lambda1405Cand.sigmaMass = sigmaCand.mSigmaMinus();
131+
lambda1405Cand.sigmaSign = sigmaCand.mothSign();
132+
lambda1405Cand.pt = pt;
133+
lambda1405Cand.sigmaPt = sigmaCand.ptMoth();
134+
lambda1405Cand.piPt = piTrack.pt();
135+
lambda1405Cand.nSigmaTPCPi = piTrack.tpcNSigmaPi();
136+
return true; // Candidate is selected
137+
}
138+
return false; // No valid pion track found
139+
}
140+
141+
void processData(CollisionsFull::iterator const& collision, aod::KinkCands const& KinkCands, TracksFull const& tracks)
142+
{
143+
if (std::abs(collision.posZ()) > cutzvertex || !collision.sel8()) {
144+
return;
145+
}
146+
rEventSelection.fill(HIST("hVertexZRec"), collision.posZ());
147+
for (const auto& sigmaCand : KinkCands) {
148+
if (selectCandidate(sigmaCand, tracks)) {
149+
rLambda1405.fill(HIST("h2PtMass"), lambda1405Cand.sigmaSign * lambda1405Cand.pt, lambda1405Cand.mass);
150+
rLambda1405.fill(HIST("h2PtMassSigma"), lambda1405Cand.sigmaSign * lambda1405Cand.sigmaPt, lambda1405Cand.sigmaMass);
151+
rLambda1405.fill(HIST("h2SigmaMassVsMass"), lambda1405Cand.mass, lambda1405Cand.sigmaMass);
152+
rLambda1405.fill(HIST("h2PtPiNSigma"), lambda1405Cand.piPt, lambda1405Cand.nSigmaTPCPi);
153+
}
154+
}
155+
}
156+
PROCESS_SWITCH(lambda1405analysis, processData, "Data processing", true);
157+
158+
void processMC(CollisionsFullMC const& collisions, aod::KinkCands const& KinkCands, aod::McTrackLabels const& trackLabelsMC, aod::McParticles const& particlesMC, TracksFull const& tracks)
159+
{
160+
for (const auto& collision : collisions) {
161+
if (std::abs(collision.posZ()) > cutzvertex || !collision.sel8()) {
162+
continue;
163+
}
164+
rEventSelection.fill(HIST("hVertexZRec"), collision.posZ());
165+
for (const auto& sigmaCand : KinkCands) {
166+
if (selectCandidate(sigmaCand, tracks)) {
167+
// Do MC association
168+
auto mcLabPiKink = trackLabelsMC.rawIteratorAt(lambda1405Cand.piFromSigmaID);
169+
auto mcLabSigma = trackLabelsMC.rawIteratorAt(lambda1405Cand.sigmaID);
170+
auto mcLabPi = trackLabelsMC.rawIteratorAt(lambda1405Cand.piID);
171+
if (!mcLabSigma.has_mcParticle() || mcLabPiKink.has_mcParticle() || mcLabPi.has_mcParticle()) {
172+
continue; // Skip if no valid MC association
173+
}
174+
auto mcTrackPiKink = mcLabPiKink.mcParticle_as<aod::McParticles>();
175+
auto mcTrackSigma = mcLabSigma.mcParticle_as<aod::McParticles>();
176+
auto mcTrackPi = mcLabPi.mcParticle_as<aod::McParticles>();
177+
if (std::abs(mcTrackPiKink.pdgCode()) != 211 || std::abs(mcTrackSigma.pdgCode()) != 3122 || std::abs(mcTrackPi.pdgCode()) != 211) {
178+
continue; // Skip if not a valid pion or Sigma candidate
179+
}
180+
if (!mcTrackPiKink.has_mothers() || !mcTrackSigma.has_mothers() || !mcTrackPi.has_mothers()) {
181+
continue; // Skip if no mothers found
182+
}
183+
// check if kink pi comes from the sigma
184+
bool isPiFromSigma = false;
185+
for (const auto& piMother : mcTrackPiKink.mothers_as<aod::McParticles>()) {
186+
if (piMother.globalIndex() == mcTrackSigma.globalIndex()) {
187+
isPiFromSigma = true;
188+
break; // Found the mother, exit loop
189+
}
190+
}
191+
if (!isPiFromSigma) {
192+
continue; // Skip if the pion does not come from the Sigma
193+
}
194+
// check that labpi and labsigma have the same mother (a lambda1405 candidate)
195+
int lambda1405Id = -1;
196+
for (const auto& piMother : mcTrackPi.mothers_as<aod::McParticles>()) {
197+
for (const auto& sigmaMother : mcTrackSigma.mothers_as<aod::McParticles>()) {
198+
if (piMother.globalIndex() == sigmaMother.globalIndex() && std::abs(piMother.pdgCode()) == lambda1405PdgCode) {
199+
lambda1405Id = piMother.globalIndex();
200+
break; // Found the mother, exit loop
201+
}
202+
}
203+
}
204+
if (lambda1405Id == -1) {
205+
continue; // Skip if the Sigma and pion do not share the same lambda1405 candidate
206+
}
207+
auto lambda1405Mother = particlesMC.rawIteratorAt(lambda1405Id);
208+
LOG(info) << "Particle selected!";
209+
float lambda1405Mass = std::sqrt(lambda1405Mother.e() * lambda1405Mother.e() - lambda1405Mother.p() * lambda1405Mother.p());
210+
rLambda1405.fill(HIST("h2PtMass"), lambda1405Cand.sigmaSign * lambda1405Cand.pt, lambda1405Cand.mass);
211+
rLambda1405.fill(HIST("h2PtMassSigma"), lambda1405Cand.sigmaSign * lambda1405Cand.sigmaPt, lambda1405Cand.sigmaMass);
212+
rLambda1405.fill(HIST("h2SigmaMassVsMass"), lambda1405Cand.mass, lambda1405Cand.sigmaMass);
213+
rLambda1405.fill(HIST("h2PtPiNSigma"), lambda1405Cand.piPt, lambda1405Cand.nSigmaTPCPi);
214+
rLambda1405.fill(HIST("h2MassResolution"), lambda1405Mass, lambda1405Mass - lambda1405Cand.mass);
215+
rLambda1405.fill(HIST("h2PtResolution"), lambda1405Cand.pt, lambda1405Cand.pt - lambda1405Mother.pt());
216+
}
217+
}
218+
}
219+
// Loop over generated particles to fill MC histograms
220+
for (const auto& mcPart : particlesMC) {
221+
if (std::abs(mcPart.pdgCode()) != lambda1405PdgCode) {
222+
continue; // Only consider Lambda(1405) candidates
223+
}
224+
225+
if (!mcPart.has_daughters()) {
226+
continue; // Skip if no daughters
227+
}
228+
// Check if the Lambda(1405) has a Sigma daughter
229+
bool hasSigmaDaughter = false;
230+
for (const auto& daughter : mcPart.daughters_as<aod::McParticles>()) {
231+
if (std::abs(daughter.pdgCode()) == 3122) { // Sigma PDG code
232+
hasSigmaDaughter = true;
233+
break; // Found a Sigma daughter, exit loop
234+
}
235+
}
236+
if (!hasSigmaDaughter) {
237+
continue; // Skip if no Sigma daughter found
238+
}
239+
240+
float mcMass = std::sqrt(mcPart.e() * mcPart.e() - mcPart.p() * mcPart.p());
241+
rLambda1405.fill(HIST("h2PtMassMC"), mcPart.pt(), mcMass);
242+
}
243+
}
244+
PROCESS_SWITCH(lambda1405analysis, processMC, "MC processing", false);
245+
};
246+
WorkflowSpec defineDataProcessing(ConfigContext const& cfgc)
247+
{
248+
return WorkflowSpec{
249+
adaptAnalysisTask<lambda1405analysis>(cfgc)};
250+
}

0 commit comments

Comments
 (0)