diff --git a/PWGLF/Tasks/Strangeness/strangecasctrack.cxx b/PWGLF/Tasks/Strangeness/strangecasctrack.cxx index acce86c5847..609537fcf36 100644 --- a/PWGLF/Tasks/Strangeness/strangecasctrack.cxx +++ b/PWGLF/Tasks/Strangeness/strangecasctrack.cxx @@ -13,112 +13,147 @@ /// \brief Analysis of strangeness tracking efficiency via primary production of Omega and Xi in Run 3 /// \author Yakiv Paroviak (yakiv.paroviak@cern.ch) +#include "PWGLF/DataModel/LFStrangenessPIDTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" +#include "CCDB/BasicCCDBManager.h" #include "Framework/ASoAHelpers.h" #include "Framework/AnalysisDataModel.h" #include "Framework/AnalysisTask.h" +#include "Framework/O2DatabasePDGPlugin.h" +#include "Framework/StaticFor.h" #include "Framework/runDataProcessing.h" +#include + +#include "TF1.h" +#include "TF2.h" +#include + +#include +#include using namespace o2; +using namespace o2::constants::math; using namespace o2::framework; using namespace o2::framework::expressions; -using namespace o2::constants::math; + +// tables for derived data +using DerCollisionWMult = soa::Join::iterator; +using DerCascDatas = soa::Join; +using DerTraCascDatas = soa::Join; + +// tables for derived MC +using DerMCGenCascades = soa::Join; +using DerMCRecCollision = soa::Join::iterator; +using DerMCRecCascDatas = soa::Join; +using DerMCRecTraCascDatas = soa::Join; + +// tables for PID selection +using DauTracks = soa::Join; struct StrangeCascTrack { - using TraCascDatas = soa::Join; - using CascDatas = soa::Join; + Service ccdb; + Service pdgDB; + + PresliceUnsorted> perMcCollision = aod::v0data::straMCCollisionId; HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + // subprocess switches: Configurable doProcessPP{"doProcessPP", true, "true for pp, false for PbPb and OO"}; - Configurable doProcessPbPb{"doProcessPbPb", false, "true for PbPb, false for pp and OO"}; - Configurable doProcessOO{"doProcessOO", false, "true for OO, false for pp and PbPb"}; - - Configurable doProcessMC{"doProcessMC", false, "true for MC, false for data"}; + // selections + Configurable doApplyCuts{"doApplyCuts", true, "apply cuts"}; // dca for filtering data primaries + Configurable doApplyTPCPID{"doApplyTPCPID", true, "apply tpc pid to dau tracks"}; + Configurable doApplyTOFPID{"doApplyTOFPID", true, "apply tof pid to dau tracks"}; + Configurable doCompetingMassRej{"doCompetingMassRej", true, "competing mass rejection for omegas"}; + // corrections + Configurable doApplyEfficiency{"doApplyEfficiency", false, "apply efficiency correction"}; + Configurable doPropagateEfficiency{"doPropagateEfficiency", false, "apply efficiency propagation"}; + Configurable doApplyPurity{"doApplyPurity", false, "apply purity correction"}; + Configurable doPropagatePurity{"doPropagatePurity", false, "apply purity propagation"}; + Configurable ccdburl{"ccdburl", "http://alice-ccdb.cern.ch", "url of the ccdb repository to use"}; + Configurable efficiencyCCDBPath{"efficiencyCCDBPath", "GLO/Config/GeometryAligned", "Path of the efficiency corrections"}; + // mc settings + Configurable doFillTruth{"doFillTruth", false, "require MC truth for reco"}; - Configurable doRequireFT0{"doRequireFT0", false, "true for offline trigger for Run 3"}; - Configurable doApplyPID{"doApplyPID", false, "true for offline trigger for Run 3"}; - Configurable doApplyCuts{"doApplyCuts", true, "true for offline trigger for Run 3"}; + // event and dau track selection + struct : ConfigurableGroup { + Configurable cutDCAtoPVxy{"cutDCAtoPVxy", 0.02f, "max cascade dca to PV in xy"}; + Configurable cutDCAtoPVz{"cutDCAtoPVz", 0.02f, "max cascade dca to PV in z"}; + Configurable compMassRej{"compMassRej", 0.008, "Competing mass rejection"}; + // TPC PID selection + Configurable NSigmaTPCPion{"NSigmaTPCPion", 4, "NSigmaTPCPion"}; + Configurable NSigmaTPCKaon{"NSigmaTPCKaon", 4, "NSigmaTPCKaon"}; + Configurable NSigmaTPCProton{"NSigmaTPCProton", 4, "NSigmaTPCProton"}; + // TOF PID selection + Configurable NSigmaTOFPion{"NSigmaTOFPion", 3, "NSigmaTOFPion"}; + Configurable NSigmaTOFKaon{"NSigmaTOFKaon", 3, "NSigmaTOFKaon"}; + Configurable NSigmaTOFProton{"NSigmaTOFProton", 3, "NSigmaTOFProton"}; + } selCuts; - ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0}, "p_{T} (GeV/c)"}; - ConfigurableAxis axisMult{"axisMult", {VARIABLE_WIDTH, 0.0f, 0.01f, 1.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 70.0f, 100.0f}, "Multiplicity"}; - ConfigurableAxis axisOmegaMass{"axisOmegaMass", {2000, 1.6, 1.8}, "#Omega M_{inv} (GeV/c^{2})"}; - ConfigurableAxis axisXiMass{"axisXiMass", {2000, 1.2, 1.4}, "#Xi M_{inv} (GeV/c^{2})"}; + // axes + struct : ConfigurableGroup { + ConfigurableAxis axisPhi{"Phi", {72, 0, TwoPI}, "#phi"}; + ConfigurableAxis axisEta{"Eta", {102, -2.01, 2.01}, "#eta"}; + ConfigurableAxis axisDCAxy{"DCA to xy plane", {500, 0., 0.5}, "cm"}; + ConfigurableAxis axisDCAz{"DCA to z plane", {500, 0., 0.5}, "cm"}; + ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0}, "p_{T} (GeV/c)"}; + ConfigurableAxis axisMult{"axisMult", {VARIABLE_WIDTH, 0.0f, 0.01f, 1.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 70.0f, 100.0f}, "FT0 mult %"}; + ConfigurableAxis axisOmegaMass{"axisOmegaMass", {2000, 1.6, 1.8}, "#Omega M_{inv} (GeV/c^{2})"}; + ConfigurableAxis axisXiMass{"axisXiMass", {2000, 1.2, 1.4}, "#Xi M_{inv} (GeV/c^{2})"}; + } axesConfig; - Configurable CutDCAtoPVxy{"CutDCAtoPVxy", 0.02f, "max cascade dca to PV in xy"}; - Configurable CutDCAtoPVz{"CutDCAtoPVz", 0.02f, "max cascade dca to PV in z"}; + // // Filters events + // if (doFilterEvents) { + // Filter eventFilter = (o2::aod::evsel::sel8 == true); + // Filter posZFilter = (nabs(o2::aod::collision::posZ) < cutzvertex); + // Filter posZFilterMC = (nabs(o2::aod::mccollision::posZ) < cutzvertex); + // } - void init(InitContext const&) - { - histos.add("Events/EvCounter", "Event Counter", kTH1F, {{1, 0, 1}}); - histos.add("Events/PVx", "PV x position", kTH1F, {{200, -0.1, 0.1}}); - histos.add("Events/PVy", "PV y position", kTH1F, {{200, -0.1, 0.1}}); - histos.add("Events/PVz", "PV z position", kTH1F, {{100, -20, 20}}); - histos.add("Events/Mult", "Multiplicity", kTH1F, {axisMult}); - - histos.add("Tracked/Phi", "Phi", kTH1F, {{100, 0., 2 * M_PI}}); - histos.add("Tracked/Eta", "Eta", kTH1F, {{102, -2.01, 2.01}}); - histos.add("Tracked/DCAxy", "DCA to xy", kTH1F, {{500, 0., 0.5}}); - histos.add("Tracked/DCAz", "DCA to z", kTH1F, {{500, 0., 0.5}}); - histos.add("Tracked/EvMult", "Multiplicity of events with >=1 cascade", kTH1F, {axisMult}); - histos.add("Tracked/MassOmega", "Invariant mass hypothesis", kTH1F, {axisOmegaMass}); - histos.add("Tracked/MassXi", "Invariant mass hypothesis", kTH1F, {axisXiMass}); - histos.add("Tracked/Omega", "", kTHnD, {axisOmegaMass, axisPt, axisMult}); - histos.add("Tracked/Xi", "", kTHnD, {axisXiMass, axisPt, axisMult}); - - histos.add("All/Phi", "Phi", kTH1F, {{100, 0., 2 * M_PI}}); - histos.add("All/Eta", "Eta", kTH1F, {{102, -2.01, 2.01}}); - histos.add("All/DCAxy", "DCA to xy", kTH1F, {{1000, 0, 1.}}); - histos.add("All/DCAz", "DCA to z", kTH1F, {{1000, 0, 1.}}); - histos.add("All/EvMult", "Multiplicity of events with >=1 cascade", kTH1F, {axisMult}); - histos.add("All/MassOmega", "Invariant mass hypothesis", kTH1F, {axisOmegaMass}); - histos.add("All/MassXi", "Invariant mass hypothesis", kTH1F, {axisXiMass}); - histos.add("All/Omega", "", kTHnD, {axisOmegaMass, axisPt, axisMult}); - histos.add("All/Xi", "", kTHnD, {axisXiMass, axisPt, axisMult}); - } + // cascade reconstruction types + static constexpr std::string_view kTypeNames[] = {"Standard", "Tracked"}; - void processTracked(soa::Join::iterator const& collision, - aod::TraCascDatas const& tracascades) + // for efficiency and purity corrections + TH2F* hEfficiency; + TH2F* hEfficiencyUncertainty; + TH2F* hPurity; + TH2F* hPurityUncertainty; + int mRunNumber; + // loads efficiencies and purities + void initEfficiencyFromCCDB(int64_t runNumber, int64_t timestamp) { - double mult = doProcessPP ? collision.centFT0M() : collision.centFT0C(); - int64_t casccollid = 0; - for (auto const& cascade : tracascades) { - - double dcaxy = cascade.dcaXYCascToPV(); - double dcaz = cascade.dcaZCascToPV(); - if (doApplyCuts && ((dcaxy > CutDCAtoPVxy) || (dcaz > CutDCAtoPVz))) - continue; // DCA check - - if (collision.index() != casccollid) { - histos.fill(HIST("Tracked/EvMult"), mult); // count and list mult of events with at least one cascade - casccollid = collision.index(); - } + if (mRunNumber == runNumber) { + return; + } + mRunNumber = runNumber; + LOG(info) << "Loading efficiencies and purities from CCDB for run " << mRunNumber << " now..."; + auto timeStamp = timestamp; - double pt = cascade.pt(); - double phi = cascade.phi(); - double eta = cascade.eta(); - double massXi = cascade.mXi(); - double massOmega = cascade.mOmega(); + TList* listEfficiencies = ccdb->getForTimeStamp(efficiencyCCDBPath, timeStamp); - histos.fill(HIST("Tracked/DCAxy"), dcaxy); - histos.fill(HIST("Tracked/DCAz"), dcaz); - histos.fill(HIST("Tracked/Phi"), phi); - histos.fill(HIST("Tracked/Eta"), eta); - histos.fill(HIST("Tracked/MassXi"), massXi); - histos.fill(HIST("Tracked/MassOmega"), massOmega); - histos.fill(HIST("Tracked/Xi"), massXi, pt, mult); - histos.fill(HIST("Tracked/Omega"), massOmega, pt, mult); + if (!listEfficiencies) { + LOG(fatal) << "Problem getting TList object with efficiencies and purities!"; } - } - void processAll(soa::Join::iterator const& collision, - aod::CascDatas const& cascades) + hEfficiency = static_cast(listEfficiencies->FindObject("hEfficiency")); + hPurity = static_cast(listEfficiencies->FindObject("hPurity")); + hEfficiencyUncertainty = static_cast(listEfficiencies->FindObject("hEfficiencyUncertainty")); + hPurityUncertainty = static_cast(listEfficiencies->FindObject("hPurityUncertainty")); + if (doPropagateEfficiency && !hEfficiencyUncertainty) + LOG(fatal) << "Problem getting hEfficiencyUncertainty!"; + if (doPropagatePurity && !hPurityUncertainty) + LOG(fatal) << "Problem getting hPurityUncertainty!"; + LOG(info) << "Efficiencies and purities now loaded for " << mRunNumber; + } + // general info about processed events + template + void fillEvents(TEvent const& collision) { histos.fill(HIST("Events/EvCounter"), 0.5); double mult = doProcessPP ? collision.centFT0M() : collision.centFT0C(); @@ -129,38 +164,384 @@ struct StrangeCascTrack { histos.fill(HIST("Events/PVx"), pvx); histos.fill(HIST("Events/PVy"), pvy); histos.fill(HIST("Events/PVz"), pvz); - + } + // checks general selection criteria + template + bool isValidCasc(TCascade cascade) + { + if (cascade.dcaXYCascToPV() > selCuts.cutDCAtoPVxy) + return false; + if (cascade.dcaZCascToPV() > selCuts.cutDCAtoPVz) + return false; + return true; + } + // checks TPC PID of dau tracks + template + bool passesTPC(TCascade cascade) + { + const auto& posDaughterTrackCasc = cascade.template posTrackExtra_as(); + const auto& negDaughterTrackCasc = cascade.template negTrackExtra_as(); + if (cascade.sign() < 0) { + if (std::abs(posDaughterTrackCasc.tpcNSigmaPr()) > selCuts.NSigmaTPCProton) { + return false; + } + if (std::abs(negDaughterTrackCasc.tpcNSigmaPi()) > selCuts.NSigmaTPCPion) { + return false; + } + } else { + if (std::abs(negDaughterTrackCasc.tpcNSigmaPr()) > selCuts.NSigmaTPCProton) { + return false; + } + if (std::abs(posDaughterTrackCasc.tpcNSigmaPi()) > selCuts.NSigmaTPCPion) { + return false; + } + } + return true; + } + // checks TOF PID of dau tracks + // template + // bool passesTOF(TCascade cascade, TString particle) + // { + // return true; + // // const auto& bachDaughterTrackCasc = cascade.bachTrackExtra_as(); + // // const auto& posDaughterTrackCasc = cascade.posTrackExtra_as(); + // // const auto& negDaughterTrackCasc = cascade.negTrackExtra_as(); + // // bool xiPassTOFSelection = true; + // // bool omegaPassTOFSelection = true; + // // if (cascade.sign() < 0) { + // // if (posDaughterTrackCasc.hasTOF()) { + // // if (std::abs(cascade.tofNSigmaXiLaPr()) > selCuts.NSigmaTOFProton) { + // // xiPassTOFSelection &= false; + // // } + // // if (std::abs(cascade.tofNSigmaOmLaPr()) > selCuts.NSigmaTOFProton) { + // // omegaPassTOFSelection &= false; + // // } + // // } + // // if (negDaughterTrackCasc.hasTOF()) { + // // if (std::abs(cascade.tofNSigmaXiLaPi()) > selCuts.NSigmaTOFPion) { + // // xiPassTOFSelection &= false; + // // } + // // if (std::abs(cascade.tofNSigmaOmLaPi()) > selCuts.NSigmaTOFPion) { + // // omegaPassTOFSelection &= false; + // // } + // // } + // // } else { + // // if (posDaughterTrackCasc.hasTOF()) { + // // if (std::abs(cascade.tofNSigmaXiLaPi()) > selCuts.NSigmaTOFPion) { + // // xiPassTOFSelection &= false; + // // } + // // if (std::abs(cascade.tofNSigmaOmLaPi()) > selCuts.NSigmaTOFPion) { + // // omegaPassTOFSelection &= false; + // // } + // // } + // // if (negDaughterTrackCasc.hasTOF()) { + // // if (std::abs(cascade.tofNSigmaXiLaPr()) > selCuts.NSigmaTOFProton) { + // // xiPassTOFSelection &= false; + // // } + // // if (std::abs(cascade.tofNSigmaOmLaPr()) > selCuts.NSigmaTOFProton) { + // // omegaPassTOFSelection &= false; + // // } + // // } + // // } + // + // // if (bachDaughterTrackCasc.hasTOF()) { + // // if (std::abs(cascade.tofNSigmaXiPi()) > selCuts.NSigmaTOFPion) { + // // xiPassTOFSelection &= false; + // // } + // // if (std::abs(cascade.tofNSigmaOmKa()) > selCuts.NSigmaTOFKaon) { + // // omegaPassTOFSelection &= false; + // // } + // // } + // + // // if (bachDaughterTrackCasc.hasTOF()) { + // // if (std::abs(cascade.tofNSigmaXiPi()) > selCuts.NSigmaTOFPion) { + // // xiPassTOFSelection &= false; + // // } + // // if (std::abs(cascade.tofNSigmaOmKa()) > selCuts.NSigmaTOFKaon) { + // // omegaPassTOFSelection &= false; + // // } + // // } + // + // // if (particle == "xi") {return xiPassTOFSelection;} else {return omegaPassTOFSelection;} + // } + // checks whether gen cascade corresponds to PDG code + template + bool isValidPDG(TCascade cascade, TString particle) + { + static constexpr int kPdgCodes[] = {3312, 3334}; // "XiMinus", "OmegaMinus" + if (particle == "xi" && std::abs(cascade.pdgCode()) == kPdgCodes[0]) + return true; + if (particle == "omega" && std::abs(cascade.pdgCode()) == kPdgCodes[1]) + return true; + return false; + } + // checks whether rec cascade is a truth primary xi or omega + template + bool isMCTruth(const TCascade& cascade, TString particle) + { + if constexpr (requires { cascade.has_cascMCCore(); }) { // safety check: discard rec cascade without gen reference + auto cascmccore = cascade.template cascMCCore_as(); + if (!cascmccore.isPhysicalPrimary()) + return false; + int pdg = std::abs(cascmccore.pdgCode()); + if (particle == "xi") + return (pdg == 3312); + if (particle == "omega") + return (pdg == 3334); + } + return false; + } + // applies purities and efficiencies + void fillHist(std::shared_ptr hist, double binFillThn[], float efficiency, float effUncert, float purity, float purityUncert) + { + float previousContent, previousError2, currentContent, currentError2; + int bin = hist->GetBin(binFillThn); + previousContent = hist->GetBinContent(bin); + previousError2 = hist->GetBinError2(bin); + currentContent = previousContent + purity / (efficiency); + currentError2 = previousError2 + std::pow(purity / (efficiency), 2) + std::pow(purityUncert / (efficiency), 2) + std::pow(effUncert * purity, 2) / std::pow(efficiency, 4); + hist->SetBinContent(bin, currentContent); + hist->SetBinError2(bin, currentError2); + } + // applies selections and fills histograms + template + void analyseCascs(TEvent collision, TCascs cascades) + { int64_t casccollid = 0; for (auto const& cascade : cascades) { - double dcaxy = cascade.dcaXYCascToPV(); - double dcaz = cascade.dcaZCascToPV(); - if (doApplyCuts && ((dcaxy > CutDCAtoPVxy) || (dcaz > CutDCAtoPVz))) - continue; // DCA check + if constexpr (requires { cascade.topologyChi2(); }) { + if (!cascade.has_standardCascade()) + continue; // safety check: dismisses tracked cascades without proper reference + } + + // for tracked cascades, make a reference to standard table + auto stdCasc = [&]() { + if constexpr (requires { cascade.topologyChi2(); }) { + if constexpr (requires { collision.straMCCollisionId(); }) { + return cascade.template standardCascade_as(); + } else { + return cascade.template standardCascade_as(); + } + } else { + return cascade; + } + }(); + + // fill cascade statistics without any selections + static constexpr int type = [&]() { + if constexpr (requires { cascade.topologyChi2(); }) { + return 1; + } else { + return 0; + } + }(); + + double mult = doProcessPP ? collision.centFT0M() : collision.centFT0C(); // ion collisions use FT0C for multiplicity, pp uses both + + float efficiency = 1.0f; + float efficiencyError = 0.0f; + float purity = 1.0f; + float purityError = 0.0f; + if (doApplyEfficiency) { + efficiency = hEfficiency->Interpolate(cascade.pt(), mult); + if (doPropagateEfficiency) { + efficiencyError = hEfficiencyUncertainty->Interpolate(cascade.pt(), mult); + } + if (efficiency == 0) { // check for zero efficiency, do not apply if the case + efficiency = 1.; + efficiencyError = 0.; + } + } + if (doApplyPurity) { + purity = hPurity->Interpolate(cascade.pt(), mult); + if (doPropagatePurity) { + purityError = hPurityUncertainty->Interpolate(cascade.pt(), mult); + } + if (purity == 0) { // check for zero purity, do not apply if the case + purity = 1.; + purityError = 0.; + } + } if (collision.index() != casccollid) { - histos.fill(HIST("All/EvMult"), mult); // count and list mult of events with at least one cascade + histos.fill(HIST(kTypeNames[type]) + HIST("/EvMult"), mult); casccollid = collision.index(); } - double pt = cascade.pt(); - double phi = cascade.phi(); - double eta = cascade.eta(); double massXi = cascade.mXi(); double massOmega = cascade.mOmega(); + double pt = cascade.pt(); - histos.fill(HIST("All/DCAxy"), dcaxy); - histos.fill(HIST("All/DCAz"), dcaz); - histos.fill(HIST("All/Phi"), phi); - histos.fill(HIST("All/Eta"), eta); - histos.fill(HIST("All/MassXi"), massXi); - histos.fill(HIST("All/MassOmega"), massOmega); - histos.fill(HIST("All/Xi"), massXi, pt, mult); - histos.fill(HIST("All/Omega"), massOmega, pt, mult); + histos.fill(HIST(kTypeNames[type]) + HIST("/DCAxy"), cascade.dcaXYCascToPV()); + histos.fill(HIST(kTypeNames[type]) + HIST("/DCAz"), cascade.dcaZCascToPV()); + histos.fill(HIST(kTypeNames[type]) + HIST("/Phi"), cascade.phi()); + histos.fill(HIST(kTypeNames[type]) + HIST("/Eta"), cascade.eta()); + histos.fill(HIST(kTypeNames[type]) + HIST("/MassXiNoSel"), massXi); + histos.fill(HIST(kTypeNames[type]) + HIST("/MassOmegaNoSel"), massOmega); + + // start checking selections + bool passedAllSels = true; + // apply general selection criteria + if (doApplyCuts) { + if (isValidCasc(cascade)) { + histos.fill(HIST(kTypeNames[type]) + HIST("/MassXiGenSel"), massXi); + histos.fill(HIST(kTypeNames[type]) + HIST("/MassOmegaGenSel"), massOmega); + } else { + passedAllSels = false; + } + } + // apply tpc pid + if (doApplyTPCPID) { + if (passesTPC(stdCasc)) { + histos.fill(HIST(kTypeNames[type]) + HIST("/MassXiTPCPID"), massXi); + histos.fill(HIST(kTypeNames[type]) + HIST("/MassOmegaTPCPID"), massOmega); + } else { + passedAllSels = false; + } + } + // apply tof pid + bool passedAllSelsXi = passedAllSels; + bool passedAllSelsOmega = passedAllSels; + // if (doApplyTOFPID) { + // if (passesTOF(cascade, "xi")) { + // histos.fill(HIST(kTypeNames[type]) + HIST("/MassXiTOFPID"), massXi); + // } else { + // passedAllSelsXi = false; + // } + // if (passesTOF(cascade, "omega")) { + // histos.fill(HIST(kTypeNames[type]) + HIST("/MassOmegaTOFPID"), massOmega); + // } else { + // passedAllSelsOmega = false; + // } + // } + // apply competing mass rej + if (doCompetingMassRej) { + if (std::abs(massXi - pdgDB->Mass(3312)) > selCuts.compMassRej) { + histos.fill(HIST(kTypeNames[type]) + HIST("/MassOmegaMassSel"), massOmega); + } else { + passedAllSelsOmega = false; + } + } + + // fill w/ cascs that passed all applied sels + double binFillXi[3] = {massXi, pt, mult}; + if (passedAllSelsXi) { + histos.fill(HIST(kTypeNames[type]) + HIST("/MassXi"), massXi); + fillHist(histos.get(HIST(kTypeNames[type]) + HIST("/Xi")), binFillXi, efficiency, efficiencyError, purity, purityError); + if constexpr (requires { collision.straMCCollisionId(); }) { + if (doFillTruth && isMCTruth(stdCasc, "xi")) + histos.fill(HIST("MC/RecTruth/") + HIST(kTypeNames[type]) + HIST("/Xi"), massXi, pt, mult); + } + } + double binFillOmega[3] = {massOmega, pt, mult}; + if (passedAllSelsOmega) { + histos.fill(HIST(kTypeNames[type]) + HIST("/MassOmega"), massOmega); + fillHist(histos.get(HIST(kTypeNames[type]) + HIST("/Omega")), binFillOmega, efficiency, efficiencyError, purity, purityError); + if constexpr (requires { collision.straMCCollisionId(); }) { + if (doFillTruth && isMCTruth(stdCasc, "omega")) + histos.fill(HIST("MC/RecTruth/") + HIST(kTypeNames[type]) + HIST("/Omega"), massOmega, pt, mult); + } + } } } - PROCESS_SWITCH(StrangeCascTrack, processTracked, "process tracked cascades", true); - PROCESS_SWITCH(StrangeCascTrack, processAll, "process all cascades", true); + + void init(InitContext const&) + { + // for all events processing + histos.add("Events/EvCounter", "Event Counter", kTH1F, {{1, 0, 1}}); + histos.add("Events/PVx", "PV x position", kTH1F, {{200, -0.1, 0.1}}); + histos.add("Events/PVy", "PV y position", kTH1F, {{200, -0.1, 0.1}}); + histos.add("Events/PVz", "PV z position", kTH1F, {{100, -20, 20}}); + histos.add("Events/Mult", "Multiplicity", kTH1F, {axesConfig.axisMult}); + // for cascade processing + static_for<0, 1>([&](auto type) { + histos.add(Form("%s/Phi", kTypeNames[type].data()), "Phi", kTH1F, {axesConfig.axisPhi}); + histos.add(Form("%s/Eta", kTypeNames[type].data()), "Eta", kTH1F, {axesConfig.axisEta}); + histos.add(Form("%s/DCAxy", kTypeNames[type].data()), "DCA to xy", kTH1F, {axesConfig.axisDCAxy}); + histos.add(Form("%s/DCAz", kTypeNames[type].data()), "DCA to z", kTH1F, {axesConfig.axisDCAz}); + histos.add(Form("%s/EvMult", kTypeNames[type].data()), "Multiplicity of events with >=1 cascade", kTH1F, {axesConfig.axisMult}); + // no selection applied + histos.add(Form("%s/MassOmegaNoSel", kTypeNames[type].data()), "Invariant mass hypothesis", kTH1F, {axesConfig.axisOmegaMass}); + histos.add(Form("%s/MassXiNoSel", kTypeNames[type].data()), "Invariant mass hypothesis", kTH1F, {axesConfig.axisXiMass}); + // only gen selection applied + histos.add(Form("%s/MassOmegaGenSel", kTypeNames[type].data()), "Invariant mass hypothesis", kTH1F, {axesConfig.axisOmegaMass}); + histos.add(Form("%s/MassXiGenSel", kTypeNames[type].data()), "Invariant mass hypothesis", kTH1F, {axesConfig.axisXiMass}); + // only tpc pid selection applied + histos.add(Form("%s/MassOmegaTPCPID", kTypeNames[type].data()), "Invariant mass hypothesis", kTH1F, {axesConfig.axisOmegaMass}); + histos.add(Form("%s/MassXiTPCPID", kTypeNames[type].data()), "Invariant mass hypothesis", kTH1F, {axesConfig.axisXiMass}); + // only tof pid selection applied + histos.add(Form("%s/MassOmegaTOFPID", kTypeNames[type].data()), "Invariant mass hypothesis", kTH1F, {axesConfig.axisOmegaMass}); + histos.add(Form("%s/MassXiTOFPID", kTypeNames[type].data()), "Invariant mass hypothesis", kTH1F, {axesConfig.axisXiMass}); + // only competing mass rejection selection applied + histos.add(Form("%s/MassOmegaMassSel", kTypeNames[type].data()), "Invariant mass hypothesis", kTH1F, {axesConfig.axisOmegaMass}); + // passed all applied sels + histos.add(Form("%s/MassOmega", kTypeNames[type].data()), "Invariant mass hypothesis", kTH1F, {axesConfig.axisOmegaMass}); + histos.add(Form("%s/MassXi", kTypeNames[type].data()), "Invariant mass hypothesis", kTH1F, {axesConfig.axisXiMass}); + histos.add(Form("%s/Omega", kTypeNames[type].data()), "", kTHnD, {axesConfig.axisOmegaMass, axesConfig.axisPt, axesConfig.axisMult}); + histos.add(Form("%s/Xi", kTypeNames[type].data()), "", kTHnD, {axesConfig.axisXiMass, axesConfig.axisPt, axesConfig.axisMult}); + }); + // for MC-specific processing + histos.add("MC/Gen/EvCounter", "Event Counter", kTH1F, {{1, 0, 1}}); + histos.add("MC/Gen/Xi", "Xi", kTH2F, {axesConfig.axisPt, axesConfig.axisMult}); // generated primary Xis + histos.add("MC/Gen/Omega", "Omega", kTH2F, {axesConfig.axisPt, axesConfig.axisMult}); // generated primary Omegas + histos.add("MC/Rec/EvCounter", "Event Counter", kTH1F, {{1, 0, 1}}); + histos.add("MC/Rec/EvMult", "Multiplicity", kTH1F, {axesConfig.axisMult}); + histos.add("MC/RecTruth/Standard/Omega", "", kTHnD, {axesConfig.axisOmegaMass, axesConfig.axisPt, axesConfig.axisMult}); + histos.add("MC/RecTruth/Standard/Xi", "", kTHnD, {axesConfig.axisXiMass, axesConfig.axisPt, axesConfig.axisMult}); + histos.add("MC/RecTruth/Tracked/Omega", "", kTHnD, {axesConfig.axisOmegaMass, axesConfig.axisPt, axesConfig.axisMult}); + histos.add("MC/RecTruth/Tracked/Xi", "", kTHnD, {axesConfig.axisXiMass, axesConfig.axisPt, axesConfig.axisMult}); + } + + void processDerivedData(DerCollisionWMult const& collision, DerCascDatas const& allCascs, DerTraCascDatas const& traCascs, DauTracks const&) + { + fillEvents(collision); // save info about all processed events + if (doApplyEfficiency) { + initEfficiencyFromCCDB(collision.runNumber(), collision.timestamp()); + } + analyseCascs(collision, allCascs); // process all cascades + analyseCascs(collision, traCascs); // process tracked cascades + } + + void processDerivedMCGen(aod::StraMCCollisions const& genColls, DerMCGenCascades const& genCascs, soa::Join const& recColls) + { + for (auto const& genColl : genColls) { + histos.fill(HIST("MC/Gen/EvCounter"), 0.5); // generated events statistics + // (for efficiency calculation) only take reconstructed events + auto slicedRecColls = recColls.sliceBy(perMcCollision, genColl.globalIndex()); + for (auto const& recColl : slicedRecColls) { + histos.fill(HIST("MC/Rec/EvCounter"), 0.5); + double casc_mult = doProcessPP ? recColl.centFT0M() : recColl.centFT0C(); + histos.fill(HIST("MC/Rec/EvMult"), casc_mult); + int64_t genCollId = recColl.straMCCollisionId(); + for (auto const& casc : genCascs) { + if (casc.straMCCollisionId() != genCollId) + continue; // safety check + if (!casc.isPhysicalPrimary()) + continue; // skip non-primary particles + double casc_pt = std::sqrt(std::pow(casc.pxMC(), 2) + std::pow(casc.pyMC(), 2)); + if (isValidPDG(casc, "xi")) + histos.fill(HIST("MC/Gen/Xi"), casc_pt, casc_mult); + if (isValidPDG(casc, "omega")) + histos.fill(HIST("MC/Gen/Omega"), casc_pt, casc_mult); + } + } + } + } + + void processDerivedMCRec(DerMCRecCollision const& collision, DerMCRecCascDatas const& allCascs, DerMCRecTraCascDatas const& traCascs, DauTracks const&, DerMCGenCascades const&) + { + fillEvents(collision); // save info about all processed events + if (doApplyEfficiency) { + initEfficiencyFromCCDB(collision.runNumber(), collision.timestamp()); + } + analyseCascs(collision, allCascs); // process all cascades + analyseCascs(collision, traCascs); // process tracked cascades + } + + PROCESS_SWITCH(StrangeCascTrack, processDerivedData, "process derived data", true); + PROCESS_SWITCH(StrangeCascTrack, processDerivedMCGen, "process derived generated mc data", false); + PROCESS_SWITCH(StrangeCascTrack, processDerivedMCRec, "process derived reconstructed mc data", false); // mc and data are mutually exclusive! }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc)