|
| 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