|
| 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 | +// Example cascade analysis task |
| 13 | +// ============================= |
| 14 | +// |
| 15 | +// This code loops over a CascData table and produces some |
| 16 | +// standard analysis output. It requires either |
| 17 | +// the cascade finder or the cascade builder tasks |
| 18 | +// to have been executed in the workflow (before). |
| 19 | +// |
| 20 | +// Note, however, that only the cascade builder does |
| 21 | +// time-frame-aware cascade building at this time. This |
| 22 | +// is deliberate, as time-frame information is available |
| 23 | +// during reconstruction and collision association is better |
| 24 | +// done at that stage. |
| 25 | +// |
| 26 | +// this task makes use of labels provided by the cascade |
| 27 | +// builder to process cascades quickly. |
| 28 | +// |
| 29 | +// Comments, questions, complaints, suggestions? |
| 30 | +// Please write to: |
| 31 | +// david.dobrigkeit.chinellato@cern.ch |
| 32 | +// |
| 33 | + |
| 34 | +#include "Framework/runDataProcessing.h" |
| 35 | +#include "Framework/AnalysisTask.h" |
| 36 | +#include "Framework/AnalysisDataModel.h" |
| 37 | +#include "Framework/ASoAHelpers.h" |
| 38 | +#include "ReconstructionDataFormats/Track.h" |
| 39 | +#include "Common/Core/RecoDecay.h" |
| 40 | +#include "Common/Core/trackUtilities.h" |
| 41 | +#include "Common/DataModel/StrangenessTables.h" |
| 42 | +#include "Common/Core/TrackSelection.h" |
| 43 | +#include "Common/DataModel/TrackSelectionTables.h" |
| 44 | +#include "Common/DataModel/EventSelection.h" |
| 45 | +#include "Common/DataModel/Centrality.h" |
| 46 | + |
| 47 | +#include <TFile.h> |
| 48 | +#include <TH2F.h> |
| 49 | +#include <TProfile.h> |
| 50 | +#include <TLorentzVector.h> |
| 51 | +#include <Math/Vector4D.h> |
| 52 | +#include <TPDGCode.h> |
| 53 | +#include <TDatabasePDG.h> |
| 54 | +#include <cmath> |
| 55 | +#include <array> |
| 56 | +#include <cstdlib> |
| 57 | +#include "Framework/ASoAHelpers.h" |
| 58 | + |
| 59 | +using namespace o2; |
| 60 | +using namespace o2::framework; |
| 61 | +using namespace o2::framework::expressions; |
| 62 | +using std::array; |
| 63 | + |
| 64 | +//use parameters + cov mat non-propagated, aux info + (extension propagated) |
| 65 | +using FullTracksExt = soa::Join<aod::Tracks, aod::TracksExtra, aod::TracksCov, aod::TracksDCA>; |
| 66 | +using FullTracksExtIU = soa::Join<aod::TracksIU, aod::TracksExtra, aod::TracksCovIU, aod::TracksDCA>; |
| 67 | +using LabeledCascades = soa::Join<aod::CascDataExt, aod::McCascLabels>; |
| 68 | + |
| 69 | +struct cascadeQaMC { |
| 70 | + // Basic checks |
| 71 | + HistogramRegistry registry{ |
| 72 | + "registry", |
| 73 | + { |
| 74 | + {"hMassXiMinus", "hMassXiMinus", {HistType::kTH1F, {{3000, 0.0f, 3.0f, "Inv. Mass (GeV/c^{2})"}}}}, |
| 75 | + {"hMassXiPlus", "hMassXiPlus", {HistType::kTH1F, {{3000, 0.0f, 3.0f, "Inv. Mass (GeV/c^{2}²)"}}}}, |
| 76 | + {"hMassOmegaMinus", "hMassOmegaMinus", {HistType::kTH1F, {{3000, 0.0f, 3.0f, "Inv. Mass (GeV/c^{2})"}}}}, |
| 77 | + {"hMassOmegaPlus", "hMassOmegaPlus", {HistType::kTH1F, {{3000, 0.0f, 3.0f, "Inv. Mass (GeV/c^{2})"}}}}, |
| 78 | + |
| 79 | + {"hV0Radius", "hV0Radius", {HistType::kTH1F, {{1000, 0.0f, 100.0f, "cm"}}}}, |
| 80 | + {"hCascRadius", "hCascRadius", {HistType::kTH1F, {{1000, 0.0f, 100.0f, "cm"}}}}, |
| 81 | + {"hV0CosPA", "hV0CosPA", {HistType::kTH1F, {{1000, 0.95f, 1.0f}}}}, |
| 82 | + {"hCascCosPA", "hCascCosPA", {HistType::kTH1F, {{1000, 0.95f, 1.0f}}}}, |
| 83 | + {"hDCAPosToPV", "hDCAPosToPV", {HistType::kTH1F, {{1000, -10.0f, 10.0f, "cm"}}}}, |
| 84 | + {"hDCANegToPV", "hDCANegToPV", {HistType::kTH1F, {{1000, -10.0f, 10.0f, "cm"}}}}, |
| 85 | + {"hDCABachToPV", "hDCABachToPV", {HistType::kTH1F, {{1000, -10.0f, 10.0f, "cm"}}}}, |
| 86 | + {"hDCAV0ToPV", "hDCAV0ToPV", {HistType::kTH1F, {{1000, -10.0f, 10.0f, "cm"}}}}, |
| 87 | + {"hDCAV0Dau", "hDCAV0Dau", {HistType::kTH1F, {{1000, 0.0f, 10.0f, "cm^{2}"}}}}, |
| 88 | + {"hDCACascDau", "hDCACascDau", {HistType::kTH1F, {{1000, 0.0f, 10.0f, "cm^{2}"}}}}, |
| 89 | + {"hLambdaMass", "hLambdaMass", {HistType::kTH1F, {{1000, 0.0f, 10.0f, "Inv. Mass (GeV/c^{2})"}}}}, |
| 90 | + }, |
| 91 | + }; |
| 92 | + |
| 93 | + Configurable<bool> assocMC{"assocMC", true, "fill histograms only for MC associated candidates"}; |
| 94 | + |
| 95 | + void process(aod::Collision const& collision, LabeledCascades const& Cascades, aod::McParticles const&) |
| 96 | + { |
| 97 | + for (auto& casc : Cascades) { |
| 98 | + Int_t lPDG = 0; |
| 99 | + if (assocMC) { |
| 100 | + if (!casc.has_mcParticle()) |
| 101 | + continue; |
| 102 | + auto cascmc = casc.mcParticle(); |
| 103 | + if (TMath::Abs(cascmc.pdgCode()) == 3312 || TMath::Abs(cascmc.pdgCode()) == 3334) |
| 104 | + lPDG = cascmc.pdgCode(); |
| 105 | + } |
| 106 | + |
| 107 | + if (casc.sign() < 0) { // FIXME: could be done better... |
| 108 | + if (!assocMC || lPDG == 3312) |
| 109 | + registry.fill(HIST("hMassXiMinus"), casc.mXi()); |
| 110 | + if (!assocMC || lPDG == 3334) |
| 111 | + registry.fill(HIST("hMassOmegaMinus"), casc.mOmega()); |
| 112 | + } else { |
| 113 | + if (!assocMC || lPDG == -3312) |
| 114 | + registry.fill(HIST("hMassXiPlus"), casc.mXi()); |
| 115 | + if (!assocMC || lPDG == -3334) |
| 116 | + registry.fill(HIST("hMassOmegaPlus"), casc.mOmega()); |
| 117 | + } |
| 118 | + // The basic eleven! |
| 119 | + registry.fill(HIST("hV0Radius"), casc.v0radius()); |
| 120 | + registry.fill(HIST("hCascRadius"), casc.cascradius()); |
| 121 | + registry.fill(HIST("hV0CosPA"), casc.v0cosPA(collision.posX(), collision.posY(), collision.posZ())); |
| 122 | + registry.fill(HIST("hCascCosPA"), casc.casccosPA(collision.posX(), collision.posY(), collision.posZ())); |
| 123 | + registry.fill(HIST("hDCAPosToPV"), casc.dcapostopv()); |
| 124 | + registry.fill(HIST("hDCANegToPV"), casc.dcanegtopv()); |
| 125 | + registry.fill(HIST("hDCABachToPV"), casc.dcabachtopv()); |
| 126 | + registry.fill(HIST("hDCAV0ToPV"), casc.dcav0topv(collision.posX(), collision.posY(), collision.posZ())); |
| 127 | + registry.fill(HIST("hDCAV0Dau"), casc.dcaV0daughters()); |
| 128 | + registry.fill(HIST("hDCACascDau"), casc.dcacascdaughters()); |
| 129 | + registry.fill(HIST("hLambdaMass"), casc.mLambda()); |
| 130 | + } |
| 131 | + } |
| 132 | +}; |
| 133 | + |
| 134 | +struct cascadeAnalysisMC { |
| 135 | + HistogramRegistry registry{ |
| 136 | + "registry", |
| 137 | + {}, |
| 138 | + }; |
| 139 | + |
| 140 | + void init(InitContext const&) |
| 141 | + { |
| 142 | + AxisSpec ptAxis = {200, 0.0f, 10.0f, "it{p}_{T} (GeV/c)"}; |
| 143 | + AxisSpec massAxisXi = {200, 1.222f, 1.422f, "Inv. Mass (GeV/c^{2})"}; |
| 144 | + AxisSpec massAxisOmega = {200, 1.572f, 1.772f, "Inv. Mass (GeV/c^{2})"}; |
| 145 | + |
| 146 | + registry.add("hCandidateCounter", "hCandidateCounter", {HistType::kTH1F, {{10, 0.0f, 10.0f}}}); |
| 147 | + registry.add("h2dMassXiMinus", "h2dMassXiMinus", {HistType::kTH2F, {ptAxis, massAxisXi}}); |
| 148 | + registry.add("h2dMassXiPlus", "h2dMassXiPlus", {HistType::kTH2F, {ptAxis, massAxisXi}}); |
| 149 | + registry.add("h2dMassOmegaMinus", "h2dMassOmegaMinus", {HistType::kTH2F, {ptAxis, massAxisOmega}}); |
| 150 | + registry.add("h2dMassOmegaPlus", "h2dMassOmegaPlus", {HistType::kTH2F, {ptAxis, massAxisOmega}}); |
| 151 | + } |
| 152 | + |
| 153 | + // Selection criteria |
| 154 | + Configurable<double> v0cospa{"v0cospa", 0.95, "V0 CosPA"}; // double -> N.B. dcos(x)/dx = 0 at x=0) |
| 155 | + Configurable<double> casccospa{"casccospa", 0.95, "Casc CosPA"}; // double -> N.B. dcos(x)/dx = 0 at x=0) |
| 156 | + Configurable<float> dcav0dau{"dcav0dau", 2.0, "DCA V0 Daughters"}; |
| 157 | + Configurable<float> dcacascdau{"dcacascdau", 1.0, "DCA Casc Daughters"}; |
| 158 | + Configurable<float> dcanegtopv{"dcanegtopv", .05, "DCA Neg To PV"}; |
| 159 | + Configurable<float> dcapostopv{"dcapostopv", .05, "DCA Pos To PV"}; |
| 160 | + Configurable<float> dcabachtopv{"dcabachtopv", .05, "DCA Bach To PV"}; |
| 161 | + Configurable<float> dcav0topv{"dcav0topv", .05, "DCA V0 To PV"}; |
| 162 | + Configurable<float> v0radius{"v0radius", 0.9, "v0radius"}; |
| 163 | + Configurable<float> cascradius{"cascradius", 0.5, "cascradius"}; |
| 164 | + Configurable<float> v0masswindow{"v0masswindow", 0.008, "v0masswindow"}; |
| 165 | + Configurable<bool> eventSelection{"eventSelection", true, "event selection"}; |
| 166 | + |
| 167 | + Configurable<int> tpcClusters{"tpcClusters", 70, "minimum number of TPC clusters requirement"}; |
| 168 | + Configurable<int> itsClusters{"itsClusters", 4, "minimum number of ITS clusters requirement for ITSSA tracks"}; |
| 169 | + Configurable<bool> allowITSSAbachelor{"allowITSSAbachelor", true, "allow for bachelor <- cascade track to be via ITS tracking only"}; |
| 170 | + Configurable<bool> allowITSSAproton{"allowITSSAproton", true, "allow for proton <- lambda track to be via ITS tracking only"}; |
| 171 | + Configurable<bool> allowITSSApion{"allowITSSApion", false, "allow for pion <- lambda track to be via ITS tracking only "}; |
| 172 | + Configurable<bool> assocMC{"assocMC", true, "fill histograms only for MC associated candidates"}; |
| 173 | + |
| 174 | + Filter preFilter = |
| 175 | + nabs(aod::cascdata::dcapostopv) > dcapostopv&& nabs(aod::cascdata::dcanegtopv) > dcanegtopv&& nabs(aod::cascdata::dcabachtopv) > dcabachtopv&& aod::cascdata::dcaV0daughters < dcav0dau&& aod::cascdata::dcacascdaughters < dcacascdau; |
| 176 | + |
| 177 | + Partition<LabeledCascades> associatedCascades = (aod::mccasclabel::mcParticleId > -1); |
| 178 | + |
| 179 | + template <class TCascTracksTo> |
| 180 | + void fillCascadeOutput(soa::Filtered<LabeledCascades> const& cascades, float const& pvx, float const& pvy, float const& pvz) |
| 181 | + //function to process cascades and generate corresponding invariant mass distributions |
| 182 | + { |
| 183 | + for (auto& casc : cascades) { |
| 184 | + registry.fill(HIST("hCandidateCounter"), 0.5); //all candidates |
| 185 | + //check mc association if requested |
| 186 | + Int_t lPDG = 0; |
| 187 | + if (assocMC) { |
| 188 | + if (!casc.has_mcParticle()) |
| 189 | + continue; |
| 190 | + auto cascmc = casc.mcParticle(); |
| 191 | + if (TMath::Abs(cascmc.pdgCode()) == 3312 || TMath::Abs(cascmc.pdgCode()) == 3334) |
| 192 | + lPDG = cascmc.pdgCode(); |
| 193 | + } |
| 194 | + |
| 195 | + auto v0 = casc.v0_as<o2::aod::V0sLinked>(); |
| 196 | + if (!(v0.has_v0Data())) { |
| 197 | + continue; //skip those cascades for which V0 doesn't exist |
| 198 | + } |
| 199 | + registry.fill(HIST("hCandidateCounter"), 1.5); //v0data exists |
| 200 | + auto v0data = v0.v0Data(); // de-reference index to correct v0data in case it exists |
| 201 | + auto bachTrackCast = casc.bachelor_as<TCascTracksTo>(); |
| 202 | + auto posTrackCast = v0data.posTrack_as<TCascTracksTo>(); |
| 203 | + auto negTrackCast = v0data.negTrack_as<TCascTracksTo>(); |
| 204 | + |
| 205 | + //track-level selections |
| 206 | + Bool_t lEnoughTPCNClsBac = kTRUE; |
| 207 | + Bool_t lEnoughTPCNClsPos = kTRUE; |
| 208 | + Bool_t lEnoughTPCNClsNeg = kTRUE; |
| 209 | + Bool_t lEnoughITSNClsBac = kTRUE; |
| 210 | + Bool_t lEnoughITSNClsPos = kTRUE; |
| 211 | + Bool_t lEnoughITSNClsNeg = kTRUE; |
| 212 | + |
| 213 | + if (bachTrackCast.tpcNClsFound() < tpcClusters) |
| 214 | + lEnoughTPCNClsBac = kFALSE; |
| 215 | + if (posTrackCast.tpcNClsFound() < tpcClusters) |
| 216 | + lEnoughTPCNClsPos = kFALSE; |
| 217 | + if (negTrackCast.tpcNClsFound() < tpcClusters) |
| 218 | + lEnoughTPCNClsNeg = kFALSE; |
| 219 | + if (bachTrackCast.itsNCls() < itsClusters) |
| 220 | + lEnoughITSNClsBac = kFALSE; |
| 221 | + if (posTrackCast.itsNCls() < itsClusters) |
| 222 | + lEnoughITSNClsPos = kFALSE; |
| 223 | + if (negTrackCast.itsNCls() < itsClusters) |
| 224 | + lEnoughITSNClsNeg = kFALSE; |
| 225 | + |
| 226 | + //Logic: either you have enough TPC clusters, OR you enabled ITSSA and have enough ITS clusters as requested |
| 227 | + //N.B.: This will require dedicated studies! |
| 228 | + |
| 229 | + Bool_t lGoodCandidate = kFALSE; |
| 230 | + if (casc.sign() < 0) { |
| 231 | + if ( |
| 232 | + (lEnoughTPCNClsBac || (allowITSSAbachelor && lEnoughITSNClsBac)) && //bachelor conditional |
| 233 | + (lEnoughTPCNClsPos || (allowITSSAproton && lEnoughITSNClsPos)) && //bachelor conditional |
| 234 | + (lEnoughTPCNClsNeg || (allowITSSApion && lEnoughITSNClsNeg)) //bachelor conditional |
| 235 | + ) { |
| 236 | + lGoodCandidate = kTRUE; |
| 237 | + } |
| 238 | + } |
| 239 | + if (casc.sign() > 0) { |
| 240 | + if ( |
| 241 | + (lEnoughTPCNClsBac || (allowITSSAbachelor && lEnoughITSNClsBac)) && //bachelor conditional |
| 242 | + (lEnoughTPCNClsPos || (allowITSSApion && lEnoughITSNClsPos)) && //bachelor conditional |
| 243 | + (lEnoughTPCNClsNeg || (allowITSSAproton && lEnoughITSNClsNeg)) //bachelor conditional |
| 244 | + ) { |
| 245 | + lGoodCandidate = kTRUE; |
| 246 | + } |
| 247 | + } |
| 248 | + if (!lGoodCandidate) |
| 249 | + continue; |
| 250 | + registry.fill(HIST("hCandidateCounter"), 2.5); //okay track quality |
| 251 | + |
| 252 | + if (casc.v0radius() > v0radius && |
| 253 | + casc.cascradius() > cascradius && |
| 254 | + casc.v0cosPA(pvx, pvy, pvz) > v0cospa && |
| 255 | + casc.casccosPA(pvx, pvy, pvz) > casccospa && |
| 256 | + casc.dcav0topv(pvx, pvy, pvz) > dcav0topv && |
| 257 | + TMath::Abs(casc.mLambda() - 1.115683) < v0masswindow) { |
| 258 | + registry.fill(HIST("hCandidateCounter"), 3.5); //pass cascade selections |
| 259 | + if (casc.sign() < 0) { |
| 260 | + if (TMath::Abs(casc.yXi()) < 0.5 && ((!assocMC) || (lPDG == 3312))) { |
| 261 | + registry.fill(HIST("h2dMassXiMinus"), casc.template pt(), casc.template mXi()); |
| 262 | + } |
| 263 | + if (TMath::Abs(casc.yOmega()) < 0.5 && ((!assocMC) || (lPDG == 3334))) { |
| 264 | + registry.fill(HIST("h2dMassOmegaMinus"), casc.template pt(), casc.template mOmega()); |
| 265 | + } |
| 266 | + } else { |
| 267 | + if (TMath::Abs(casc.yXi()) < 0.5 && ((!assocMC) || (lPDG == -3312))) { |
| 268 | + registry.fill(HIST("h2dMassXiPlus"), casc.template pt(), casc.template mXi()); |
| 269 | + } |
| 270 | + if (TMath::Abs(casc.yOmega()) < 0.5 && ((!assocMC) || (lPDG == -3334))) { |
| 271 | + registry.fill(HIST("h2dMassOmegaPlus"), casc.template pt(), casc.template mOmega()); |
| 272 | + } |
| 273 | + } |
| 274 | + } |
| 275 | + } |
| 276 | + } |
| 277 | + |
| 278 | + void processRun3(soa::Join<aod::Collisions, aod::EvSels>::iterator const& collision, soa::Filtered<LabeledCascades> const& Cascades, aod::V0sLinked const&, aod::V0Datas const&, FullTracksExtIU const&, aod::McParticles const&) |
| 279 | + //process function subscribing to Run 3-like analysis objects |
| 280 | + { |
| 281 | + //Run 3 event selection criteria |
| 282 | + if (eventSelection && !collision.sel8()) { |
| 283 | + return; |
| 284 | + } |
| 285 | + //fill cascade information with tracksIU typecast (Run 3) |
| 286 | + fillCascadeOutput<FullTracksExtIU>(Cascades, collision.posX(), collision.posY(), collision.posZ()); |
| 287 | + } |
| 288 | + PROCESS_SWITCH(cascadeAnalysisMC, processRun3, "Process Run 3 data", true); |
| 289 | + |
| 290 | + void processRun2(soa::Join<aod::Collisions, aod::EvSels, aod::CentRun2V0Ms>::iterator const& collision, soa::Filtered<LabeledCascades> const& Cascades, aod::V0sLinked const&, aod::V0Datas const&, FullTracksExt const&, aod::McParticles const&) |
| 291 | + //process function subscribing to Run 3-like analysis objects |
| 292 | + { |
| 293 | + //Run 2 event selection criteria |
| 294 | + if (eventSelection && !collision.alias()[kINT7]) { |
| 295 | + return; |
| 296 | + } |
| 297 | + if (eventSelection && !collision.sel7()) { |
| 298 | + return; |
| 299 | + } |
| 300 | + //fill cascade information with tracks typecast (Run 2) |
| 301 | + fillCascadeOutput<FullTracksExt>(Cascades, collision.posX(), collision.posY(), collision.posZ()); |
| 302 | + } |
| 303 | + PROCESS_SWITCH(cascadeAnalysisMC, processRun2, "Process Run 2 data", false); |
| 304 | +}; |
| 305 | + |
| 306 | +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) |
| 307 | +{ |
| 308 | + return WorkflowSpec{ |
| 309 | + adaptAnalysisTask<cascadeAnalysisMC>(cfgc), |
| 310 | + adaptAnalysisTask<cascadeQaMC>(cfgc)}; |
| 311 | +} |
0 commit comments