diff --git a/PWGCF/Femto/Core/baseSelection.h b/PWGCF/Femto/Core/baseSelection.h index 8ddc97e3da1..30fe70231e5 100644 --- a/PWGCF/Femto/Core/baseSelection.h +++ b/PWGCF/Femto/Core/baseSelection.h @@ -161,13 +161,15 @@ class BaseSelection /// -1 = optional cut, bit is stored in bitmask; /// 0 = cut is disabled, no bit stored; /// 1 = minimal (mandatory) cut, no extra bit stored since only one threshold exists. + /// In pass-through mode, enabled selections (-1, 1) are stored as bits without being required, + /// disabled selections (0) stay disabled so they do not occupy bits. void addSelection(int observableIndex, std::string const& selectionName, int mode) { int selectionMode = mode; - if (mPassThrough) { + if (mPassThrough && mode != 0) { selectionMode = 2; } switch (selectionMode) { diff --git a/PWGCF/Femto/Core/closePairRejection.h b/PWGCF/Femto/Core/closePairRejection.h index b6e42f80408..a8db746c394 100644 --- a/PWGCF/Femto/Core/closePairRejection.h +++ b/PWGCF/Femto/Core/closePairRejection.h @@ -60,10 +60,11 @@ enum CprHist { }; // template configurable group for Cpr -template +// DefaultCutAverage: default of cutAverage (e.g. false for pairs of generated particles, where no detector effects are present) +template struct ConfCpr : o2::framework::ConfigurableGroup { std::string prefix = std::string(Prefix); - o2::framework::Configurable cutAverage{"cutAverage", true, "Apply CPR if the average deta-dphistar is below the configured values"}; + o2::framework::Configurable cutAverage{"cutAverage", DefaultCutAverage, "Apply CPR if the average deta-dphistar is below the configured values"}; o2::framework::Configurable cutAnyRadius{"cutAnyRadius", false, "Apply CPR if the deta-dphistar is below the configured values at any radius"}; o2::framework::Configurable cutElipsoidal{"cutElipsoidal", true, "If true, apply CPR as episoidal cut. If false use rectangluar cut."}; o2::framework::Configurable plotAllRadii{"plotAllRadii", true, "Plot deta-dphi distribution at all radii"}; @@ -100,6 +101,7 @@ constexpr const char PrefixCprV0DaughterV0DaughterNeg[] = "CprV0DaughterV0Daught constexpr const char PrefixCprV0DaughterResoDaughterPos[] = "CprV0DaughterResoDaughterPos"; constexpr const char PrefixCprV0DaughterResoDaughterNeg[] = "CprV0DaughterResoDaughterNeg"; constexpr const char PrefixCprTrackCascadeBachelor[] = "CprTrackCascadeBachelor"; +constexpr const char PrefixCprMcParticleMcParticle[] = "CprMcParticleMcParticle"; // pairs using ConfCprTrackTrack = ConfCpr; @@ -117,6 +119,7 @@ using ConfCprV0DaugherV0DaughterNeg = ConfCpr; using ConfCprV0DaughterResoDaughterPos = ConfCpr; using ConfCprV0DaughterResoDaughterNeg = ConfCpr; using ConfCprTrackCascadeBachelor = ConfCpr; +using ConfCprMcParticleMcParticle = ConfCpr; // generated particles, no cut by default // tpc radii for computing phistar constexpr int Nradii = 9; diff --git a/PWGCF/Femto/Core/collisionBuilder.h b/PWGCF/Femto/Core/collisionBuilder.h index 9ffa9848882..7a425a0c2a2 100644 --- a/PWGCF/Femto/Core/collisionBuilder.h +++ b/PWGCF/Femto/Core/collisionBuilder.h @@ -62,8 +62,9 @@ struct ConfCollisionFilters : o2::framework::ConfigurableGroup { struct ConfCollisionBits : o2::framework::ConfigurableGroup { std::string prefix = std::string("CollisionBits"); - o2::framework::Configurable passThrough{"passThrough", false, "If true, all tracks are passed through. Bits for all selections are stored."}; + o2::framework::Configurable passThrough{"passThrough", false, "If true, all collisions are passed through. Bits for all enabled (non-zero) selection flags are stored, disabled flags (0) are not."}; o2::framework::Configurable sel8{"sel8", 1, "Use sel8 (-1: stored in bitmaks; 0 off; 1 on)"}; + o2::framework::Configurable rctFlags{"rctFlags", 1, "RCT flags ok, checker configured via CollisionRctFlags (-1: stored in bitmaks; 0 off; 1 on)"}; o2::framework::Configurable noSameBunchPileup{"noSameBunchPileup", 0, "Reject collisions in case of pileup with another collision in the same foundBC (-1: stored in bitmaks; 0 off; 1 on)"}; o2::framework::Configurable isVertexItsTpc{"isVertexItsTpc", 0, "At least one ITS-TPC track found for the vertex (-1: stored in bitmaks; 0 off; 1 on)"}; o2::framework::Configurable isGoodZvtxFt0VsPv{"isGoodZvtxFt0VsPv", 0, "small difference between z-vertex from PV and from FT0 (-1: stored in bitmaks; 0 off; 1 on)"}; @@ -81,9 +82,9 @@ struct ConfCollisionBits : o2::framework::ConfigurableGroup { o2::framework::Configurable> sphericityMin{"sphericityMin", {}, "Minimum sphericity"}; o2::framework::Configurable> sphericityMax{"sphericityMax", {}, "Maximum sphericity"}; o2::framework::Configurable> triggers{"triggers", {}, "List of all triggers to be used"}; - o2::framework::Configurable eventPlaneAngleDetector{"eventPlaneAngleDetector", 0, "Detector used to estimate the event plane angle: 0 -> FT0C, 1 -> FT0A"}; - o2::framework::Configurable qvecDetector{"qvecDetector", 0, "Detector used to estimate the Q-vector: 0 -> FT0C, 1 -> FT0A"}; - o2::framework::Configurable qvecHarmonic{"qvecHarmonic", 2, "Harmonic n of the Q-vector and event plane angle Psi_n: 2 -> elliptic, 3 -> triangular"}; + o2::framework::Configurable eventPlaneAngleDetector{"eventPlaneAngleDetector", 0, "Detector used to estimate the event plane angle: 0 -> FT0C, 1 -> FT0A"}; + o2::framework::Configurable qvecDetector{"qvecDetector", 0, "Detector used to estimate the Q-vector: 0 -> FT0C, 1 -> FT0A"}; + o2::framework::Configurable qvecHarmonic{"qvecHarmonic", 2, "Harmonic n of the Q-vector and event plane angle Psi_n: 2 -> elliptic, 3 -> triangular"}; }; struct ConfCcdb : o2::framework::ConfigurableGroup { @@ -96,7 +97,6 @@ struct ConfCcdb : o2::framework::ConfigurableGroup { struct ConfCollisionRctFlags : o2::framework::ConfigurableGroup { std::string prefix = std::string("CollisionRctFlags"); - o2::framework::Configurable useRctFlags{"useRctFlags", true, "Set to true to use RCT flags"}; o2::framework::Configurable label{"label", std::string("CBT_hadronPID"), "Which RCT flag to check"}; o2::framework::Configurable useZdc{"useZdc", false, "Whether to use ZDC (only use for PbPb)"}; o2::framework::Configurable treatLimitedAcceptanceAsBad{"treatLimitedAcceptanceAsBad", false, "Whether to treat limited acceptance as bad or not"}; @@ -120,6 +120,7 @@ struct ConfCollisionSelection : o2::framework::ConfigurableGroup { enum CollisionSels { // collsion selection flags kSel8, ///< Sel8 + kRctFlags, ///< RCT flags ok kNoSameBunchPileUp, ///< Reject collisions in case of pileup with another collision in the same foundBC kIsVertexItsTpc, ///< At least one ITS-TPC track found for the vertex kIsGoodZvtxFt0VsPv, ///< small difference between z-vertex from PV and from FT0 @@ -146,6 +147,7 @@ constexpr char ColSelHistName[] = "hCollisionSelection"; const char colSelsName[] = "Collision Selection Object"; const std::unordered_map collisionSelectionNames = { {kSel8, "Sel8"}, + {kRctFlags, "RCT flags ok"}, {kNoSameBunchPileUp, "No same bunch pileup"}, {kIsVertexItsTpc, "Is vertex ITS TPC"}, {kIsGoodZvtxFt0VsPv, "Is good zvtx FT0 vs PV"}, @@ -176,7 +178,6 @@ enum CollisionFilters { kFilterMagFieldMax, kFilterSphericityMin, kFilterSphericityMax, - kFilterRctFlags, kFilterCollisionFiltersMax }; @@ -191,8 +192,7 @@ const std::unordered_map collisionFilterNames = { {kFilterMagFieldMin, "magFieldMin"}, {kFilterMagFieldMax, "magFieldMax"}, {kFilterSphericityMin, "sphericityMin"}, - {kFilterSphericityMax, "sphericityMax"}, - {kFilterRctFlags, "rctFlagsOkFilter"}}; + {kFilterSphericityMax, "sphericityMax"}}; template class CollisionSelection : public baseselection::BaseSelection @@ -206,7 +206,7 @@ class CollisionSelection : public baseselection::BaseSelection void configure(o2::framework::HistogramRegistry* registry, T1 const& filter, T2 const& config, T3 const& confRct, T4 const& confCcdb) @@ -225,8 +225,8 @@ class CollisionSelection : public baseselection::BaseSelectionaddSelection(kSel8, collisionSelectionNames.at(kSel8), config.sel8.value); + this->addSelection(kRctFlags, collisionSelectionNames.at(kRctFlags), config.rctFlags.value); this->addSelection(kNoSameBunchPileUp, collisionSelectionNames.at(kNoSameBunchPileUp), config.noSameBunchPileup.value); this->addSelection(kIsVertexItsTpc, collisionSelectionNames.at(kIsVertexItsTpc), config.isVertexItsTpc.value); this->addSelection(kIsGoodZvtxFt0VsPv, collisionSelectionNames.at(kIsGoodZvtxFt0VsPv), config.isGoodZvtxFt0VsPv.value); @@ -296,7 +297,6 @@ class CollisionSelection : public baseselection::BaseSelectiontemplate fillFilter(kFilterSphericityMax, p); pass &= p; - p = !mUseRctFlags || mRctFlagsChecker(col); - this->template fillFilter(kFilterRctFlags, p); - pass &= p; - this->template fillFilterSummary(pass); return this->isPassThrough() || pass; @@ -481,6 +477,10 @@ class CollisionSelection : public baseselection::BaseSelectionevaluateObservable(kIsGoodItsLayer3, static_cast(col.selection_bit(o2::aod::evsel::kIsGoodITSLayer3))); this->evaluateObservable(kIsGoodItsLayer0123, static_cast(col.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123))); this->evaluateObservable(kIsGoodItsLayerAll, static_cast(col.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll))); + // checker is only initialized if the rct selection is enabled + if (mUseRctFlags) { + this->evaluateObservable(kRctFlags, static_cast(mRctFlagsChecker(col))); + } this->evaluateObservable(kOccupancyMin, col.trackOccupancyInTimeRange()); this->evaluateObservable(kOccupancyMax, col.trackOccupancyInTimeRange()); @@ -581,7 +581,7 @@ class CollisionSelection : public baseselection::BaseSelection +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::femto::dndetabuilder +{ + +/// general configuration +struct ConfDndeta : o2::framework::ConfigurableGroup { + std::string prefix = std::string("Dndeta"); + o2::framework::Configurable requireInelGt0{"requireInelGt0", false, "Require INEL>0 (reco: at least one PV contributor in |eta|<1; gen: at least one charged primary in |eta|<1)"}; + o2::framework::Configurable doCorrelation{"doCorrelation", false, "Data only: fill multiplicity estimator correlations"}; + o2::framework::Configurable doMcEfficiency{"doMcEfficiency", true, "MC only: fill generated/reconstructed dN/deta for the efficiency"}; + o2::framework::Configurable doMcLoss{"doMcLoss", true, "MC only: fill event and signal loss histograms"}; + o2::framework::Configurable useGenTrigger{"useGenTrigger", false, "MC only: require the trigger also at generator level for the event/signal loss (only if the task supports it)"}; + o2::framework::Configurable multEtaMax{"multEtaMax", 0.5f, "MC only: maximum |eta| of the generated multiplicity estimator used in the event/signal loss histograms"}; + // pT variation of the generated spectrum below the threshold, to estimate the uncertainty of the extrapolation to pT = 0 + // weight = p0 + p1 * pT for pT < ptVariationThreshold, 1 otherwise + o2::framework::Configurable ptVariationThreshold{"ptVariationThreshold", 0.1f, "MC only: generated particles below this pT (GeV/c) are reweighted for the pT variations"}; + o2::framework::Configurable> ptVariationUp{"ptVariationUp", {2., -10.}, "MC only: weight of the upward pT variation, p0 + p1 * pT"}; + o2::framework::Configurable> ptVariationDown{"ptVariationDown", {0.5, 5.}, "MC only: weight of the downward pT variation, p0 + p1 * pT"}; +}; + +/// acceptance of the measurement, applied to reconstructed tracks and generated particles +/// the track quality is selected with the track partitions (bitmask) +struct ConfDndetaAcceptance : o2::framework::ConfigurableGroup { + std::string prefix = std::string("DndetaAcceptance"); + o2::framework::Configurable etaMax{"etaMax", 1.0f, "Maximum |eta| (reconstructed tracks and generated particles). The eta range of the track partitions should be at least as large"}; + // regions with possible ITS acceptance problems, off by default + o2::framework::Configurable applyExtraPhiCut{"applyExtraPhiCut", false, "Reject the phi regions below (reconstructed tracks and generated particles)"}; + o2::framework::Configurable phiGapLow{"phiGapLow", 3.07666f, "Reject phiGapLow < phi < phiGapHigh"}; + o2::framework::Configurable phiGapHigh{"phiGapHigh", 3.12661f, "Reject phiGapLow < phi < phiGapHigh"}; + o2::framework::Configurable phiEdgeLow{"phiEdgeLow", 0.03f, "Reject phi <= phiEdgeLow"}; + o2::framework::Configurable phiEdgeHigh{"phiEdgeHigh", 6.253f, "Reject phi >= phiEdgeHigh"}; +}; + +constexpr double MinAbsChargeInUnitsOfThird = 3.; // TParticlePDG::Charge() is given in units of |e|/3 +constexpr float InelGt0EtaMax = 1.f; // INEL>0 is defined for |eta| < 1 (reco side: multNTracksPVeta1) +constexpr std::size_t NPtVariationParameters = 2; + +class DndetaBuilder +{ + public: + DndetaBuilder() = default; + ~DndetaBuilder() = default; + + template + void init(o2::framework::HistogramRegistry* registry, + T1 const& confDndeta, + T2 const& confAcceptance, + T3 const& confCollisionSelection, + T4 const& confTracksGlobal, + T5 const& confTrackBinningGlobal, + T6 const& confTrackQaBinningGlobal, + T7 const& confTracksItsOnly, + T8 const& confTrackBinningItsOnly, + T9 const& confTrackQaBinningItsOnly, + T10 const& confBinning, + bool isMc, + bool doStrangeness) + { + // general + mRequireInelGt0 = confDndeta.requireInelGt0.value; + mDoCorrelation = confDndeta.doCorrelation.value; + mDoMcEfficiency = confDndeta.doMcEfficiency.value; + mDoMcLoss = confDndeta.doMcLoss.value; + mUseGenTrigger = confDndeta.useGenTrigger.value; + mMultEtaMax = confDndeta.multEtaMax.value; + mPtVariationThreshold = confDndeta.ptVariationThreshold.value; + mPtVariationUp = confDndeta.ptVariationUp.value; + mPtVariationDown = confDndeta.ptVariationDown.value; + if (mPtVariationUp.size() != NPtVariationParameters || mPtVariationDown.size() != NPtVariationParameters) { + LOG(fatal) << "Dndeta.ptVariationUp and Dndeta.ptVariationDown need exactly " << NPtVariationParameters << " parameters (p0 + p1 * pT). Breaking..."; + } + + // collision selection + mVtxZMin = confCollisionSelection.vtxZMin.value; + mVtxZMax = confCollisionSelection.vtxZMax.value; + mMultMin = confCollisionSelection.multMin.value; + mMultMax = confCollisionSelection.multMax.value; + mCentMin = confCollisionSelection.centMin.value; + mCentMax = confCollisionSelection.centMax.value; + mMagFieldMin = confCollisionSelection.magFieldMin.value; + mMagFieldMax = confCollisionSelection.magFieldMax.value; + mCollisionMask = confCollisionSelection.collisionMask.value; + + // acceptance + mEtaMax = confAcceptance.etaMax.value; + mApplyExtraPhiCut = confAcceptance.applyExtraPhiCut.value; + mPhiGapLow = confAcceptance.phiGapLow.value; + mPhiGapHigh = confAcceptance.phiGapHigh.value; + mPhiEdgeLow = confAcceptance.phiEdgeLow.value; + mPhiEdgeHigh = confAcceptance.phiEdgeHigh.value; + + // the dndeta track selections reuse the standard track selection object, check that it is configured for charged particle counting + checkTrackSelection(confTracksGlobal); + checkTrackSelection(confTracksItsOnly); + + // track QA (analysis + QA histograms, also in mc; the mc track classes are part of the dndeta histograms, + // the mc histograms of the track histogram manager assume a single particle species) + mTrackHistManagerGlobal.template init(registry, trackhistmanager::makeTrackQaHistSpecMap(confTrackBinningGlobal, confTrackQaBinningGlobal), confTracksGlobal, confTrackQaBinningGlobal); + mTrackHistManagerItsOnly.template init(registry, trackhistmanager::makeTrackQaHistSpecMap(confTrackBinningItsOnly, confTrackQaBinningItsOnly), confTracksItsOnly, confTrackQaBinningItsOnly); + + // histograms + uint32_t blocks = dndetahistmanager::kBlockEvent; + if (isMc) { + if (mDoMcEfficiency) { + blocks |= dndetahistmanager::kBlockMcEfficiency; + } + if (mDoMcLoss) { + blocks |= dndetahistmanager::kBlockLoss; + } + } else { + blocks |= dndetahistmanager::kBlockData; + if (mDoCorrelation) { + blocks |= dndetahistmanager::kBlockCorrelation; + } + if (doStrangeness) { + blocks |= dndetahistmanager::kBlockStrangeness; + } + } + mHistManager.init(registry, dndetahistmanager::makeDndetaHistSpecMap(confBinning), blocks); + } + + [[nodiscard]] bool useGenTrigger() const { return mUseGenTrigger; } + + // --------------------------------------------------------------------------- + // data + // --------------------------------------------------------------------------- + + /// \return true if the collision was selected (including the trigger) + template + bool processData(T1 const& col, T2& partitionGlobal, T3& partitionItsOnly, T4& cache, T5 const& trigger) + { + if (!selectCollision(col, trigger)) { + return false; + } + const float posZ = col.posZ(); + const float cent = col.cent(); + mHistManager.fillEvent(posZ, cent); + + auto tracksGlobal = partitionGlobal->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto tracksItsOnly = partitionItsOnly->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + + int nch = 0; + for (auto const& track : tracksGlobal) { + if (!isTrackAccepted(track, true)) { + continue; + } + nch++; + mTrackHistManagerGlobal.template fill(track, tracksGlobal); + mHistManager.fillDataTrack(posZ, cent, track.eta(), track.phi(), true); + } + for (auto const& track : tracksItsOnly) { + if (!isTrackAccepted(track, false)) { + continue; + } + nch++; + mTrackHistManagerItsOnly.template fill(track, tracksItsOnly); + mHistManager.fillDataTrack(posZ, cent, track.eta(), track.phi(), false); + } + if (mDoCorrelation) { + mHistManager.fillCorrelation(static_cast(nch), col.mult(), col.multFT0A(), col.multFT0C()); + } + return true; + } + + /// fill strangeness yields of a selected collision; lambdas and k0shorts are the already selected slices of this collision + template + void processStrangeness(T1 const& col, T2 const& lambdas, T3 const& k0shorts) + { + const float cent = col.cent(); + mHistManager.fillStrangenessCollision(col.posZ(), cent); + for (auto const& lambda : lambdas) { + mHistManager.fillLambda(cent, lambda.eta(), lambda.mass(), lambda.sign() < 0); + } + for (auto const& k0short : k0shorts) { + mHistManager.fillK0short(cent, k0short.eta(), k0short.mass()); + } + } + + // --------------------------------------------------------------------------- + // mc + // --------------------------------------------------------------------------- + + /// timeframe-level mc processing + /// - reco: for every mc collision only the reconstructed collision with the most PV contributors is used + /// (emulates bestCollisionIndex); it has to pass the full event selection including the reco trigger + /// - gen: efficiency histograms for all generated collisions, loss histograms for the generated event class + /// (vertex z, optional INEL>0, optional generator-level trigger) + template + void processMc(T1 const& cols, + T2 const& mcCols, + T3& partitionGlobal, + T4& partitionItsOnly, + T5& cache, + T6 const& mcParticles, + T7 const& mcMothers, + T8 const& perMcColParticles, + T9& pdg, + T10 const& recoTrigger, + T11 const& genTrigger) + { + const auto nMcCols = static_cast(mcCols.size()); + + // best reconstructed collision per mc collision (largest number of PV contributors, as in mcCollisionExtra) + std::vector bestReco(nMcCols, -1); + std::vector bestNumContrib(nMcCols, -1); + for (auto const& col : cols) { + if (!col.has_fMcCol()) { + continue; + } + const auto mcColIndex = static_cast(col.fMcColId()); + if (col.numContrib() > bestNumContrib[mcColIndex]) { + bestNumContrib[mcColIndex] = col.numContrib(); + bestReco[mcColIndex] = col.globalIndex(); + } + } + + // reconstructed side + std::vector selectedReco(nMcCols, -1); + for (auto const& col : cols) { + if (!col.has_fMcCol() || bestReco[static_cast(col.fMcColId())] != col.globalIndex()) { + continue; + } + if (!selectCollision(col, [&]() { + return recoTrigger(col); + })) { + continue; + } + selectedReco[static_cast(col.fMcColId())] = col.globalIndex(); + if (mDoMcEfficiency) { + auto tracksGlobal = partitionGlobal->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto tracksItsOnly = partitionItsOnly->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + fillRecoMc(col, tracksGlobal, tracksItsOnly, mcParticles, mcMothers); + } + } + + // generated side + for (auto const& mcCol : mcCols) { + const auto mcColIndex = static_cast(mcCol.globalIndex()); + const bool hasSelectedReco = selectedReco[mcColIndex] >= 0; + auto particles = mcParticles.sliceBy(perMcColParticles, mcCol.globalIndex()); + + // efficiency: centrality of the selected reconstructed collision (underflow if there is none) + if (mDoMcEfficiency) { + const float recoCent = hasSelectedReco ? cols.rawIteratorAt(selectedReco[mcColIndex]).cent() : -1.f; + mHistManager.fillGenCollision(mcCol.posZ(), recoCent, hasSelectedReco); + for (auto const& particle : particles) { + if (!isGenParticleSelected(particle, pdg)) { + continue; + } + mHistManager.fillGenParticle(mcCol.posZ(), recoCent, particle.eta(), particle.phi(), genType(particle.pdgCode()), hasSelectedReco, + ptVariationWeight(particle.pt(), mPtVariationUp), ptVariationWeight(particle.pt(), mPtVariationDown)); + } + } + + // event and signal loss: generated event class, true centrality + if (!mDoMcLoss) { + continue; + } + if (mcCol.posZ() < mVtxZMin || mcCol.posZ() > mVtxZMax) { + continue; + } + int nChMult = 0; + int nChEta1 = 0; + for (auto const& particle : particles) { + if (!isPhysicalPrimary(particle) || std::abs(particle.eta()) >= InelGt0EtaMax || !isCharged(particle.pdgCode(), pdg)) { + continue; + } + nChEta1++; + if (std::abs(particle.eta()) < mMultEtaMax) { + nChMult++; + } + } + if (mRequireInelGt0 && nChEta1 == 0) { + continue; + } + if (mUseGenTrigger && !genTrigger(mcCol)) { + continue; + } + const auto mult = static_cast(nChMult); + mHistManager.fillLossEvent(mcCol.cent(), mult, hasSelectedReco); + for (auto const& particle : particles) { + if (!isGenParticleSelected(particle, pdg)) { + continue; + } + mHistManager.fillLossParticle(particle.eta(), mcCol.cent(), mult, hasSelectedReco); + } + } + } + + private: + /// warn about settings of the standard track selection object that do not make sense for charged particle counting + template + static void checkTrackSelection(T const& conf) + { + const std::string name = conf.prefix; + if (conf.chargeSign.value != 0) { + LOG(warn) << name << ".chargeSign is " << conf.chargeSign.value << ", only one charge sign is counted. Set it to 0 for dN/deta."; + } + if (conf.maskLowMomentum.value != conf.maskHighMomentum.value) { + LOG(warn) << name << ": maskLowMomentum and maskHighMomentum differ, the track selection depends on pidThres. Set both to the same track quality mask for dN/deta."; + } + if (conf.rejectionMaskLowMomentum.value != 0 || conf.rejectionMaskHighMomentum.value != 0) { + LOG(warn) << name << ": rejection masks are set, tracks are rejected based on PID. Set them to 0 for dN/deta."; + } + LOG(info) << name << ": pT range " << conf.ptMin.value << " - " << conf.ptMax.value << " GeV/c, eta range " << conf.etaMin.value << " - " << conf.etaMax.value; + } + + /// event selection with cutflow; the trigger is evaluated last, so the trigger QA + /// only contains events that passed all other event selections + template + bool selectCollision(T1 const& col, T2 const& trigger) + { + mHistManager.fillEventCutflow(dndetahistmanager::kEventAll); + if (col.posZ() < mVtxZMin || col.posZ() > mVtxZMax) { + return false; + } + mHistManager.fillEventCutflow(dndetahistmanager::kEventVtxZ); + if (col.mult() < mMultMin || col.mult() > mMultMax || + col.cent() < mCentMin || col.cent() > mCentMax || + col.magField() < mMagFieldMin || col.magField() > mMagFieldMax || + (col.mask() & mCollisionMask) != mCollisionMask) { + return false; + } + mHistManager.fillEventCutflow(dndetahistmanager::kEventCollisionSelection); + if (mRequireInelGt0 && col.multNTracksPVeta1() <= 0) { + return false; + } + mHistManager.fillEventCutflow(dndetahistmanager::kEventInelGt0); + if (!trigger()) { + return false; + } + mHistManager.fillEventCutflow(dndetahistmanager::kEventTrigger); + return true; + } + + /// weight of a pT variation, p[0] + p[1] * pT below the threshold, 1 above + [[nodiscard]] double ptVariationWeight(float pt, std::vector const& p) const + { + if (pt >= mPtVariationThreshold) { + return 1.; + } + return p[0] + p[1] * pt; + } + + [[nodiscard]] bool isInPhiGap(float phi) const + { + if (!mApplyExtraPhiCut) { + return false; + } + return (phi > mPhiGapLow && phi < mPhiGapHigh) || phi <= mPhiEdgeLow || phi >= mPhiEdgeHigh; + } + + /// track quality is already selected by the partition + /// \param fromTpcPartition true if the track comes from the partition with TPC quality bits + template + bool isTrackAccepted(T const& track, bool fromTpcPartition) const + { + // rows only stored to resolve a daughter index (e.g. V0 daughter from another collision) are no tracks of this collision + if (track.isDaughterOnly()) { + return false; + } + // tracks with TPC are only taken from the TPC partition and tracks without TPC only from the other one, + // so the TPC cuts apply to every track with TPC and no track is counted twice + if (track.hasTpc() != fromTpcPartition) { + return false; + } + return std::abs(track.eta()) < mEtaMax && !isInPhiGap(track.phi()); + } + + template + static bool isPhysicalPrimary(T const& particle) + { + return particle.origin() == static_cast(modes::McOrigin::kPhysicalPrimary); + } + + template + static bool isCharged(int pdgCode, T& pdg) + { + const auto* particle = pdg->GetParticle(pdgCode); + return particle != nullptr && std::abs(particle->Charge()) >= MinAbsChargeInUnitsOfThird; + } + + /// generated charged physical primary inside the acceptance + template + bool isGenParticleSelected(T1 const& particle, T2& pdg) const + { + if (!isPhysicalPrimary(particle)) { + return false; + } + if (std::abs(particle.eta()) >= mEtaMax || isInPhiGap(particle.phi())) { + return false; + } + return isCharged(particle.pdgCode(), pdg); + } + + static dndetahistmanager::GenType genType(int pdgCode) + { + switch (std::abs(pdgCode)) { + case PDG_t::kPiPlus: + return dndetahistmanager::kGenPion; + case PDG_t::kKPlus: + return dndetahistmanager::kGenKaon; + case PDG_t::kProton: + return dndetahistmanager::kGenProton; + default: + return dndetahistmanager::kGenOther; + } + } + + static dndetahistmanager::RecoType recoTypeFromSpecies(int pdgCode) + { + switch (std::abs(pdgCode)) { + case PDG_t::kPiPlus: + return dndetahistmanager::kRecoPion; + case PDG_t::kKPlus: + return dndetahistmanager::kRecoKaon; + case PDG_t::kProton: + return dndetahistmanager::kRecoProton; + default: + return dndetahistmanager::kRecoOther; + } + } + + template + void fillRecoMcTrack(T1 const& col, T2 const& track, T3 const& /*mcParticles*/, T4 const& /*mcMothers*/, std::vector& usedLabels) + { + const float posZ = col.posZ(); + const float cent = col.cent(); + if (!track.has_fMcParticle()) { + mHistManager.fillRecoTrack(posZ, cent, track.eta(), track.phi(), dndetahistmanager::kRecoBkg); + return; + } + auto mcParticle = track.template fMcParticle_as(); + // tracks matched to a particle from another mc collision are skipped (as in the legacy task) + if (mcParticle.fMcColId() != col.fMcColId()) { + return; + } + mHistManager.fillRecoTrack(posZ, cent, track.eta(), track.phi(), dndetahistmanager::kRecoAll); + + dndetahistmanager::RecoType type = dndetahistmanager::kRecoSecondary; + if (isPhysicalPrimary(mcParticle)) { + type = recoTypeFromSpecies(mcParticle.pdgCode()); + } + if (mcParticle.has_fMcMother()) { + const int motherPdg = mcParticle.template fMcMother_as().pdgCode(); + if (motherPdg == PDG_t::kK0Short || std::abs(motherPdg) == PDG_t::kLambda0) { + type = dndetahistmanager::kRecoWeakDecay; + } + } + if (std::find(usedLabels.begin(), usedLabels.end(), track.fMcParticleId()) != usedLabels.end()) { + type = dndetahistmanager::kRecoFake; + } + usedLabels.push_back(track.fMcParticleId()); + mHistManager.fillRecoTrack(posZ, cent, mcParticle.eta(), mcParticle.phi(), type); + } + + template + void fillRecoMc(T1 const& col, T2 const& tracksGlobal, T3 const& tracksItsOnly, T4 const& mcParticles, T5 const& mcMothers) + { + mHistManager.fillRecoCollision(col.posZ(), col.cent()); + // labels are tracked over both partitions, so a fake is also found if the other track comes from the other partition + std::vector usedLabels; + for (auto const& track : tracksGlobal) { + if (isTrackAccepted(track, true)) { + mTrackHistManagerGlobal.template fill(track, tracksGlobal); + fillRecoMcTrack(col, track, mcParticles, mcMothers, usedLabels); + } + } + for (auto const& track : tracksItsOnly) { + if (isTrackAccepted(track, false)) { + mTrackHistManagerItsOnly.template fill(track, tracksItsOnly); + fillRecoMcTrack(col, track, mcParticles, mcMothers, usedLabels); + } + } + } + + dndetahistmanager::DndetaHistManager mHistManager; + trackhistmanager::TrackHistManager mTrackHistManagerGlobal; + trackhistmanager::TrackHistManager mTrackHistManagerItsOnly; + + // general + bool mRequireInelGt0 = false; + bool mDoCorrelation = false; + bool mDoMcEfficiency = true; + bool mDoMcLoss = true; + bool mUseGenTrigger = false; + float mMultEtaMax = 0.f; + float mPtVariationThreshold = 0.f; + std::vector mPtVariationUp; + std::vector mPtVariationDown; + + // collision selection + float mVtxZMin = 0.f; + float mVtxZMax = 0.f; + float mMultMin = 0.f; + float mMultMax = 0.f; + float mCentMin = 0.f; + float mCentMax = 0.f; + int mMagFieldMin = 0; + int mMagFieldMax = 0; + datatypes::CollisionMaskType mCollisionMask = 0; + + // acceptance + float mEtaMax = 0.f; + bool mApplyExtraPhiCut = false; + float mPhiGapLow = 0.f; + float mPhiGapHigh = 0.f; + float mPhiEdgeLow = 0.f; + float mPhiEdgeHigh = 0.f; +}; + +} // namespace o2::analysis::femto::dndetabuilder + +#endif // PWGCF_FEMTO_CORE_DNDETABUILDER_H_ diff --git a/PWGCF/Femto/Core/dndetaHistManager.h b/PWGCF/Femto/Core/dndetaHistManager.h new file mode 100644 index 00000000000..ed4f425aed6 --- /dev/null +++ b/PWGCF/Femto/Core/dndetaHistManager.h @@ -0,0 +1,567 @@ +// Copyright 2019-2022 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file dndetaHistManager.h +/// \brief histogram manager for charged-particle pseudorapidity density measurements on femto derived data +/// \author anton.riedel@tum.de, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_DNDETAHISTMANAGER_H_ +#define PWGCF_FEMTO_CORE_DNDETAHISTMANAGER_H_ + +#include "PWGCF/Femto/Core/histManager.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::femto::dndetahistmanager +{ + +// --------------------------------------------------------------------------- +// categories used as histogram axes, with labels +// --------------------------------------------------------------------------- + +/// event selection steps, used for the cutflow histogram +enum EventCut : int { + kEventAll = 0, + kEventVtxZ, + kEventCollisionSelection, ///< multiplicity/centrality/magnetic field ranges and collision bitmask + kEventInelGt0, + kEventTrigger, ///< pair/triplet trigger (always passed for minimum bias) + kEventCutLast +}; +constexpr std::array EventCutNames = {"All", "Vertex z", "Collision selection", "INEL>0", "Trigger"}; + +/// reconstructed track types in data +enum DataTrackType : int { + kGlobalPlusIts = 0, ///< all selected tracks + kGlobalOnly, ///< tracks with TPC + kItsOnly, ///< tracks without TPC + kDataTrackTypeLast +}; +constexpr std::array DataTrackTypeNames = {"Global+ITS", "Global", "ITS only"}; + +/// generated particle species +enum GenType : int { + kGenAll = 0, + kGenPion, + kGenKaon, + kGenProton, + kGenOther, + kGenTypeLast +}; +constexpr std::array GenTypeNames = {"All", "Pion", "Kaon", "Proton", "Other"}; + +/// variations of the generated pT spectrum below the extrapolation threshold +enum GenPtVariation : int { + kPtNominal = 0, + kPtUp, + kPtDown, + kGenPtVariationLast +}; +constexpr std::array GenPtVariationNames = {"Nominal", "p_{T} up", "p_{T} down"}; + +/// classification of reconstructed tracks in MC +enum RecoType : int { + kRecoAll = 0, + kRecoPion, + kRecoKaon, + kRecoProton, + kRecoOther, + kRecoSecondary, + kRecoWeakDecay, + kRecoFake, + kRecoBkg, + kRecoTypeLast +}; +constexpr std::array RecoTypeNames = {"All", "Pion", "Kaon", "Proton", "Other", "Secondary", "Weak decay", "Fake", "No MC particle"}; + +/// generated event classes for event/signal loss +enum LossEvent : int { + kLossGenAll = 0, + kLossGenWithReco, + kLossEventLast +}; +constexpr std::array LossEventNames = {"Generated", "Generated with selected reco"}; + +// --------------------------------------------------------------------------- +// histograms +// --------------------------------------------------------------------------- + +enum DndetaHist { + // event + kEventCutflow, + kPosZ, + kCent, + kPosZVsCent, + // data + kDataDndeta, + kDataDndetaMb, + // mc: generated + kGenPosZ, + kGenPosZVsCent, + kGenAssocRecoPosZ, + kGenAssocRecoPosZVsCent, + kGenDndeta, + kGenAssocRecoDndeta, + kGenAssocRecoDndetaMb, + // mc: reconstructed + kRecoPosZ, + kRecoCent, + kRecoPosZVsCent, + kRecoDndeta, + kRecoDndetaMb, + // mc: event and signal loss + kLossEvents, + kLossCentGen, + kLossCentGenAssocReco, + kLossMultGen, + kLossMultGenAssocReco, + kLossEtaGen, + kLossEtaGenAssocReco, + kLossEtaVsCentGen, + kLossEtaVsCentGenAssocReco, + kLossEtaVsMultGen, + kLossEtaVsMultGenAssocReco, + // multiplicity correlations + kCorrNchVsFt0a, + kCorrNchVsFt0c, + kCorrNchVsNpv, + kCorrNpvVsFt0c, + // strangeness + kStrangePosZVsCent, + kK0shortCentEtaMass, + kLambdaCentEtaMass, + kAntiLambdaCentEtaMass, + kDndetaHistLast +}; + +constexpr std::string_view EventDir = "Dndeta/Event/"; +constexpr std::string_view DataDir = "Dndeta/Data/"; +constexpr std::string_view GenDir = "Dndeta/Gen/"; +constexpr std::string_view RecoDir = "Dndeta/Reco/"; +constexpr std::string_view LossDir = "Dndeta/Loss/"; +constexpr std::string_view CorrelationDir = "Dndeta/Correlation/"; +constexpr std::string_view StrangenessDir = "Dndeta/Strangeness/"; + +constexpr std::array, kDndetaHistLast> HistTable = { + { + // event + {kEventCutflow, o2::framework::HistType::kTH1F, "hEventCutflow", "Event selection; ; Entries"}, + {kPosZ, o2::framework::HistType::kTH1F, "hPosZ", "Vertex z; V_{Z} (cm); Entries"}, + {kCent, o2::framework::HistType::kTH1F, "hCent", "Centrality; Centrality (%); Entries"}, + {kPosZVsCent, o2::framework::HistType::kTH2F, "hPosZVsCent", "Vertex z vs centrality; V_{Z} (cm); Centrality (%)"}, + // data + {kDataDndeta, o2::framework::HistType::kTHnSparseF, "hDndeta", "Tracks; V_{Z} (cm); Centrality (%); #eta; #varphi; Track type"}, + {kDataDndetaMb, o2::framework::HistType::kTHnSparseF, "hDndetaMb", "Tracks (MB); V_{Z} (cm); #eta; #varphi"}, + // mc: generated + {kGenPosZ, o2::framework::HistType::kTH1F, "hPosZ", "Generated vertex z; V_{Z,gen} (cm); Entries"}, + {kGenPosZVsCent, o2::framework::HistType::kTH2F, "hPosZVsCent", "Generated vertex z vs centrality; V_{Z,gen} (cm); Centrality_{gen} (%)"}, + {kGenAssocRecoPosZ, o2::framework::HistType::kTH1F, "hAssocRecoPosZ", "Generated vertex z (with selected reco); V_{Z,gen} (cm); Entries"}, + {kGenAssocRecoPosZVsCent, o2::framework::HistType::kTH2F, "hAssocRecoPosZVsCent", "Generated vertex z vs centrality (with selected reco); V_{Z,gen} (cm); Centrality_{gen} (%)"}, + {kGenDndeta, o2::framework::HistType::kTHnSparseF, "hDndeta", "Generated primaries; V_{Z,gen} (cm); Centrality_{gen} (%); #eta; #varphi"}, + {kGenAssocRecoDndeta, o2::framework::HistType::kTHnSparseF, "hAssocRecoDndeta", "Generated primaries (with selected reco); V_{Z,gen} (cm); Centrality_{gen} (%); #eta; #varphi; Species; p_{T} variation"}, + {kGenAssocRecoDndetaMb, o2::framework::HistType::kTHnSparseF, "hAssocRecoDndetaMb", "Generated primaries (with selected reco, MB); V_{Z,gen} (cm); #eta; #varphi; Species"}, + // mc: reconstructed + {kRecoPosZ, o2::framework::HistType::kTH1F, "hPosZ", "Reconstructed vertex z; V_{Z} (cm); Entries"}, + {kRecoCent, o2::framework::HistType::kTH1F, "hCent", "Reconstructed centrality; Centrality (%); Entries"}, + {kRecoPosZVsCent, o2::framework::HistType::kTH2F, "hPosZVsCent", "Reconstructed vertex z vs centrality; V_{Z} (cm); Centrality (%)"}, + {kRecoDndeta, o2::framework::HistType::kTHnSparseF, "hDndeta", "Reconstructed tracks; V_{Z} (cm); Centrality (%); #eta; #varphi; Track class"}, + {kRecoDndetaMb, o2::framework::HistType::kTHnSparseF, "hDndetaMb", "Reconstructed tracks (MB); V_{Z} (cm); #eta; #varphi; Track class"}, + // mc: event and signal loss + {kLossEvents, o2::framework::HistType::kTH1F, "hEvents", "Generated events; ; Entries"}, + {kLossCentGen, o2::framework::HistType::kTH1F, "hCentGen", "Generated events; Centrality_{gen} (%); Entries"}, + {kLossCentGenAssocReco, o2::framework::HistType::kTH1F, "hCentGenAssocReco", "Generated events with selected reco; Centrality_{gen} (%); Entries"}, + {kLossMultGen, o2::framework::HistType::kTH1F, "hMultGen", "Generated events; N_{ch,gen}; Entries"}, + {kLossMultGenAssocReco, o2::framework::HistType::kTH1F, "hMultGenAssocReco", "Generated events with selected reco; N_{ch,gen}; Entries"}, + {kLossEtaGen, o2::framework::HistType::kTH1F, "hEtaGen", "Generated primaries; #eta; Entries"}, + {kLossEtaGenAssocReco, o2::framework::HistType::kTH1F, "hEtaGenAssocReco", "Generated primaries in events with selected reco; #eta; Entries"}, + {kLossEtaVsCentGen, o2::framework::HistType::kTH2F, "hEtaVsCentGen", "Generated primaries; #eta; Centrality_{gen} (%)"}, + {kLossEtaVsCentGenAssocReco, o2::framework::HistType::kTH2F, "hEtaVsCentGenAssocReco", "Generated primaries in events with selected reco; #eta; Centrality_{gen} (%)"}, + {kLossEtaVsMultGen, o2::framework::HistType::kTH2F, "hEtaVsMultGen", "Generated primaries; #eta; N_{ch,gen}"}, + {kLossEtaVsMultGenAssocReco, o2::framework::HistType::kTH2F, "hEtaVsMultGenAssocReco", "Generated primaries in events with selected reco; #eta; N_{ch,gen}"}, + // multiplicity correlations + {kCorrNchVsFt0a, o2::framework::HistType::kTH2F, "hNchVsFt0a", "N_{ch} vs FT0A; N_{ch}; FT0A amplitude"}, + {kCorrNchVsFt0c, o2::framework::HistType::kTH2F, "hNchVsFt0c", "N_{ch} vs FT0C; N_{ch}; FT0C amplitude"}, + {kCorrNchVsNpv, o2::framework::HistType::kTH2F, "hNchVsNpv", "N_{ch} vs N_{PV}; N_{ch}; N_{PV contributors}"}, + {kCorrNpvVsFt0c, o2::framework::HistType::kTH2F, "hNpvVsFt0c", "N_{PV} vs FT0C; N_{PV contributors}; FT0C amplitude"}, + // strangeness + {kStrangePosZVsCent, o2::framework::HistType::kTH2F, "hPosZVsCent", "Vertex z vs centrality; V_{Z} (cm); Centrality (%)"}, + {kK0shortCentEtaMass, o2::framework::HistType::kTH3F, "hK0shortCentEtaMass", "K^{0}_{S}; Centrality (%); #eta; m_{#pi#pi} (GeV/#it{c}^{2})"}, + {kLambdaCentEtaMass, o2::framework::HistType::kTH3F, "hLambdaCentEtaMass", "#Lambda; Centrality (%); #eta; m_{p#pi} (GeV/#it{c}^{2})"}, + {kAntiLambdaCentEtaMass, o2::framework::HistType::kTH3F, "hAntiLambdaCentEtaMass", "#bar{#Lambda}; Centrality (%); #eta; m_{p#pi} (GeV/#it{c}^{2})"}, + }}; + +// --------------------------------------------------------------------------- +// binning +// --------------------------------------------------------------------------- + +struct ConfDndetaBinning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("DndetaBinning"); + o2::framework::ConfigurableAxis vtxZ{"vtxZ", {40, -20, 20}, "Vertex z binning"}; + o2::framework::ConfigurableAxis cent{"cent", {o2::framework::VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, "Centrality binning for the dN/deta histograms"}; + o2::framework::ConfigurableAxis centFine{"centFine", {100, 0, 100}, "Centrality binning for event and loss histograms"}; + o2::framework::ConfigurableAxis eta{"eta", {40, -2, 2}, "Pseudorapidity binning"}; + o2::framework::ConfigurableAxis phi{"phi", {o2::framework::VARIABLE_WIDTH, 0, o2::constants::math::PIQuarter, o2::constants::math::PIHalf, o2::constants::math::PIQuarter * 3., o2::constants::math::PI, o2::constants::math::PIQuarter * 5., o2::constants::math::PIHalf * 3., o2::constants::math::PIQuarter * 7., o2::constants::math::TwoPI}, "Azimuth binning for the dN/deta histograms"}; + o2::framework::ConfigurableAxis multGen{"multGen", {500, 0, 500}, "Binning of the generated multiplicity estimator of the loss histograms (eta range: Dndeta.multEtaMax)"}; + o2::framework::ConfigurableAxis nch{"nch", {501, -0.5, 500.5}, "Number of selected tracks binning (correlations)"}; + o2::framework::ConfigurableAxis npv{"npv", {501, -0.5, 500.5}, "Number of PV contributors binning (correlations)"}; + o2::framework::ConfigurableAxis ft0a{"ft0a", {501, -0.5, 500.5}, "FT0A amplitude binning (correlations)"}; + o2::framework::ConfigurableAxis ft0c{"ft0c", {501, -0.5, 500.5}, "FT0C amplitude binning (correlations)"}; + o2::framework::ConfigurableAxis massK0short{"massK0short", {200, 0.4, 0.6}, "K0short mass binning"}; + o2::framework::ConfigurableAxis massLambda{"massLambda", {200, 1.07, 1.17}, "Lambda mass binning"}; +}; + +namespace detail +{ +/// one bin per category, centered on the integer value of the enum; labels are set after booking +template +o2::framework::AxisSpec makeCategoryAxis(std::array const& /*names*/, const char* title) +{ + return {static_cast(N), -0.5, static_cast(N) - 0.5, title}; +} +} // namespace detail + +template +auto makeDndetaHistSpecMap(T const& conf) +{ + const o2::framework::AxisSpec axisTrackType = detail::makeCategoryAxis(DataTrackTypeNames, "Track type"); + const o2::framework::AxisSpec axisGenType = detail::makeCategoryAxis(GenTypeNames, "Species"); + const o2::framework::AxisSpec axisPtVariation = detail::makeCategoryAxis(GenPtVariationNames, "p_{T} variation"); + const o2::framework::AxisSpec axisRecoType = detail::makeCategoryAxis(RecoTypeNames, "Track class"); + const o2::framework::AxisSpec axisEventCut = detail::makeCategoryAxis(EventCutNames, ""); + const o2::framework::AxisSpec axisLossEvent = detail::makeCategoryAxis(LossEventNames, ""); + + return std::map>{ + // event + {kEventCutflow, {axisEventCut}}, + {kPosZ, {conf.vtxZ}}, + {kCent, {conf.centFine}}, + {kPosZVsCent, {conf.vtxZ, conf.cent}}, + // data + {kDataDndeta, {conf.vtxZ, conf.cent, conf.eta, conf.phi, axisTrackType}}, + {kDataDndetaMb, {conf.vtxZ, conf.eta, conf.phi}}, + // mc: generated + {kGenPosZ, {conf.vtxZ}}, + {kGenPosZVsCent, {conf.vtxZ, conf.cent}}, + {kGenAssocRecoPosZ, {conf.vtxZ}}, + {kGenAssocRecoPosZVsCent, {conf.vtxZ, conf.cent}}, + {kGenDndeta, {conf.vtxZ, conf.cent, conf.eta, conf.phi}}, + {kGenAssocRecoDndeta, {conf.vtxZ, conf.cent, conf.eta, conf.phi, axisGenType, axisPtVariation}}, + {kGenAssocRecoDndetaMb, {conf.vtxZ, conf.eta, conf.phi, axisGenType}}, + // mc: reconstructed + {kRecoPosZ, {conf.vtxZ}}, + {kRecoCent, {conf.centFine}}, + {kRecoPosZVsCent, {conf.vtxZ, conf.cent}}, + {kRecoDndeta, {conf.vtxZ, conf.cent, conf.eta, conf.phi, axisRecoType}}, + {kRecoDndetaMb, {conf.vtxZ, conf.eta, conf.phi, axisRecoType}}, + // mc: event and signal loss + {kLossEvents, {axisLossEvent}}, + {kLossCentGen, {conf.centFine}}, + {kLossCentGenAssocReco, {conf.centFine}}, + {kLossMultGen, {conf.multGen}}, + {kLossMultGenAssocReco, {conf.multGen}}, + {kLossEtaGen, {conf.eta}}, + {kLossEtaGenAssocReco, {conf.eta}}, + {kLossEtaVsCentGen, {conf.eta, conf.centFine}}, + {kLossEtaVsCentGenAssocReco, {conf.eta, conf.centFine}}, + {kLossEtaVsMultGen, {conf.eta, conf.multGen}}, + {kLossEtaVsMultGenAssocReco, {conf.eta, conf.multGen}}, + // multiplicity correlations + {kCorrNchVsFt0a, {conf.nch, conf.ft0a}}, + {kCorrNchVsFt0c, {conf.nch, conf.ft0c}}, + {kCorrNchVsNpv, {conf.nch, conf.npv}}, + {kCorrNpvVsFt0c, {conf.npv, conf.ft0c}}, + // strangeness + {kStrangePosZVsCent, {conf.vtxZ, conf.cent}}, + {kK0shortCentEtaMass, {conf.centFine, conf.eta, conf.massK0short}}, + {kLambdaCentEtaMass, {conf.centFine, conf.eta, conf.massLambda}}, + {kAntiLambdaCentEtaMass, {conf.centFine, conf.eta, conf.massLambda}}, + }; +} + +/// blocks of histograms, enabled at runtime depending on the active process functions +enum Block : uint32_t { + kBlockEvent = 1u << 0, + kBlockData = 1u << 1, + kBlockMcEfficiency = 1u << 2, + kBlockLoss = 1u << 3, + kBlockCorrelation = 1u << 4, + kBlockStrangeness = 1u << 5, +}; + +class DndetaHistManager +{ + public: + DndetaHistManager() = default; + ~DndetaHistManager() = default; + + /// \param blocks bitwise OR of Block values + void init(o2::framework::HistogramRegistry* registry, + std::map> const& Specs, + uint32_t blocks) + { + mHistogramRegistry = registry; + mBlocks = blocks; + if ((mBlocks & kBlockEvent) != 0u) { + initEvent(Specs); + } + if ((mBlocks & kBlockData) != 0u) { + initData(Specs); + } + if ((mBlocks & kBlockMcEfficiency) != 0u) { + initMcEfficiency(Specs); + } + if ((mBlocks & kBlockLoss) != 0u) { + initLoss(Specs); + } + if ((mBlocks & kBlockCorrelation) != 0u) { + initCorrelation(Specs); + } + if ((mBlocks & kBlockStrangeness) != 0u) { + initStrangeness(Specs); + } + } + + // ---- event -------------------------------------------------------------- + + void fillEventCutflow(EventCut cut) + { + mHistogramRegistry->fill(HIST(EventDir) + HIST(getHistName(kEventCutflow, HistTable)), static_cast(cut)); + } + + void fillEvent(float posZ, float cent) + { + mHistogramRegistry->fill(HIST(EventDir) + HIST(getHistName(kPosZ, HistTable)), posZ); + mHistogramRegistry->fill(HIST(EventDir) + HIST(getHistName(kCent, HistTable)), cent); + mHistogramRegistry->fill(HIST(EventDir) + HIST(getHistName(kPosZVsCent, HistTable)), posZ, cent); + } + + // ---- data --------------------------------------------------------------- + + /// fill a selected data track (track QA is done with the track histogram managers) + void fillDataTrack(float posZ, float cent, float eta, float phi, bool hasTpc) + { + mHistogramRegistry->fill(HIST(DataDir) + HIST(getHistName(kDataDndetaMb, HistTable)), posZ, eta, phi); + mHistogramRegistry->fill(HIST(DataDir) + HIST(getHistName(kDataDndeta, HistTable)), posZ, cent, eta, phi, static_cast(kGlobalPlusIts)); + const DataTrackType type = hasTpc ? kGlobalOnly : kItsOnly; + mHistogramRegistry->fill(HIST(DataDir) + HIST(getHistName(kDataDndeta, HistTable)), posZ, cent, eta, phi, static_cast(type)); + } + + // ---- mc: generated ------------------------------------------------------ + + void fillGenCollision(float posZ, float cent, bool hasSelectedReco) + { + mHistogramRegistry->fill(HIST(GenDir) + HIST(getHistName(kGenPosZ, HistTable)), posZ); + mHistogramRegistry->fill(HIST(GenDir) + HIST(getHistName(kGenPosZVsCent, HistTable)), posZ, cent); + if (hasSelectedReco) { + mHistogramRegistry->fill(HIST(GenDir) + HIST(getHistName(kGenAssocRecoPosZ, HistTable)), posZ); + mHistogramRegistry->fill(HIST(GenDir) + HIST(getHistName(kGenAssocRecoPosZVsCent, HistTable)), posZ, cent); + } + } + + /// fill a selected generated primary + /// weightUp/weightDown are the weights of the pT variations (uncertainty of the extrapolation to pT = 0), + /// they are computed by the dndeta builder + void fillGenParticle(float posZ, float cent, float eta, float phi, GenType species, bool hasSelectedReco, double weightUp, double weightDown) + { + mHistogramRegistry->fill(HIST(GenDir) + HIST(getHistName(kGenDndeta, HistTable)), posZ, cent, eta, phi); + if (!hasSelectedReco) { + return; + } + const auto all = static_cast(kGenAll); + mHistogramRegistry->fill(HIST(GenDir) + HIST(getHistName(kGenAssocRecoDndeta, HistTable)), posZ, cent, eta, phi, all, static_cast(kPtNominal)); + mHistogramRegistry->fill(HIST(GenDir) + HIST(getHistName(kGenAssocRecoDndetaMb, HistTable)), posZ, eta, phi, all); + mHistogramRegistry->fill(HIST(GenDir) + HIST(getHistName(kGenAssocRecoDndeta, HistTable)), posZ, cent, eta, phi, all, static_cast(kPtUp), weightUp); + mHistogramRegistry->fill(HIST(GenDir) + HIST(getHistName(kGenAssocRecoDndeta, HistTable)), posZ, cent, eta, phi, all, static_cast(kPtDown), weightDown); + + const auto spec = static_cast(species); + mHistogramRegistry->fill(HIST(GenDir) + HIST(getHistName(kGenAssocRecoDndeta, HistTable)), posZ, cent, eta, phi, spec, static_cast(kPtNominal)); + mHistogramRegistry->fill(HIST(GenDir) + HIST(getHistName(kGenAssocRecoDndetaMb, HistTable)), posZ, eta, phi, spec); + } + + // ---- mc: reconstructed -------------------------------------------------- + + void fillRecoCollision(float posZ, float cent) + { + mHistogramRegistry->fill(HIST(RecoDir) + HIST(getHistName(kRecoPosZ, HistTable)), posZ); + mHistogramRegistry->fill(HIST(RecoDir) + HIST(getHistName(kRecoCent, HistTable)), cent); + mHistogramRegistry->fill(HIST(RecoDir) + HIST(getHistName(kRecoPosZVsCent, HistTable)), posZ, cent); + } + + /// eta/phi are the reconstructed ones for kRecoAll and kRecoBkg, the generated ones otherwise (as in the legacy task) + void fillRecoTrack(float posZ, float cent, float eta, float phi, RecoType type) + { + mHistogramRegistry->fill(HIST(RecoDir) + HIST(getHistName(kRecoDndeta, HistTable)), posZ, cent, eta, phi, static_cast(type)); + mHistogramRegistry->fill(HIST(RecoDir) + HIST(getHistName(kRecoDndetaMb, HistTable)), posZ, eta, phi, static_cast(type)); + } + + // ---- mc: event and signal loss ------------------------------------------ + + void fillLossEvent(float cent, float multGen, bool hasSelectedReco) + { + mHistogramRegistry->fill(HIST(LossDir) + HIST(getHistName(kLossEvents, HistTable)), static_cast(kLossGenAll)); + mHistogramRegistry->fill(HIST(LossDir) + HIST(getHistName(kLossCentGen, HistTable)), cent); + mHistogramRegistry->fill(HIST(LossDir) + HIST(getHistName(kLossMultGen, HistTable)), multGen); + if (hasSelectedReco) { + mHistogramRegistry->fill(HIST(LossDir) + HIST(getHistName(kLossEvents, HistTable)), static_cast(kLossGenWithReco)); + mHistogramRegistry->fill(HIST(LossDir) + HIST(getHistName(kLossCentGenAssocReco, HistTable)), cent); + mHistogramRegistry->fill(HIST(LossDir) + HIST(getHistName(kLossMultGenAssocReco, HistTable)), multGen); + } + } + + void fillLossParticle(float eta, float cent, float multGen, bool hasSelectedReco) + { + mHistogramRegistry->fill(HIST(LossDir) + HIST(getHistName(kLossEtaGen, HistTable)), eta); + mHistogramRegistry->fill(HIST(LossDir) + HIST(getHistName(kLossEtaVsCentGen, HistTable)), eta, cent); + mHistogramRegistry->fill(HIST(LossDir) + HIST(getHistName(kLossEtaVsMultGen, HistTable)), eta, multGen); + if (hasSelectedReco) { + mHistogramRegistry->fill(HIST(LossDir) + HIST(getHistName(kLossEtaGenAssocReco, HistTable)), eta); + mHistogramRegistry->fill(HIST(LossDir) + HIST(getHistName(kLossEtaVsCentGenAssocReco, HistTable)), eta, cent); + mHistogramRegistry->fill(HIST(LossDir) + HIST(getHistName(kLossEtaVsMultGenAssocReco, HistTable)), eta, multGen); + } + } + + // ---- multiplicity correlations ------------------------------------------ + + void fillCorrelation(float nch, float npv, float ft0a, float ft0c) + { + mHistogramRegistry->fill(HIST(CorrelationDir) + HIST(getHistName(kCorrNchVsFt0a, HistTable)), nch, ft0a); + mHistogramRegistry->fill(HIST(CorrelationDir) + HIST(getHistName(kCorrNchVsFt0c, HistTable)), nch, ft0c); + mHistogramRegistry->fill(HIST(CorrelationDir) + HIST(getHistName(kCorrNchVsNpv, HistTable)), nch, npv); + mHistogramRegistry->fill(HIST(CorrelationDir) + HIST(getHistName(kCorrNpvVsFt0c, HistTable)), npv, ft0c); + } + + // ---- strangeness -------------------------------------------------------- + + void fillStrangenessCollision(float posZ, float cent) + { + mHistogramRegistry->fill(HIST(StrangenessDir) + HIST(getHistName(kStrangePosZVsCent, HistTable)), posZ, cent); + } + + void fillK0short(float cent, float eta, float mass) + { + mHistogramRegistry->fill(HIST(StrangenessDir) + HIST(getHistName(kK0shortCentEtaMass, HistTable)), cent, eta, mass); + } + + void fillLambda(float cent, float eta, float mass, bool isAntiLambda) + { + if (isAntiLambda) { + mHistogramRegistry->fill(HIST(StrangenessDir) + HIST(getHistName(kAntiLambdaCentEtaMass, HistTable)), cent, eta, mass); + } else { + mHistogramRegistry->fill(HIST(StrangenessDir) + HIST(getHistName(kLambdaCentEtaMass, HistTable)), cent, eta, mass); + } + } + + private: + void add(std::string_view dir, DndetaHist hist, std::map> const& Specs) + { + mHistogramRegistry->add(std::string(dir) + getHistNameV2(hist, HistTable), getHistDesc(hist, HistTable), getHistType(hist, HistTable), {Specs.at(hist)}); + } + + template + static void setLabels(TAxis* axis, std::array const& names) + { + for (std::size_t i = 0; i < N; i++) { + axis->SetBinLabel(static_cast(i) + 1, names[i]); + } + } + + void initEvent(std::map> const& Specs) + { + add(EventDir, kEventCutflow, Specs); + setLabels(mHistogramRegistry->get(HIST(EventDir) + HIST(getHistName(kEventCutflow, HistTable)))->GetXaxis(), EventCutNames); + add(EventDir, kPosZ, Specs); + add(EventDir, kCent, Specs); + add(EventDir, kPosZVsCent, Specs); + } + + void initData(std::map> const& Specs) + { + add(DataDir, kDataDndeta, Specs); + setLabels(mHistogramRegistry->get(HIST(DataDir) + HIST(getHistName(kDataDndeta, HistTable)))->GetAxis(4), DataTrackTypeNames); + add(DataDir, kDataDndetaMb, Specs); + } + + void initMcEfficiency(std::map> const& Specs) + { + add(GenDir, kGenPosZ, Specs); + add(GenDir, kGenPosZVsCent, Specs); + add(GenDir, kGenAssocRecoPosZ, Specs); + add(GenDir, kGenAssocRecoPosZVsCent, Specs); + add(GenDir, kGenDndeta, Specs); + add(GenDir, kGenAssocRecoDndeta, Specs); + setLabels(mHistogramRegistry->get(HIST(GenDir) + HIST(getHistName(kGenAssocRecoDndeta, HistTable)))->GetAxis(4), GenTypeNames); + setLabels(mHistogramRegistry->get(HIST(GenDir) + HIST(getHistName(kGenAssocRecoDndeta, HistTable)))->GetAxis(5), GenPtVariationNames); + add(GenDir, kGenAssocRecoDndetaMb, Specs); + setLabels(mHistogramRegistry->get(HIST(GenDir) + HIST(getHistName(kGenAssocRecoDndetaMb, HistTable)))->GetAxis(3), GenTypeNames); + + add(RecoDir, kRecoPosZ, Specs); + add(RecoDir, kRecoCent, Specs); + add(RecoDir, kRecoPosZVsCent, Specs); + add(RecoDir, kRecoDndeta, Specs); + setLabels(mHistogramRegistry->get(HIST(RecoDir) + HIST(getHistName(kRecoDndeta, HistTable)))->GetAxis(4), RecoTypeNames); + add(RecoDir, kRecoDndetaMb, Specs); + setLabels(mHistogramRegistry->get(HIST(RecoDir) + HIST(getHistName(kRecoDndetaMb, HistTable)))->GetAxis(3), RecoTypeNames); + } + + void initLoss(std::map> const& Specs) + { + add(LossDir, kLossEvents, Specs); + setLabels(mHistogramRegistry->get(HIST(LossDir) + HIST(getHistName(kLossEvents, HistTable)))->GetXaxis(), LossEventNames); + add(LossDir, kLossCentGen, Specs); + add(LossDir, kLossCentGenAssocReco, Specs); + add(LossDir, kLossMultGen, Specs); + add(LossDir, kLossMultGenAssocReco, Specs); + add(LossDir, kLossEtaGen, Specs); + add(LossDir, kLossEtaGenAssocReco, Specs); + add(LossDir, kLossEtaVsCentGen, Specs); + add(LossDir, kLossEtaVsCentGenAssocReco, Specs); + add(LossDir, kLossEtaVsMultGen, Specs); + add(LossDir, kLossEtaVsMultGenAssocReco, Specs); + } + + void initCorrelation(std::map> const& Specs) + { + add(CorrelationDir, kCorrNchVsFt0a, Specs); + add(CorrelationDir, kCorrNchVsFt0c, Specs); + add(CorrelationDir, kCorrNchVsNpv, Specs); + add(CorrelationDir, kCorrNpvVsFt0c, Specs); + } + + void initStrangeness(std::map> const& Specs) + { + add(StrangenessDir, kStrangePosZVsCent, Specs); + add(StrangenessDir, kK0shortCentEtaMass, Specs); + add(StrangenessDir, kLambdaCentEtaMass, Specs); + add(StrangenessDir, kAntiLambdaCentEtaMass, Specs); + } + + o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; + uint32_t mBlocks = 0; +}; + +} // namespace o2::analysis::femto::dndetahistmanager + +#endif // PWGCF_FEMTO_CORE_DNDETAHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/femtoUtils.h b/PWGCF/Femto/Core/femtoUtils.h index c623e161f2b..643ddffe654 100644 --- a/PWGCF/Femto/Core/femtoUtils.h +++ b/PWGCF/Femto/Core/femtoUtils.h @@ -93,6 +93,9 @@ inline double getPdgMass(int pdgCode) case kKPlus: mass = o2::constants::physics::MassKPlus; break; + case kK0Short: + mass = o2::constants::physics::MassK0Short; + break; case kProton: mass = o2::constants::physics::MassProton; break; diff --git a/PWGCF/Femto/Core/mcBuilder.h b/PWGCF/Femto/Core/mcBuilder.h index ac110ae0424..e722e70d780 100644 --- a/PWGCF/Femto/Core/mcBuilder.h +++ b/PWGCF/Femto/Core/mcBuilder.h @@ -105,15 +105,19 @@ struct ConfMcCollisionFilters : o2::framework::ConfigurableGroup { template struct ConfMcParticleSelection : o2::framework::ConfigurableGroup { std::string prefix = std::string(Prefix); - // kinematic cuts for filtering tracks + // kinematic cuts o2::framework::Configurable ptMin{"ptMin", 0.2f, "Minimum pT"}; o2::framework::Configurable ptMax{"ptMax", 6.f, "Maximum pT"}; o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + // pdg code and charge o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", 2212, "Absolute value of PDG code. Set sign of charge to -1 for antiparticle."}; o2::framework::Configurable chargeSign{"chargeSign", 1, "Particle charge sign: +1 for positive, -1 for negative, 0 for both"}; + // origin + o2::framework::Configurable requireOrigin{"requireOrigin", false, "If true, only particles with the origin given in 'origin' are selected"}; + o2::framework::Configurable origin{"origin", static_cast(modes::McOrigin::kPhysicalPrimary), "Required mc origin, only used if requireOrigin is true (see modes::McOrigin; 2: physical primary)"}; }; constexpr const char PrefixMcParticleSelection1[] = "McParticleSelection1"; @@ -284,9 +288,14 @@ class McBuilder this->getOrCreateMcParticleRow(mcParticle, mcParticles, mcCol, mcProducts); } - /// Write the generated primary charged particles needed for the dNch/deta calculation - template - void fillMcPassThrough(T1 const& mcCols, T2 const& mcParticles, T3& perMcCollision, T4& mcProducts, T5& pdgDb) + /// Write all generated physical primaries within the eta acceptance. + /// No charge requirement is applied here: neutral primaries (e.g. Lambdas) are needed for generator-level + /// pair triggers. Charge (and any other) selection has to be done downstream. + /// NOTE: FMcParticles also contains rows created through reco labels (secondaries, particles outside the + /// acceptance), so a dNch/deta loop must still require origin == kPhysicalPrimary, a charged pdg code + /// and the eta acceptance + template + void fillMcPassThrough(T1 const& mcCols, T2 const& mcParticles, T3& perMcCollision, T4& mcProducts) { if (!mPassThrough) { return; @@ -300,10 +309,6 @@ class McBuilder if (!mcParticle.isPhysicalPrimary() || std::fabs(mcParticle.eta()) > mEtaAcceptanceMcReco) { continue; } - const auto* pdgParticle = pdgDb->GetParticle(mcParticle.pdgCode()); - if (pdgParticle == nullptr || std::fabs(pdgParticle->Charge()) < o2::constants::math::Almost0) { - continue; - } // NOTE: full mcParticles table, never the slice - the ancestry walk resolves global indices this->fillMcParticle(mcParticle, mcParticles, mcCol, mcProducts); } diff --git a/PWGCF/Femto/Core/modes.h b/PWGCF/Femto/Core/modes.h index f55830b958a..ca55b122c80 100644 --- a/PWGCF/Femto/Core/modes.h +++ b/PWGCF/Femto/Core/modes.h @@ -16,8 +16,6 @@ #ifndef PWGCF_FEMTO_CORE_MODES_H_ #define PWGCF_FEMTO_CORE_MODES_H_ -#include "dataTypes.h" - #include "PWGCF/Femto/Core/dataTypes.h" #include @@ -86,6 +84,21 @@ enum class MomentumType : o2::analysis::femto::datatypes::MomentumType { kPTpc = 2, // momentum at inner wall of tpc }; +/// axis title for the momentum used on the x axis of PID histograms +constexpr const char* momentumTypeToAxisTitle(MomentumType type) +{ + switch (type) { + case MomentumType::kPt: + return "p_{T} (GeV/#it{c})"; + case MomentumType::kPAtPv: + return "p (GeV/#it{c})"; + case MomentumType::kPTpc: + return "p_{TPC} (GeV/#it{c})"; + default: + return "p (GeV/#it{c})"; + } +} + enum class TransverseMassType : o2::analysis::femto::datatypes::TransverseMassType { kAveragePdgMass = 0, kReducedPdgMass = 1, @@ -146,9 +159,57 @@ enum class Track : o2::analysis::femto::datatypes::TrackType { kCascadeBachelor, kResonanceDaughter, kKinkDaughter, - kCharmDaughter + kCharmDaughter, + kTrackTypeLast +}; + +constexpr const char* trackTypeToString(Track type) +{ + switch (type) { + case Track::kTrack: + return "Track"; + case Track::kV0Daughter: + return "V0Daughter"; + case Track::kCascadeBachelor: + return "CascadeBachelor"; + case Track::kResonanceDaughter: + return "ResonanceDaughter"; + case Track::kKinkDaughter: + return "KinkDaughter"; + case Track::kCharmDaughter: + return "CharmDaughter"; + default: + return "UnknownTrackType"; + } +} + +enum class Trigger : uint8_t { + kMinimumBias, + kTrackTrack, + kTrackV0, + kTrackTrackTrack, + kTrackTrackV0, + kTriggerLast }; +constexpr const char* triggerToString(Trigger trigger) +{ + switch (trigger) { + case Trigger::kMinimumBias: + return "MinimumBias"; + case Trigger::kTrackTrack: + return "TrackTrack"; + case Trigger::kTrackV0: + return "TrackV0"; + case Trigger::kTrackTrackTrack: + return "TrackTrackTrack"; + case Trigger::kTrackTrackV0: + return "TrackTrackV0"; + default: + return "UnknownTrigger"; + } +} + enum class V0 : o2::analysis::femto::datatypes::V0Type { kLambda, kAntiLambda, diff --git a/PWGCF/Femto/Core/pairBuilder.h b/PWGCF/Femto/Core/pairBuilder.h index 43dc4c8c82c..e3e485eabb8 100644 --- a/PWGCF/Femto/Core/pairBuilder.h +++ b/PWGCF/Femto/Core/pairBuilder.h @@ -148,13 +148,14 @@ class PairTrackTrackBuilder } // data + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2& trackTable, T3& partition1, T4& partition2, T5& cache) + bool processSameEvent(T1 const& col, T2& trackTable, T3& partition1, T4& partition2, T5& cache) { if (mSameSpecies) { auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice1.size() < nLimitPartitionIdenticalParticles) { - return; + return false; } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); @@ -162,27 +163,28 @@ class PairTrackTrackBuilder if (mMixIdenticalParticles) { pairOrder = static_cast(mDist(mRng)); } - pairprocesshelpers::processSameEvent(trackSlice1, trackTable, col, mTrackHistManager1, mPairHistManagerSe, mCprSe, mPcSe, pairOrder); - } else { - auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles) { - return; - } - mColHistManager.template fill(col); - mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackTable, col, mTrackHistManager1, mTrackHistManager2, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice1, trackTable, col, mTrackHistManager1, mPairHistManagerSe, mCprSe, mPcSe, pairOrder); + } + + auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles) { + return false; } + mColHistManager.template fill(col); + mCprSe.setMagField(col.magField()); + return pairprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackTable, col, mTrackHistManager1, mTrackHistManager2, mPairHistManagerSe, mCprSe, mPcSe); } // mc + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2 const& mcCols, T3& trackTable, T4& partition1, T5& partition2, T6 const& mcParticles, T7 const& mcMothers, T8 const& mcPartonicMothers, T9& cache) + bool processSameEvent(T1 const& col, T2 const& mcCols, T3& trackTable, T4& partition1, T5& partition2, T6 const& mcParticles, T7 const& mcMothers, T8 const& mcPartonicMothers, T9& cache) { if (mSameSpecies) { auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice1.size() < nLimitPartitionIdenticalParticles) { - return; + return false; } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); @@ -190,17 +192,17 @@ class PairTrackTrackBuilder if (mMixIdenticalParticles) { pairOrder = static_cast(mDist(mRng)); } - pairprocesshelpers::processSameEvent(trackSlice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mPairHistManagerSe, mTrackCleaner1, mCprSe, mPcSe, pairOrder); - } else { - auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles) { - return; - } - mColHistManager.template fill(col, mcCols); - mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTrackHistManager2, mPairHistManagerSe, mTrackCleaner1, mTrackCleaner2, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mPairHistManagerSe, mTrackCleaner1, mCprSe, mPcSe, pairOrder); + } + + auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles) { + return false; } + mColHistManager.template fill(col, mcCols); + mCprSe.setMagField(col.magField()); + return pairprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTrackHistManager2, mPairHistManagerSe, mTrackCleaner1, mTrackCleaner2, mCprSe, mPcSe); } template @@ -456,13 +458,14 @@ class PairV0V0Builder } } + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2 const& trackTable, T3& /*v0table*/, T4& partition1, T5& partition2, T6& cache) + bool processSameEvent(T1 const& col, T2 const& trackTable, T3& /*v0table*/, T4& partition1, T5& partition2, T6& cache) { if (mSameSpecies) { auto v0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (v0Slice1.size() < nLimitPartitionIdenticalParticles) { - return; + return false; } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); @@ -470,27 +473,28 @@ class PairV0V0Builder if (mMixIdenticalParticles) { pairOrder = static_cast(mDist(mRng)); } - pairprocesshelpers::processSameEvent(v0Slice1, trackTable, col, mV0HistManager1, mPairHistManagerSe, mCprSe, mPcSe, pairOrder); - } else { - auto v0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto v0Slice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (v0Slice1.size() < nLimitPartitionParticles || v0Slice2.size() < nLimitPartitionParticles) { - return; - } - mColHistManager.template fill(col); - mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(v0Slice1, v0Slice2, trackTable, col, mV0HistManager1, mV0HistManager2, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(v0Slice1, trackTable, col, mV0HistManager1, mPairHistManagerSe, mCprSe, mPcSe, pairOrder); } + + auto v0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto v0Slice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (v0Slice1.size() < nLimitPartitionParticles || v0Slice2.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(col); + mCprSe.setMagField(col.magField()); + return pairprocesshelpers::processSameEvent(v0Slice1, v0Slice2, trackTable, col, mV0HistManager1, mV0HistManager2, mPairHistManagerSe, mCprSe, mPcSe); } // mc + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2 const& mcCols, T3 const& trackTable, T4 const& /*v0table*/, T5& partition1, T6& partition2, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) + bool processSameEvent(T1 const& col, T2 const& mcCols, T3 const& trackTable, T4 const& /*v0table*/, T5& partition1, T6& partition2, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) { if (mSameSpecies) { auto v0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (v0Slice1.size() < nLimitPartitionIdenticalParticles) { - return; + return false; } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); @@ -498,17 +502,17 @@ class PairV0V0Builder if (mMixIdenticalParticles) { pairOrder = static_cast(mDist(mRng)); } - pairprocesshelpers::processSameEvent(v0Slice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mV0HistManager1, mPairHistManagerSe, mV0Cleaner1, mCprSe, mPcSe, pairOrder); - } else { - auto v0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto v0Slice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (v0Slice1.size() < nLimitPartitionParticles || v0Slice2.size() < nLimitPartitionParticles) { - return; - } - mColHistManager.template fill(col, mcCols); - mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(v0Slice1, v0Slice2, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mV0HistManager1, mV0HistManager2, mPairHistManagerSe, mV0Cleaner1, mV0Cleaner2, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(v0Slice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mV0HistManager1, mPairHistManagerSe, mV0Cleaner1, mCprSe, mPcSe, pairOrder); + } + + auto v0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto v0Slice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (v0Slice1.size() < nLimitPartitionParticles || v0Slice2.size() < nLimitPartitionParticles) { + return false; } + mColHistManager.template fill(col, mcCols); + mCprSe.setMagField(col.magField()); + return pairprocesshelpers::processSameEvent(v0Slice1, v0Slice2, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mV0HistManager1, mV0HistManager2, mPairHistManagerSe, mV0Cleaner1, mV0Cleaner2, mCprSe, mPcSe); } template @@ -741,13 +745,14 @@ class PairD0D0Builder } } + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2 const& trackTable, T3& /*d0table*/, T4& partition1, T5& partition2, T6& cache) + bool processSameEvent(T1 const& col, T2 const& trackTable, T3& /*d0table*/, T4& partition1, T5& partition2, T6& cache) { if (mSameSpecies) { auto d0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (d0Slice1.size() < nLimitPartitionIdenticalParticles) { - return; + return false; } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); @@ -755,27 +760,28 @@ class PairD0D0Builder if (mMixIdenticalParticles) { pairOrder = static_cast(mDist(mRng)); } - pairprocesshelpers::processSameEvent(d0Slice1, trackTable, col, mD0HistManager1, mPairHistManagerSe, mCprSe, mPcSe, pairOrder); - } else { - auto d0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto d0Slice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (d0Slice1.size() < nLimitPartitionParticles || d0Slice2.size() < nLimitPartitionParticles) { - return; - } - mColHistManager.template fill(col); - mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(d0Slice1, d0Slice2, trackTable, col, mD0HistManager1, mD0HistManager2, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(d0Slice1, trackTable, col, mD0HistManager1, mPairHistManagerSe, mCprSe, mPcSe, pairOrder); } + + auto d0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto d0Slice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (d0Slice1.size() < nLimitPartitionParticles || d0Slice2.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(col); + mCprSe.setMagField(col.magField()); + return pairprocesshelpers::processSameEvent(d0Slice1, d0Slice2, trackTable, col, mD0HistManager1, mD0HistManager2, mPairHistManagerSe, mCprSe, mPcSe); } // mc + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2 const& mcCols, T3 const& trackTable, T4 const& /*d0table*/, T5& partition1, T6& partition2, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) + bool processSameEvent(T1 const& col, T2 const& mcCols, T3 const& trackTable, T4 const& /*d0table*/, T5& partition1, T6& partition2, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) { if (mSameSpecies) { auto d0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (d0Slice1.size() < nLimitPartitionIdenticalParticles) { - return; + return false; } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); @@ -783,17 +789,16 @@ class PairD0D0Builder if (mMixIdenticalParticles) { pairOrder = static_cast(mDist(mRng)); } - pairprocesshelpers::processSameEvent(d0Slice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mD0HistManager1, mPairHistManagerSe, mD0Cleaner1, mCprSe, mPcSe, pairOrder); - } else { - auto d0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto d0Slice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (d0Slice1.size() < nLimitPartitionParticles || d0Slice2.size() < nLimitPartitionParticles) { - return; - } - mColHistManager.template fill(col, mcCols); - mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(d0Slice1, d0Slice2, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mD0HistManager1, mD0HistManager2, mPairHistManagerSe, mD0Cleaner1, mD0Cleaner2, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(d0Slice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mD0HistManager1, mPairHistManagerSe, mD0Cleaner1, mCprSe, mPcSe, pairOrder); + } + auto d0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto d0Slice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (d0Slice1.size() < nLimitPartitionParticles || d0Slice2.size() < nLimitPartitionParticles) { + return false; } + mColHistManager.template fill(col, mcCols); + mCprSe.setMagField(col.magField()); + return pairprocesshelpers::processSameEvent(d0Slice1, d0Slice2, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mD0HistManager1, mD0HistManager2, mPairHistManagerSe, mD0Cleaner1, mD0Cleaner2, mCprSe, mPcSe); } template @@ -976,17 +981,18 @@ class PairTrackD0Builder mMixingDepth = confMixing.depth.value; } + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2& trackTable, T3& trackPartition, T4& /*d0table*/, T5& d0Partition, T6& cache) + bool processSameEvent(T1 const& col, T2& trackTable, T3& trackPartition, T4& /*d0table*/, T5& d0Partition, T6& cache) { auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto d0Slice = d0Partition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice.size() < nLimitPartitionParticles || d0Slice.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(trackSlice, d0Slice, trackTable, col, mTrackHistManager, mD0HistManager, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, d0Slice, trackTable, col, mTrackHistManager, mD0HistManager, mPairHistManagerSe, mCprSe, mPcSe); } template @@ -1007,17 +1013,18 @@ class PairTrackD0Builder } } + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2 const& mcCols, T3 const& trackTable, T4& trackPartition, T5 const& /*d0table*/, T6& d0Partition, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) + bool processSameEvent(T1 const& col, T2 const& mcCols, T3 const& trackTable, T4& trackPartition, T5 const& /*d0table*/, T6& d0Partition, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) { auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto d0Slice = d0Partition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice.size() < nLimitPartitionParticles || d0Slice.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(trackSlice, d0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager, mD0HistManager, mPairHistManagerSe, mTrackCleaner, mD0Cleaner, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, d0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager, mD0HistManager, mPairHistManagerSe, mTrackCleaner, mD0Cleaner, mCprSe, mPcSe); } template @@ -1132,17 +1139,18 @@ class PairTrackLcBuilder mMixingDepth = confMixing.depth.value; } + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2& trackTable, T3& trackPartition, T4& /*lcTable*/, T5& lcPartition, T6& cache) + bool processSameEvent(T1 const& col, T2& trackTable, T3& trackPartition, T4& /*lcTable*/, T5& lcPartition, T6& cache) { auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto lcSlice = lcPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice.size() < nLimitPartitionParticles || lcSlice.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(trackSlice, lcSlice, trackTable, col, mTrackHistManager, mLcHistManager, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, lcSlice, trackTable, col, mTrackHistManager, mLcHistManager, mPairHistManagerSe, mCprSe, mPcSe); } template @@ -1163,17 +1171,18 @@ class PairTrackLcBuilder } } + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2 const& mcCols, T3 const& trackTable, T4& trackPartition, T5 const& /*lcTable*/, T6& lcPartition, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) + bool processSameEvent(T1 const& col, T2 const& mcCols, T3 const& trackTable, T4& trackPartition, T5 const& /*lcTable*/, T6& lcPartition, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) { auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto lcSlice = lcPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice.size() < nLimitPartitionParticles || lcSlice.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(trackSlice, lcSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager, mLcHistManager, mPairHistManagerSe, mTrackCleaner, mLcCleaner, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, lcSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager, mLcHistManager, mPairHistManagerSe, mTrackCleaner, mLcCleaner, mCprSe, mPcSe); } template @@ -1303,30 +1312,32 @@ class PairTrackV0Builder mMixingDepth = confMixing.depth.value; } + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2& trackTable, T3& trackPartition, T4& /*v0table*/, T5& v0Partition, T6& cache) + bool processSameEvent(T1 const& col, T2& trackTable, T3& trackPartition, T4& /*v0table*/, T5& v0Partition, T6& cache) { auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto v0Slice = v0Partition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice.size() < nLimitPartitionParticles || v0Slice.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(trackSlice, v0Slice, trackTable, col, mTrackHistManager, mV0HistManager, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, v0Slice, trackTable, col, mTrackHistManager, mV0HistManager, mPairHistManagerSe, mCprSe, mPcSe); } + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2 const& mcCols, T3 const& trackTable, T4& trackPartition, T5 const& /*v0table*/, T6& v0Partition, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) + bool processSameEvent(T1 const& col, T2 const& mcCols, T3 const& trackTable, T4& trackPartition, T5 const& /*v0table*/, T6& v0Partition, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) { auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto v0Slice = v0Partition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice.size() < nLimitPartitionParticles || v0Slice.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(trackSlice, v0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager, mV0HistManager, mPairHistManagerSe, mTrackCleaner, mV0Cleaner, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, v0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager, mV0HistManager, mPairHistManagerSe, mTrackCleaner, mV0Cleaner, mCprSe, mPcSe); } template @@ -1452,17 +1463,18 @@ class PairTrackTwoTrackResonanceBuilder mMixingDepth = confMixing.depth.value; } + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2& trackTable, T3& trackPartition, T4& /*resonanceTable*/, T5& resonancePartition, T6& cache) + bool processSameEvent(T1 const& col, T2& trackTable, T3& trackPartition, T4& /*resonanceTable*/, T5& resonancePartition, T6& cache) { auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto resonanaceSlice = resonancePartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice.size() < nLimitPartitionParticles || resonanaceSlice.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(trackSlice, resonanaceSlice, trackTable, col, mTrackHistManager, mResonanceHistManager, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, resonanaceSlice, trackTable, col, mTrackHistManager, mResonanceHistManager, mPairHistManagerSe, mCprSe, mPcSe); } template @@ -1581,17 +1593,18 @@ class PairV0TwoTrackResonanceBuilder mMixingDepth = confMixing.depth.value; } + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2& trackTable, T3& v0Partition, T4& resonancePartition, T5& cache) + bool processSameEvent(T1 const& col, T2& trackTable, T3& v0Partition, T4& resonancePartition, T5& cache) { auto v0Slice = v0Partition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto resonanaceSlice = resonancePartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (v0Slice.size() < nLimitPartitionParticles || resonanaceSlice.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(v0Slice, resonanaceSlice, trackTable, col, mV0HistManager, mResonanceHistManager, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(v0Slice, resonanaceSlice, trackTable, col, mV0HistManager, mResonanceHistManager, mPairHistManagerSe, mCprSe, mPcSe); } template @@ -1701,30 +1714,32 @@ class PairTrackKinkBuilder mMixingDepth = confMixing.depth.value; } + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2& trackTable, T3& trackPartition, T4& /*kinktable*/, T5& kinkPartition, T6& cache) + bool processSameEvent(T1 const& col, T2& trackTable, T3& trackPartition, T4& /*kinktable*/, T5& kinkPartition, T6& cache) { auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto kinkSlice = kinkPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice.size() < nLimitPartitionParticles || kinkSlice.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(trackSlice, kinkSlice, trackTable, col, mTrackHistManager, mKinkHistManager, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, kinkSlice, trackTable, col, mTrackHistManager, mKinkHistManager, mPairHistManagerSe, mCprSe, mPcSe); } + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2 const& mcCols, T3& trackTable, T4& trackPartition, T5& /*kinktable*/, T6& kinkPartition, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) + bool processSameEvent(T1 const& col, T2 const& mcCols, T3& trackTable, T4& trackPartition, T5& /*kinktable*/, T6& kinkPartition, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) { auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto kinkSlice = kinkPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice.size() < nLimitPartitionParticles || kinkSlice.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(trackSlice, kinkSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager, mKinkHistManager, mPairHistManagerSe, mTrackCleaner, mKinkCleaner, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, kinkSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager, mKinkHistManager, mPairHistManagerSe, mTrackCleaner, mKinkCleaner, mCprSe, mPcSe); } template @@ -1866,30 +1881,32 @@ class PairTrackCascadeBuilder mMixingDepth = confMixing.depth.value; } + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2& trackTable, T3& trackPartition, T4& /*cascadeTable*/, T5& cascadePartition, T6& cache) + bool processSameEvent(T1 const& col, T2& trackTable, T3& trackPartition, T4& /*cascadeTable*/, T5& cascadePartition, T6& cache) { auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto cascadeSlice = cascadePartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice.size() < nLimitPartitionParticles || cascadeSlice.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(trackSlice, cascadeSlice, trackTable, col, mTrackHistManager, mCascadeHistManager, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, cascadeSlice, trackTable, col, mTrackHistManager, mCascadeHistManager, mPairHistManagerSe, mCprSe, mPcSe); } + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2 const& mcCols, T3 const& trackTable, T4& trackPartition, T5 const& /*cascadeTabel*/, T6& cascadePartition, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) + bool processSameEvent(T1 const& col, T2 const& mcCols, T3 const& trackTable, T4& trackPartition, T5 const& /*cascadeTabel*/, T6& cascadePartition, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) { auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto cascadeSlice = cascadePartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice.size() < nLimitPartitionParticles || cascadeSlice.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(trackSlice, cascadeSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager, mCascadeHistManager, mPairHistManagerSe, mTrackCleaner, mCascadeCleaner, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, cascadeSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager, mCascadeHistManager, mPairHistManagerSe, mTrackCleaner, mCascadeCleaner, mCprSe, mPcSe); } template @@ -2045,30 +2062,31 @@ class PairMcParticleMcParticleBuilder } // only mc + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2 const& mcParticles, T3 const& mcMothers, T4 const& mcPartonicMothers, T5& partition1, T6& partition2, T7& cache) + bool processSameEvent(T1 const& col, T2 const& mcParticles, T3 const& mcMothers, T4 const& mcPartonicMothers, T5& partition1, T6& partition2, T7& cache) { if (mSameSpecies) { auto mcParticleSlice = partition1->sliceByCached(o2::aod::femtomcparticle::fMcColId, col.globalIndex(), cache); if (mcParticleSlice.size() < nLimitPartitionIdenticalParticles) { - return; + return false; } mColHistManager.template fill(col); pairprocesshelpers::PairOrder pairOrder = pairprocesshelpers::kOrder12; if (mMixIdenticalParticles) { pairOrder = static_cast(mDist(mRng)); } - pairprocesshelpers::processSameEvent(mcParticleSlice, mcParticles, mcMothers, mcPartonicMothers, col, mMcParticleHistManager1, mPairHistManagerSe, mMcParticleCleaner1, mCprSe, mPcSe, pairOrder); - } else { - auto mcParticleSlice1 = partition1->sliceByCached(o2::aod::femtomcparticle::fMcColId, col.globalIndex(), cache); - auto mcParticleSlice2 = partition2->sliceByCached(o2::aod::femtomcparticle::fMcColId, col.globalIndex(), cache); - if (mcParticleSlice1.size() < nLimitPartitionParticles || mcParticleSlice2.size() < nLimitPartitionParticles) { - return; - } - mColHistManager.template fill(col); - pairprocesshelpers::processSameEvent(mcParticleSlice1, mcParticleSlice2, mcParticles, mcMothers, mcPartonicMothers, col, mMcParticleHistManager1, mMcParticleHistManager2, mPairHistManagerSe, mMcParticleCleaner1, mMcParticleCleaner2, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(mcParticleSlice, mcParticles, mcMothers, mcPartonicMothers, col, mMcParticleHistManager1, mPairHistManagerSe, mMcParticleCleaner1, mCprSe, mPcSe, pairOrder); } + + auto mcParticleSlice1 = partition1->sliceByCached(o2::aod::femtomcparticle::fMcColId, col.globalIndex(), cache); + auto mcParticleSlice2 = partition2->sliceByCached(o2::aod::femtomcparticle::fMcColId, col.globalIndex(), cache); + if (mcParticleSlice1.size() < nLimitPartitionParticles || mcParticleSlice2.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(col); + return pairprocesshelpers::processSameEvent(mcParticleSlice1, mcParticleSlice2, mcParticles, mcMothers, mcPartonicMothers, col, mMcParticleHistManager1, mMcParticleHistManager2, mPairHistManagerSe, mMcParticleCleaner1, mMcParticleCleaner2, mCprSe, mPcSe); } template diff --git a/PWGCF/Femto/Core/pairProcessHelpers.h b/PWGCF/Femto/Core/pairProcessHelpers.h index ced0f7e7e14..0df4aa3853d 100644 --- a/PWGCF/Femto/Core/pairProcessHelpers.h +++ b/PWGCF/Femto/Core/pairProcessHelpers.h @@ -27,6 +27,10 @@ namespace o2::analysis::femto::pairprocesshelpers { +// NOTE: all processSameEvent helpers return true if at least one pair in the event passed +// the pair cleaner, the close pair rejection and the pair cuts (e.g. kstarMax). +// This allows to use them as a pair trigger. + enum PairOrder : uint8_t { kOrder12, kOrder21 @@ -41,7 +45,7 @@ template -void processSameEvent(T1 const& SliceParticle, +bool processSameEvent(T1 const& SliceParticle, T2 const& TrackTable, T3 const& Collision, T4& ParticleHistManager, @@ -51,6 +55,7 @@ void processSameEvent(T1 const& SliceParticle, PairOrder pairOrder) { PairHistManager.resetTrackedParticlesPerEvent(); + bool foundPair = false; for (auto const& part : SliceParticle) { ParticleHistManager.template fill(part, TrackTable); } @@ -78,9 +83,11 @@ void processSameEvent(T1 const& SliceParticle, if (PairHistManager.checkPairCuts()) { PairHistManager.template fill(); PairHistManager.trackParticlesPerEvent(p1, p2); + foundPair = true; } } PairHistManager.fillMixingQaSe(); + return foundPair; } // process same event for identical particles with mc information @@ -97,7 +104,7 @@ template -void processSameEvent(T1 const& SliceParticle, +bool processSameEvent(T1 const& SliceParticle, T2 const& TrackTable, T3 const& mcParticles, T4 const& mcMothers, @@ -112,6 +119,7 @@ void processSameEvent(T1 const& SliceParticle, PairOrder pairOrder) { PairHistManager.resetTrackedParticlesPerEvent(); + bool foundPair = false; for (auto const& part : SliceParticle) { if (!ParticleCleaner.isClean(part, mcParticles, mcMothers, mcPartonicMothers)) { continue; @@ -147,9 +155,11 @@ void processSameEvent(T1 const& SliceParticle, if (PairHistManager.checkPairCuts()) { PairHistManager.template fill(); PairHistManager.trackParticlesPerEvent(p1, p2); + foundPair = true; } } PairHistManager.fillMixingQaSe(); + return foundPair; } // process same event for non-identical particles @@ -163,7 +173,7 @@ template -void processSameEvent(T1 const& SliceParticle1, +bool processSameEvent(T1 const& SliceParticle1, T2 const& SliceParticle2, T3 const& TrackTable, T4 const& Collision, @@ -174,6 +184,7 @@ void processSameEvent(T1 const& SliceParticle1, T9& PcManager) { PairHistManager.resetTrackedParticlesPerEvent(); + bool foundPair = false; // Fill single particle histograms for (auto const& part : SliceParticle1) { ParticleHistManager1.template fill(part, TrackTable); @@ -195,9 +206,11 @@ void processSameEvent(T1 const& SliceParticle1, if (PairHistManager.checkPairCuts()) { PairHistManager.template fill(); PairHistManager.trackParticlesPerEvent(p1, p2); + foundPair = true; } } PairHistManager.fillMixingQaSe(); + return foundPair; } // process same event for non-identical particles with mc information @@ -217,7 +230,7 @@ template -void processSameEvent(T1 const& SliceParticle1, +bool processSameEvent(T1 const& SliceParticle1, T2 const& SliceParticle2, T3 const& TrackTable, T4 const& mcParticles, @@ -234,6 +247,7 @@ void processSameEvent(T1 const& SliceParticle1, T15& PcManager) { PairHistManager.resetTrackedParticlesPerEvent(); + bool foundPair = false; // Fill single particle histograms for (auto const& part : SliceParticle1) { if (!ParticleCleaner1.isClean(part, mcParticles, mcMothers, mcPartonicMothers)) { @@ -266,9 +280,11 @@ void processSameEvent(T1 const& SliceParticle1, if (PairHistManager.checkPairCuts()) { PairHistManager.template fill(); PairHistManager.trackParticlesPerEvent(p1, p2); + foundPair = true; } } PairHistManager.fillMixingQaSe(); + return foundPair; } // process same event for identical particles, mc truth only (no track table, no reco collisions) template -void processSameEvent(T1 const& SliceParticle, +bool processSameEvent(T1 const& SliceParticle, T2 const& /*mcParticles*/, T3 const& mcMothers, T4 const& mcPartonicMothers, @@ -295,6 +311,7 @@ void processSameEvent(T1 const& SliceParticle, PairOrder pairOrder) { PairHistManager.resetTrackedParticlesPerEvent(); + bool foundPair = false; for (auto const& part : SliceParticle) { if (!ParticleCleaner.isClean(part, mcMothers, mcPartonicMothers)) { continue; @@ -327,9 +344,11 @@ void processSameEvent(T1 const& SliceParticle, if (PairHistManager.checkPairCuts()) { PairHistManager.template fill(); PairHistManager.trackParticlesPerEvent(p1, p2); + foundPair = true; } } PairHistManager.fillMixingQaSe(); + return foundPair; } // process same event for non-identical particles, mc truth only @@ -347,7 +366,7 @@ template -void processSameEvent(T1 const& SliceParticle1, +bool processSameEvent(T1 const& SliceParticle1, T2 const& SliceParticle2, T3 const& /*mcParticles*/, T4 const& mcMothers, @@ -362,6 +381,7 @@ void processSameEvent(T1 const& SliceParticle1, T13& PcManager) { PairHistManager.resetTrackedParticlesPerEvent(); + bool foundPair = false; for (auto const& part : SliceParticle1) { if (!ParticleCleaner1.isClean(part, mcMothers, mcPartonicMothers)) { continue; @@ -391,9 +411,11 @@ void processSameEvent(T1 const& SliceParticle1, if (PairHistManager.checkPairCuts()) { PairHistManager.template fill(); PairHistManager.trackParticlesPerEvent(p1, p2); + foundPair = true; } } PairHistManager.fillMixingQaSe(); + return foundPair; } // mixed event in data diff --git a/PWGCF/Femto/Core/partitions.h b/PWGCF/Femto/Core/partitions.h index ad0af6731b8..fac0704c71f 100644 --- a/PWGCF/Femto/Core/partitions.h +++ b/PWGCF/Femto/Core/partitions.h @@ -207,7 +207,8 @@ // macros for mc particle (mc only) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define MAKE_MC_PARTICLE_PARTITION(selection) \ - ifnode((selection).pdgCodeAbs.node() == 0, true, (selection).pdgCodeAbs == nabs(o2::aod::femtomcparticle::pdgCode)) && \ + ifnode((selection).requireOrigin.node(), o2::aod::femtomcparticle::origin == o2::framework::expressions::as((selection).origin), true) && \ + ifnode((selection).pdgCodeAbs.node() == 0, true, (selection).pdgCodeAbs == nabs(o2::aod::femtomcparticle::pdgCode)) && \ ifnode((selection).chargeSign.node() != 0, ifnode((selection).chargeSign.node() > 0, o2::aod::femtobase::stored::signedPt > 0.f, o2::aod::femtobase::stored::signedPt < 0.f), true) && \ (nabs(o2::aod::femtobase::stored::signedPt) > (selection).ptMin) && \ (nabs(o2::aod::femtobase::stored::signedPt) < (selection).ptMax) && \ diff --git a/PWGCF/Femto/Core/trackBuilder.h b/PWGCF/Femto/Core/trackBuilder.h index bec064663c4..e499cd26381 100644 --- a/PWGCF/Femto/Core/trackBuilder.h +++ b/PWGCF/Femto/Core/trackBuilder.h @@ -16,8 +16,6 @@ #ifndef PWGCF_FEMTO_CORE_TRACKBUILDER_H_ #define PWGCF_FEMTO_CORE_TRACKBUILDER_H_ -#include "femtoUtils.h" - #include "PWGCF/Femto/Core/baseSelection.h" #include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/femtoUtils.h" @@ -63,6 +61,8 @@ struct ConfTrackBits : o2::framework::ConfigurableGroup { o2::framework::Configurable> itsIbClustersMin{"itsIbClustersMin", {3.f}, "Minimum number of clusters in inner barrel (max 3) of ITS"}; o2::framework::Configurable> dcaxyMax{"dcaxyMax", {"0.004 + 0.013*pow(x, -1)"}, "Maximum |dca_xy| as a function of pT. Has to be a valid TForumal, where x=pt"}; o2::framework::Configurable> dcazMax{"dcazMax", {"0.004 + 0.013*pow(x, -1)"}, "Maximum |dca_z| as a function of pT. Has to be a valid TForumal, where x=pt"}; + o2::framework::Configurable> itsChi2Max{"itsChi2Max", {}, "Maximum ITS chi2 per cluster"}; + o2::framework::Configurable> tpcChi2Max{"tpcChi2Max", {}, "Maximum TPC chi2 per cluster"}; // Electron PID cuts o2::framework::Configurable requirePidElectron{"requirePidElectron", false, "Make election PID optional"}; @@ -166,6 +166,11 @@ constexpr const char PrefixPionMinus[] = "PionMinusSelection"; constexpr const char PrefixKaonPlus[] = "KaonPlusSelection"; constexpr const char PrefixKaonMinus[] = "KaonMinusSelection"; +// for dN/deta: tracks with TPC (ITS + TPC quality bits) and tracks without TPC (ITS quality bits only) +// set maskLowMomentum = maskHighMomentum (quality bits only), chargeSign = 0 and the eta/pT range of the measurement +constexpr const char PrefixTrackSelectionDndetaGlobal[] = "TrackSelectionDndetaGlobal"; // Global = ITS + TPC +constexpr const char PrefixTrackSelectionDndetaItsOnly[] = "TrackSelectionDndetaItsOnly"; // ITS only + // Instantiate different instances with unique prefixes using ConfTrackSelection1 = ConfTrackSelection; using ConfTrackSelection2 = ConfTrackSelection; @@ -176,6 +181,9 @@ using ConfPionMinusSelection = ConfTrackSelection; using ConfKaonPlusSelection = ConfTrackSelection; using ConfKaonMinusSelection = ConfTrackSelection; +using ConfTrackSelectionDndetaGlobal = ConfTrackSelection; +using ConfTrackSelectionDndetaItsOnly = ConfTrackSelection; + /// enum for all track selections enum TrackSels { // track quality cuts @@ -188,6 +196,8 @@ enum TrackSels { kITSnClsIbMin, ///< Min. number of ITS clusters in the inner barrel kDCAxyMax, ///< Max. |DCA_xy| (cm) as a function of pT kDCAzMax, ///< Max. |DCA_z| (cm) as a function of pT + kITSchi2Max, ///< Max. ITS chi2 per cluster + kTPCchi2Max, ///< Max. TPC chi2 per cluster /// track pid cuts kItsElectron, ///< ITS Electon PID @@ -246,6 +256,8 @@ const std::unordered_map trackSelectionNames = { {kITSnClsIbMin, "Min. number of ITS clusters in the inner barrel"}, {kDCAxyMax, "Max. |DCA_xy| (cm) as a function of pT"}, {kDCAzMax, "Max. |DCA_z| (cm) as a function of pT"}, + {kITSchi2Max, "Max. ITS chi2 per cluster"}, + {kTPCchi2Max, "Max. TPC chi2 per cluster"}, {kItsElectron, "ITS Electron PID"}, {kItsPion, "ITS Pion PID"}, @@ -339,6 +351,8 @@ class TrackSelection : public baseselection::BaseSelectionaddSelection(kITSnClsIbMin, trackSelectionNames.at(kITSnClsIbMin), config.itsIbClustersMin.value, limits::kLowerLimit, true, true, false); this->addSelection(kDCAxyMax, trackSelectionNames.at(kDCAxyMax), filter.ptMin.value, filter.ptMax.value, config.dcaxyMax.value, limits::kAbsUpperFunctionLimit, true, true, false); this->addSelection(kDCAzMax, trackSelectionNames.at(kDCAzMax), filter.ptMin.value, filter.ptMax.value, config.dcazMax.value, limits::kAbsUpperFunctionLimit, true, true, false); + this->addSelection(kITSchi2Max, trackSelectionNames.at(kITSchi2Max), config.itsChi2Max.value, limits::kUpperLimit, true, true, false); + this->addSelection(kTPCchi2Max, trackSelectionNames.at(kTPCchi2Max), config.tpcChi2Max.value, limits::kUpperLimit, true, true, false); // add selections for Electron pid this->addSelection(kItsElectron, trackSelectionNames.at(kItsElectron), config.itsElectron.value, false, false, config.requirePidElectron); @@ -484,6 +498,8 @@ class TrackSelection : public baseselection::BaseSelectionevaluateObservable(kDCAxyMax, Track.dcaXY()); this->updateLimits(kDCAzMax, Track.pt()); this->evaluateObservable(kDCAzMax, Track.dcaZ()); + this->evaluateObservable(kITSchi2Max, Track.itsChi2NCl()); + this->evaluateObservable(kTPCchi2Max, Track.tpcChi2NCl()); // first pass: threshold-aware PID evaluation // determines if the track passes any optional selection and if should be stored in the first place @@ -684,7 +700,13 @@ class TrackBuilder track.tpcNClsFound(), track.tpcNClsCrossedRows(), track.tpcNClsShared(), - track.beta()); + track.beta(), + track.tpcChi2NCl(), + track.detectorMap(), + // fillType distinguishes selected tracks (kTrack) from rows that only exist to resolve a daughter index. + // A daughter whose collisionId differs from the one of its mother candidate is written a second time + // under the mother's collision, so downstream counting must skip isDaughterOnly() rows. + static_cast(type)); } if (mProduceElectronPids) { float itsEl = 0.f; @@ -805,6 +827,7 @@ class TrackBuilder [[nodiscard]] bool isPassThrough() const { return mTrackSelection.isPassThrough(); } [[nodiscard]] bool producingTracks() const { return mProduceTracks; } [[nodiscard]] bool producingLiteTracks() const { return mProduceLiteTracks; } + [[nodiscard]] bool producingTrackExtras() const { return mProduceTrackExtras; } template void reset(T const& tracks) diff --git a/PWGCF/Femto/Core/trackHistManager.h b/PWGCF/Femto/Core/trackHistManager.h index 2c831f20962..1a4312a8195 100644 --- a/PWGCF/Femto/Core/trackHistManager.h +++ b/PWGCF/Femto/Core/trackHistManager.h @@ -55,6 +55,9 @@ enum TrackHist { kTpcClusterOverCrossedRows, kTpcClusterShared, kTpcClusterFractionShared, + kItsChi2, + kTpcChi2, + kTrackType, // 2d qa kPtVsEta, kPtVsPhi, @@ -65,6 +68,8 @@ enum TrackHist { kPtVsTpcClusterOverCrossedRows, kPtVsTpcClusterShared, kPtVsTpcClusterFractionShared, + kPtVsItsChi2, + kPtVsTpcChi2, kTpcClusterVsTpcCrossedRows, kTpcClusterVsTpcClusterShared, kPtVsDcaxy, @@ -193,6 +198,11 @@ using ConfLc1PionDauBinning = ConfTrackBinning; using ConfCascadePosDauBinning = ConfTrackBinning; using ConfCascadeNegDauBinning = ConfTrackBinning; using ConfCascadeBachelorBinning = ConfTrackBinning; +// for dN/deta tracks +constexpr const char PrefixDndetaTrackGlobalBinning[] = "DndetaTrackGlobalBinning"; +constexpr const char PrefixDndetaTrackItsOnlyBinning[] = "DndetaTrackItsOnlyBinning"; +using ConfDndetaTrackGlobalBinning = ConfTrackBinning; +using ConfDndetaTrackItsOnlyBinning = ConfTrackBinning; using ConfKinkChaDauBinning = ConfTrackBinning; // for resonance daughters using ConfResonancePosDauBinning = ConfTrackBinning; @@ -224,6 +234,8 @@ struct ConfTrackQaBinning : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis tpcClusterOverCrossedRows{"tpcClusterOverCrossedRows", {{75, 0, 1.5}}, "TPC cluster over TPC crossed rows"}; o2::framework::ConfigurableAxis tpcClusterShared{"tpcClusterShared", {{161, -0.5, 160.5}}, "TPC cluster shared"}; o2::framework::ConfigurableAxis tpcClusterFractionShared{"tpcClusterFractionShared", {{60, 0, 1.2}}, "TPC cluster fraction shared"}; + o2::framework::ConfigurableAxis itsChi2{"itsChi2", {{100, 0, 50}}, "ITS chi2 / cluster"}; + o2::framework::ConfigurableAxis tpcChi2{"tpcChi2", {{100, 0, 10}}, "TPC chi2 / cluster"}; o2::framework::ConfigurableAxis dcaXy{"dcaXy", {{300, -0.3, 0.3}}, "DCA_xy"}; o2::framework::ConfigurableAxis dcaZ{"dcaZ", {{300, -0.3, 0.3}}, "DCA_Z"}; o2::framework::ConfigurableAxis dca{"dca", {{300, 0, 0.3}}, "DCA"}; @@ -289,6 +301,11 @@ constexpr const char PrefixKinkChaDauQaBinning[] = "KinkChaDauQaBinning"; using ConfTrackQaBinning1 = ConfTrackQaBinning; using ConfTrackQaBinning2 = ConfTrackQaBinning; +// for dN/deta tracks +constexpr const char PrefixDndetaTrackGlobalQaBinning[] = "DndetaTrackGlobalQaBinning"; +constexpr const char PrefixDndetaTrackItsOnlyQaBinning[] = "DndetaTrackItsOnlyQaBinning"; +using ConfDndetaTrackGlobalQaBinning = ConfTrackQaBinning; +using ConfDndetaTrackItsOnlyQaBinning = ConfTrackQaBinning; using ConfResonancePosDauQaBinning = ConfTrackQaBinning; using ConfResonanceNegDauQaBinning = ConfTrackQaBinning; using ConfV01PosDauQaBinning = ConfTrackQaBinning; @@ -323,7 +340,10 @@ constexpr std::array, kTrackHistLast> {kTpcCluster, o2::framework::HistType::kTH1F, "hTpcCluster", "TPC cluster found; TPC cluster found; Entries"}, {kTpcClusterOverCrossedRows, o2::framework::HistType::kTH1F, "hTpcClusterOverCrossedRows", "TPC cluster found over TPC crossed rows; TPC cluster found / Tpc crossed rows; Entries"}, {kTpcClusterShared, o2::framework::HistType::kTH1F, "hTpcClusterShared", "TPC cluster shared; TPC cluster shared ; Entries"}, - {kTpcClusterFractionShared, o2::framework::HistType::kTH1F, "hTpcClusterFractionShared", "TPC cluster fraction shared; TPC cluster found / TPC cluster shared ; Entries"}, + {kTpcClusterFractionShared, o2::framework::HistType::kTH1F, "hTpcClusterFractionShared", "TPC cluster fraction shared; TPC cluster shared / TPC cluster found ; Entries"}, + {kItsChi2, o2::framework::HistType::kTH1F, "hItsChi2", "ITS #chi^{2} / cluster; #chi^{2}_{ITS} / N_{cls}; Entries"}, + {kTpcChi2, o2::framework::HistType::kTH1F, "hTpcChi2", "TPC #chi^{2} / cluster; #chi^{2}_{TPC} / N_{cls}; Entries"}, + {kTrackType, o2::framework::HistType::kTH1F, "hTrackType", "Type the track row was stored as; Track type; Entries"}, {kPtVsEta, o2::framework::HistType::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}) ; #eta"}, {kPtVsPhi, o2::framework::HistType::kTH2F, "hPtVsPhi", "p_{T} vs #varphi; p_{T} (GeV/#it{c}) ; #varphi"}, {kPhiVsEta, o2::framework::HistType::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}, @@ -333,6 +353,8 @@ constexpr std::array, kTrackHistLast> {kPtVsTpcClusterOverCrossedRows, o2::framework::HistType::kTH2F, "hPtVsTpcClusterOverCrossedRows", "p_{T} vs TPC cluster found over crossed rows; p_{T} (GeV/#it{c}) ; TPC cluster found / TPC crossed rows"}, {kPtVsTpcClusterShared, o2::framework::HistType::kTH2F, "hPtVsTpcClusterShared", "p_{T} vs TPC cluster shared; p_{T} (GeV/#it{c}) ; TPC cluster shared"}, {kPtVsTpcClusterFractionShared, o2::framework::HistType::kTH2F, "hPtVsTpcClusterSharedFraction", "p_{T} vs TPC cluster shared over TPC cluster found; p_{T} (GeV/#it{c}) ; TPC cluster shared / TPC cluster found"}, + {kPtVsItsChi2, o2::framework::HistType::kTH2F, "hPtVsItsChi2", "p_{T} vs ITS #chi^{2} / cluster; p_{T} (GeV/#it{c}) ; #chi^{2}_{ITS} / N_{cls}"}, + {kPtVsTpcChi2, o2::framework::HistType::kTH2F, "hPtVsTpcChi2", "p_{T} vs TPC #chi^{2} / cluster; p_{T} (GeV/#it{c}) ; #chi^{2}_{TPC} / N_{cls}"}, {kTpcClusterVsTpcCrossedRows, o2::framework::HistType::kTH2F, "hTpcClusterVsTpcCrossedRows", "TPC cluster found vs TPC crossed rows; TPC cluster found; TPC crossed rows"}, {kTpcClusterVsTpcClusterShared, o2::framework::HistType::kTH2F, "hTpcClusterVsTpcClusterShared", "TPC cluster found vs TPC cluster shared; TPC cluster found; TPC cluster shared"}, {kPtVsDcaxy, o2::framework::HistType::kTH2F, "hPtVsDcaxy", "p_{T} vs DCA_{XY}; p_{T} (GeV/#it{c}); DCA_{XY} (cm)"}, @@ -341,7 +363,7 @@ constexpr std::array, kTrackHistLast> {kPtVsDcaxyVsDcaz, o2::framework::HistType::kTHnSparseF, "hPtVsDcaxyVsDcaz", "Transverse momentum vs DCA_{xy} vs DCA_{z}; p_{T} (GeV/#it{c}); DCA_{XY} (cm); DCA_{Z} (cm);"}, {kPVsPTpc, o2::framework::HistType::kTH2F, "hPVsPTpc", "Correlation p_{global} and p_{TPC}; p_{global} (GeV/#it{c}); p_{tpc} (GeV/#it{c})"}, {kItsSignal, o2::framework::HistType::kTH2F, "hItsSignal", "ITS Signal; p (GeV/#it{c}) ; x "}, - {kItsElectron, o2::framework::HistType::kTH2F, "hItsPidElectron", "ITS PID Electron; p (GeV/#it{c}) ; n#sigma_{TPC,el}"}, + {kItsElectron, o2::framework::HistType::kTH2F, "hItsPidElectron", "ITS PID Electron; p (GeV/#it{c}) ; n#sigma_{ITS,el}"}, {kItsPion, o2::framework::HistType::kTH2F, "hItsPidPion", "ITS PID Pion; p (GeV/#it{c}) ; n#sigma_{ITS,pi}"}, {kItsKaon, o2::framework::HistType::kTH2F, "hItsPidKaon", "ITS PID Kaon; p (GeV/#it{c}) ; n#sigma_{ITS,ka}"}, {kItsProton, o2::framework::HistType::kTH2F, "hItsPidProton", "ITS PID Proton; p (GeV/#it{c}) ; n#sigma_{ITS,pr}"}, @@ -365,14 +387,14 @@ constexpr std::array, kTrackHistLast> {kTofDeuteron, o2::framework::HistType::kTH2F, "hTofPidDeuteron", "TOF PID Deuteron; p (GeV/#it{c}) ; n#sigma_{TOF,de}"}, {kTofTriton, o2::framework::HistType::kTH2F, "hTofPidTriton", "TOF PID Triton; p (GeV/#it{c}) ; n#sigma_{TOF,tr}"}, {kTofHelium, o2::framework::HistType::kTH2F, "hTofPidHelium", "TOF PID Helium; p (GeV/#it{c}) ; n#sigma_{TOF,he}"}, - {kTpcitsElectron, o2::framework::HistType::kTH2F, "hTpcitsPidElectron", "its PID Electron; p (GeV/#it{c}) ; n#sigma_{its,el}"}, + {kTpcitsElectron, o2::framework::HistType::kTH2F, "hTpcitsPidElectron", "TPC+ITS PID Electron; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,el}^{2}+n#sigma_{ITS,el}^{2}}"}, {kTpcitsPion, o2::framework::HistType::kTH2F, "hTpcitsPidPion", "TPC+ITS PID Pion; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,pi}^{2}+n#sigma_{its,pi}^{2}}"}, {kTpcitsKaon, o2::framework::HistType::kTH2F, "hTpcitsPidKaon", "TPC+ITS PID Kaon; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,ka}^{2}+n#sigma_{its,ka}^{2}}"}, {kTpcitsProton, o2::framework::HistType::kTH2F, "hTpcitsPidProton", "TPC+ITS PID Proton; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,pr}^{2}+n#sigma_{its,pr}^{2}}"}, {kTpcitsDeuteron, o2::framework::HistType::kTH2F, "hTpcitsPidDeuteron", "TPC+ITS PID Deuteron; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,de}^{2}+n#sigma_{its,de}^{2}}"}, {kTpcitsTriton, o2::framework::HistType::kTH2F, "hTpcitsPidTriton", "TPC+ITS PID Triton; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,tr}^{2}+n#sigma_{its,tr}^{2}}"}, {kTpcitsHelium, o2::framework::HistType::kTH2F, "hTpcitsPidHelium", "TPC+ITS PID Helium; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,he}^{2}+n#sigma_{its,he}^{2}}"}, - {kTpctofElectron, o2::framework::HistType::kTH2F, "hTpctofPidElectron", "TOF PID Electron; p (GeV/#it{c}) ; n#sigma_{TOF,el}"}, + {kTpctofElectron, o2::framework::HistType::kTH2F, "hTpctofPidElectron", "TPC+TOF PID Electron; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,el}^{2}+n#sigma_{TOF,el}^{2}}"}, {kTpctofPion, o2::framework::HistType::kTH2F, "hTpctofPidPion", "TPC+TOF PID Pion; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,pi}^{2}+n#sigma_{TOF,pi}^{2}}"}, {kTpctofKaon, o2::framework::HistType::kTH2F, "hTpctofPidKaon", "TPC+TOF PID Kaon; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,ka}^{2}+n#sigma_{TOF,ka}^{2}}"}, {kTpctofProton, o2::framework::HistType::kTH2F, "hTpctofPidProton", "TPC+TOF PID Proton; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,pr}^{2}+n#sigma_{TOF,pr}^{2}}"}, @@ -420,6 +442,8 @@ constexpr std::array, kTrackHistLast> {kPtVsTpcClusterOverCrossedRows, {(confAnalysis).pt, (confQa).tpcClusterOverCrossedRows}}, \ {kPtVsTpcClusterShared, {(confAnalysis).pt, (confQa).tpcClusterShared}}, \ {kPtVsTpcClusterFractionShared, {(confAnalysis).pt, (confQa).tpcClusterFractionShared}}, \ + {kPtVsItsChi2, {(confAnalysis).pt, (confQa).itsChi2}}, \ + {kPtVsTpcChi2, {(confAnalysis).pt, (confQa).tpcChi2}}, \ {kTpcClusterVsTpcCrossedRows, {(confQa).tpcCluster, (confQa).tpcCrossedRows}}, \ {kTpcClusterVsTpcClusterShared, {(confQa).tpcCluster, (confQa).tpcClusterShared}}, \ {kTpcCrossedRows, {(confQa).tpcCrossedRows}}, \ @@ -427,6 +451,8 @@ constexpr std::array, kTrackHistLast> {kTpcClusterOverCrossedRows, {(confQa).tpcClusterOverCrossedRows}}, \ {kTpcClusterShared, {(confQa).tpcClusterShared}}, \ {kTpcClusterFractionShared, {(confQa).tpcClusterFractionShared}}, \ + {kItsChi2, {(confQa).itsChi2}}, \ + {kTpcChi2, {(confQa).tpcChi2}}, \ {kPtVsDcaxy, {(confAnalysis).pt, (confQa).dcaXy}}, \ {kPtVsDcaz, {(confAnalysis).pt, (confQa).dcaZ}}, \ {kPtVsDca, {(confAnalysis).pt, (confQa).dca}}, \ @@ -534,6 +560,9 @@ auto makeTrackMcQaHistSpecMap(T1 const& confBinningAnalysis, T2 const& confBinni constexpr char PrefixTrackQa[] = "TrackQA/"; constexpr char PrefixTrack1[] = "Track1/"; constexpr char PrefixTrack2[] = "Track2/"; +// for dN/deta tracks +constexpr char PrefixDndetaTrackGlobal[] = "DndetaTrackGlobal/"; +constexpr char PrefixDndetaTrackItsOnly[] = "DndetaTrackItsOnly/"; constexpr char PrefixTrack3[] = "Track3/"; constexpr char PrefixResonancePosDaughter[] = "ResonancePosDau/"; @@ -724,6 +753,16 @@ class TrackHistManager mHistogramRegistry->add(qaDir + getHistNameV2(kTpcClusterOverCrossedRows, HistTable), getHistDesc(kTpcClusterOverCrossedRows, HistTable), getHistType(kTpcClusterOverCrossedRows, HistTable), {Specs.at(kTpcClusterOverCrossedRows)}); mHistogramRegistry->add(qaDir + getHistNameV2(kTpcClusterShared, HistTable), getHistDesc(kTpcClusterShared, HistTable), getHistType(kTpcClusterShared, HistTable), {Specs.at(kTpcClusterShared)}); mHistogramRegistry->add(qaDir + getHistNameV2(kTpcClusterFractionShared, HistTable), getHistDesc(kTpcClusterFractionShared, HistTable), getHistType(kTpcClusterFractionShared, HistTable), {Specs.at(kTpcClusterFractionShared)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kItsChi2, HistTable), getHistDesc(kItsChi2, HistTable), getHistType(kItsChi2, HistTable), {Specs.at(kItsChi2)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kTpcChi2, HistTable), getHistDesc(kTpcChi2, HistTable), getHistType(kTpcChi2, HistTable), {Specs.at(kTpcChi2)}); + + // track type: shows e.g. whether rows were only stored as daughters of a candidate + const int nTrackTypes = static_cast(modes::Track::kTrackTypeLast); + const o2::framework::AxisSpec axisTrackType = {nTrackTypes, -0.5, static_cast(nTrackTypes) - 0.5}; + mHistogramRegistry->add(qaDir + getHistNameV2(kTrackType, HistTable), getHistDesc(kTrackType, HistTable), getHistType(kTrackType, HistTable), {axisTrackType}); + for (int i = 0; i < nTrackTypes; i++) { + mHistogramRegistry->get(HIST(prefix) + HIST(QaDir) + HIST(histmanager::getHistName(kTrackType, HistTable)))->GetXaxis()->SetBinLabel(1 + i, modes::trackTypeToString(static_cast(i))); + } // qa 2d if (mPlot2d) { @@ -736,6 +775,8 @@ class TrackHistManager mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsTpcClusterOverCrossedRows, HistTable), getHistDesc(kPtVsTpcClusterOverCrossedRows, HistTable), getHistType(kPtVsTpcClusterOverCrossedRows, HistTable), {Specs.at(kPtVsTpcClusterOverCrossedRows)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsTpcClusterShared, HistTable), getHistDesc(kPtVsTpcClusterShared, HistTable), getHistType(kPtVsTpcClusterShared, HistTable), {Specs.at(kPtVsTpcClusterShared)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsTpcClusterFractionShared, HistTable), getHistDesc(kPtVsTpcClusterFractionShared, HistTable), getHistType(kPtVsTpcClusterFractionShared, HistTable), {Specs.at(kPtVsTpcClusterFractionShared)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsItsChi2, HistTable), getHistDesc(kPtVsItsChi2, HistTable), getHistType(kPtVsItsChi2, HistTable), {Specs.at(kPtVsItsChi2)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsTpcChi2, HistTable), getHistDesc(kPtVsTpcChi2, HistTable), getHistType(kPtVsTpcChi2, HistTable), {Specs.at(kPtVsTpcChi2)}); mHistogramRegistry->add(qaDir + getHistNameV2(kTpcClusterVsTpcCrossedRows, HistTable), getHistDesc(kTpcClusterVsTpcCrossedRows, HistTable), getHistType(kTpcClusterVsTpcCrossedRows, HistTable), {Specs.at(kTpcClusterVsTpcCrossedRows)}); mHistogramRegistry->add(qaDir + getHistNameV2(kTpcClusterVsTpcClusterShared, HistTable), getHistDesc(kTpcClusterVsTpcClusterShared, HistTable), getHistType(kTpcClusterVsTpcClusterShared, HistTable), {Specs.at(kTpcClusterVsTpcClusterShared)}); // dca @@ -751,66 +792,80 @@ class TrackHistManager std::string pidDir = std::string(prefix) + std::string(PidDir); - mHistogramRegistry->add(pidDir + getHistNameV2(kItsSignal, HistTable), getHistDesc(kItsSignal, HistTable), getHistType(kItsSignal, HistTable), {Specs.at(kItsSignal)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcSignal, HistTable), getHistDesc(kTpcSignal, HistTable), getHistType(kTpcSignal, HistTable), {Specs.at(kTpcSignal)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTofBeta, HistTable), getHistDesc(kTofBeta, HistTable), getHistType(kTofBeta, HistTable), {Specs.at(kTofBeta)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTofMass, HistTable), getHistDesc(kTofMass, HistTable), getHistType(kTofMass, HistTable), {Specs.at(kTofMass)}); + this->addPidHistogram(pidDir, kItsSignal, Specs); + this->addPidHistogram(pidDir, kTpcSignal, Specs); + this->addPidHistogram(pidDir, kTofBeta, Specs); + this->addPidHistogram(pidDir, kTofMass, Specs); if (mPlotElectronPid) { - mHistogramRegistry->add(pidDir + getHistNameV2(kItsElectron, HistTable), getHistDesc(kItsElectron, HistTable), getHistType(kItsElectron, HistTable), {Specs.at(kItsElectron)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcElectron, HistTable), getHistDesc(kTpcElectron, HistTable), getHistType(kTpcElectron, HistTable), {Specs.at(kTpcElectron)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTofElectron, HistTable), getHistDesc(kTofElectron, HistTable), getHistType(kTofElectron, HistTable), {Specs.at(kTofElectron)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcitsElectron, HistTable), getHistDesc(kTpcitsElectron, HistTable), getHistType(kTpcitsElectron, HistTable), {Specs.at(kTpcitsElectron)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpctofElectron, HistTable), getHistDesc(kTpctofElectron, HistTable), getHistType(kTpctofElectron, HistTable), {Specs.at(kTpctofElectron)}); + this->addPidHistogram(pidDir, kItsElectron, Specs); + this->addPidHistogram(pidDir, kTpcElectron, Specs); + this->addPidHistogram(pidDir, kTofElectron, Specs); + this->addPidHistogram(pidDir, kTpcitsElectron, Specs); + this->addPidHistogram(pidDir, kTpctofElectron, Specs); } if (mPlotPionPid) { - mHistogramRegistry->add(pidDir + getHistNameV2(kItsPion, HistTable), getHistDesc(kItsPion, HistTable), getHistType(kItsPion, HistTable), {Specs.at(kItsPion)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcPion, HistTable), getHistDesc(kTpcPion, HistTable), getHistType(kTpcPion, HistTable), {Specs.at(kTpcPion)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTofPion, HistTable), getHistDesc(kTofPion, HistTable), getHistType(kTofPion, HistTable), {Specs.at(kTofPion)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcitsPion, HistTable), getHistDesc(kTpcitsPion, HistTable), getHistType(kTpcitsPion, HistTable), {Specs.at(kTpcitsPion)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpctofPion, HistTable), getHistDesc(kTpctofPion, HistTable), getHistType(kTpctofPion, HistTable), {Specs.at(kTpctofPion)}); + this->addPidHistogram(pidDir, kItsPion, Specs); + this->addPidHistogram(pidDir, kTpcPion, Specs); + this->addPidHistogram(pidDir, kTofPion, Specs); + this->addPidHistogram(pidDir, kTpcitsPion, Specs); + this->addPidHistogram(pidDir, kTpctofPion, Specs); } if (mPlotKaonPid) { - mHistogramRegistry->add(pidDir + getHistNameV2(kItsKaon, HistTable), getHistDesc(kItsKaon, HistTable), getHistType(kItsKaon, HistTable), {Specs.at(kItsKaon)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcKaon, HistTable), getHistDesc(kTpcKaon, HistTable), getHistType(kTpcKaon, HistTable), {Specs.at(kTpcKaon)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTofKaon, HistTable), getHistDesc(kTofKaon, HistTable), getHistType(kTofKaon, HistTable), {Specs.at(kTofKaon)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcitsKaon, HistTable), getHistDesc(kTpcitsKaon, HistTable), getHistType(kTpcitsKaon, HistTable), {Specs.at(kTpcitsKaon)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpctofKaon, HistTable), getHistDesc(kTpctofKaon, HistTable), getHistType(kTpctofKaon, HistTable), {Specs.at(kTpctofKaon)}); + this->addPidHistogram(pidDir, kItsKaon, Specs); + this->addPidHistogram(pidDir, kTpcKaon, Specs); + this->addPidHistogram(pidDir, kTofKaon, Specs); + this->addPidHistogram(pidDir, kTpcitsKaon, Specs); + this->addPidHistogram(pidDir, kTpctofKaon, Specs); } if (mPlotProtonPid) { - mHistogramRegistry->add(pidDir + getHistNameV2(kItsProton, HistTable), getHistDesc(kItsProton, HistTable), getHistType(kItsProton, HistTable), {Specs.at(kItsProton)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcProton, HistTable), getHistDesc(kTpcProton, HistTable), getHistType(kTpcProton, HistTable), {Specs.at(kTpcProton)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTofProton, HistTable), getHistDesc(kTofProton, HistTable), getHistType(kTofProton, HistTable), {Specs.at(kTofProton)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcitsProton, HistTable), getHistDesc(kTpcitsProton, HistTable), getHistType(kTpcitsProton, HistTable), {Specs.at(kTpcitsProton)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpctofProton, HistTable), getHistDesc(kTpctofProton, HistTable), getHistType(kTpctofProton, HistTable), {Specs.at(kTpctofProton)}); + this->addPidHistogram(pidDir, kItsProton, Specs); + this->addPidHistogram(pidDir, kTpcProton, Specs); + this->addPidHistogram(pidDir, kTofProton, Specs); + this->addPidHistogram(pidDir, kTpcitsProton, Specs); + this->addPidHistogram(pidDir, kTpctofProton, Specs); } if (mPlotDeuteronPid) { - mHistogramRegistry->add(pidDir + getHistNameV2(kItsDeuteron, HistTable), getHistDesc(kItsDeuteron, HistTable), getHistType(kItsDeuteron, HistTable), {Specs.at(kItsDeuteron)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcDeuteron, HistTable), getHistDesc(kTpcDeuteron, HistTable), getHistType(kTpcDeuteron, HistTable), {Specs.at(kTpcDeuteron)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTofDeuteron, HistTable), getHistDesc(kTofDeuteron, HistTable), getHistType(kTofDeuteron, HistTable), {Specs.at(kTofDeuteron)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcitsDeuteron, HistTable), getHistDesc(kTpcitsDeuteron, HistTable), getHistType(kTpcitsDeuteron, HistTable), {Specs.at(kTpcitsDeuteron)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpctofDeuteron, HistTable), getHistDesc(kTpctofDeuteron, HistTable), getHistType(kTpctofDeuteron, HistTable), {Specs.at(kTpctofDeuteron)}); + this->addPidHistogram(pidDir, kItsDeuteron, Specs); + this->addPidHistogram(pidDir, kTpcDeuteron, Specs); + this->addPidHistogram(pidDir, kTofDeuteron, Specs); + this->addPidHistogram(pidDir, kTpcitsDeuteron, Specs); + this->addPidHistogram(pidDir, kTpctofDeuteron, Specs); } if (mPlotTritonPid) { - mHistogramRegistry->add(pidDir + getHistNameV2(kItsTriton, HistTable), getHistDesc(kItsTriton, HistTable), getHistType(kItsTriton, HistTable), {Specs.at(kItsTriton)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcTriton, HistTable), getHistDesc(kTpcTriton, HistTable), getHistType(kTpcTriton, HistTable), {Specs.at(kTpcTriton)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTofTriton, HistTable), getHistDesc(kTofTriton, HistTable), getHistType(kTofTriton, HistTable), {Specs.at(kTofTriton)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcitsTriton, HistTable), getHistDesc(kTpcitsTriton, HistTable), getHistType(kTpcitsTriton, HistTable), {Specs.at(kTpcitsTriton)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpctofTriton, HistTable), getHistDesc(kTpctofTriton, HistTable), getHistType(kTpctofTriton, HistTable), {Specs.at(kTpctofTriton)}); + this->addPidHistogram(pidDir, kItsTriton, Specs); + this->addPidHistogram(pidDir, kTpcTriton, Specs); + this->addPidHistogram(pidDir, kTofTriton, Specs); + this->addPidHistogram(pidDir, kTpcitsTriton, Specs); + this->addPidHistogram(pidDir, kTpctofTriton, Specs); } if (mPlotHeliumPid) { - mHistogramRegistry->add(pidDir + getHistNameV2(kItsHelium, HistTable), getHistDesc(kItsHelium, HistTable), getHistType(kItsHelium, HistTable), {Specs.at(kItsHelium)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcHelium, HistTable), getHistDesc(kTpcHelium, HistTable), getHistType(kTpcHelium, HistTable), {Specs.at(kTpcHelium)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTofHelium, HistTable), getHistDesc(kTofHelium, HistTable), getHistType(kTofHelium, HistTable), {Specs.at(kTofHelium)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcitsHelium, HistTable), getHistDesc(kTpcitsHelium, HistTable), getHistType(kTpcitsHelium, HistTable), {Specs.at(kTpcitsHelium)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpctofHelium, HistTable), getHistDesc(kTpctofHelium, HistTable), getHistType(kTpctofHelium, HistTable), {Specs.at(kTpctofHelium)}); + this->addPidHistogram(pidDir, kItsHelium, Specs); + this->addPidHistogram(pidDir, kTpcHelium, Specs); + this->addPidHistogram(pidDir, kTofHelium, Specs); + this->addPidHistogram(pidDir, kTpcitsHelium, Specs); + this->addPidHistogram(pidDir, kTpctofHelium, Specs); + } + } + + /// add a PID histogram, replacing the x-axis title of the table description + /// with the one matching the configured momentum type + void addPidHistogram(std::string const& pidDir, TrackHist hist, std::map> const& Specs) + { + // descriptions have the form "title; x axis ; y axis" + std::string desc(getHistDesc(hist, HistTable)); + const auto first = desc.find(';'); + const auto second = (first == std::string::npos) ? std::string::npos : desc.find(';', first + 1); + if (second != std::string::npos) { + desc.replace(first + 1, second - first - 1, " " + std::string(modes::momentumTypeToAxisTitle(mMomentumType)) + " "); } + mHistogramRegistry->add(pidDir + getHistNameV2(hist, HistTable), desc.c_str(), getHistType(hist, HistTable), {Specs.at(hist)}); } void initMc(std::map> const& Specs) @@ -880,6 +935,9 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kTpcClusterOverCrossedRows, HistTable)), static_cast(track.tpcNClsFound()) / static_cast(track.tpcNClsCrossedRows())); mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kTpcClusterShared, HistTable)), static_cast(track.tpcNClsShared())); mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kTpcClusterFractionShared, HistTable)), track.tpcSharedOverFound()); + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kItsChi2, HistTable)), track.itsChi2NCl()); + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kTpcChi2, HistTable)), track.tpcChi2NCl()); + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kTrackType, HistTable)), static_cast(track.fillType())); if (mPlot2d) { mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPtVsEta, HistTable)), mAbsCharge * track.pt(), track.eta()); @@ -891,6 +949,8 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPtVsTpcClusterOverCrossedRows, HistTable)), mAbsCharge * track.pt(), static_cast(track.tpcNClsFound()) / static_cast(track.tpcNClsCrossedRows())); mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPtVsTpcClusterShared, HistTable)), mAbsCharge * track.pt(), static_cast(track.tpcNClsShared())); mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPtVsTpcClusterFractionShared, HistTable)), mAbsCharge * track.pt(), static_cast(track.tpcNClsShared()) / static_cast(track.tpcNClsFound())); + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPtVsItsChi2, HistTable)), mAbsCharge * track.pt(), track.itsChi2NCl()); + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPtVsTpcChi2, HistTable)), mAbsCharge * track.pt(), track.tpcChi2NCl()); mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kTpcClusterVsTpcCrossedRows, HistTable)), static_cast(track.tpcNClsFound()), static_cast(track.tpcNClsCrossedRows())); mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kTpcClusterVsTpcClusterShared, HistTable)), static_cast(track.tpcNClsFound()), static_cast(track.tpcNClsShared())); mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPtVsDcaxy, HistTable)), mAbsCharge * track.pt(), track.dcaXY()); diff --git a/PWGCF/Femto/Core/tripletBuilder.h b/PWGCF/Femto/Core/tripletBuilder.h index 914c9bafdc3..048b60abd94 100644 --- a/PWGCF/Femto/Core/tripletBuilder.h +++ b/PWGCF/Femto/Core/tripletBuilder.h @@ -185,85 +185,90 @@ class TripletTrackTrackTrackBuilder } // data + /// \return true if at least one triplet passed all triplet selections (usable as triplet trigger) template - void processSameEvent(T1 const& col, T2& trackTable, T3& partition1, T4& partition2, T5& partition3, T6& cache) + bool processSameEvent(T1 const& col, T2& trackTable, T3& partition1, T4& partition2, T5& partition3, T6& cache) { tripletprocesshelpers::TripletOrder tripletOrder = tripletprocesshelpers::kOrder123; if (mTrack1Track2Track3AreSameSpecies) { auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice1.size() < nLimitPartitionIdenticalParticles123) { - return; + return false; } mColHistManager.template fill(col); mCtrSe.setMagField(col.magField()); if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - tripletprocesshelpers::processSameEvent(trackSlice1, trackTable, col, mTrackHistManager1, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); - } else if (mTrack1Track2AreSameSpecies) { + return tripletprocesshelpers::processSameEvent(trackSlice1, trackTable, col, mTrackHistManager1, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); + } + + if (mTrack1Track2AreSameSpecies) { auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto trackSlice3 = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice1.size() < nLimitPartitionIdenticalParticles12 || trackSlice3.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col); mCtrSe.setMagField(col.magField()); if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice3, trackTable, col, mTrackHistManager1, mTrackHistManager3, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); - } else { - auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto trackSlice3 = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles || trackSlice3.size() < nLimitPartitionParticles) { - return; - } - mColHistManager.template fill(col); - mCtrSe.setMagField(col.magField()); - tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackSlice3, trackTable, col, mTrackHistManager1, mTrackHistManager2, mTrackHistManager3, mTripletHistManagerSe, mCtrSe, mTcSe); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice3, trackTable, col, mTrackHistManager1, mTrackHistManager3, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); } + + auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto trackSlice3 = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles || trackSlice3.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(col); + mCtrSe.setMagField(col.magField()); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackSlice3, trackTable, col, mTrackHistManager1, mTrackHistManager2, mTrackHistManager3, mTripletHistManagerSe, mCtrSe, mTcSe); } // mc + /// \return true if at least one triplet passed all triplet selections (usable as triplet trigger) template - void processSameEvent(T1 const& col, T2 const& mcCols, T3& trackTable, T4& partition1, T5& partition2, T6& partition3, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) + bool processSameEvent(T1 const& col, T2 const& mcCols, T3& trackTable, T4& partition1, T5& partition2, T6& partition3, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) { tripletprocesshelpers::TripletOrder tripletOrder = tripletprocesshelpers::kOrder123; if (mTrack1Track2Track3AreSameSpecies) { auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice1.size() < nLimitPartitionIdenticalParticles123) { - return; + return false; } mColHistManager.template fill(col, mcCols); mCtrSe.setMagField(col.magField()); if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - tripletprocesshelpers::processSameEvent(trackSlice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTripletHistManagerSe, mCleaner1, mCtrSe, mTcSe, tripletOrder); - } else if (mTrack1Track2AreSameSpecies) { + return tripletprocesshelpers::processSameEvent(trackSlice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTripletHistManagerSe, mCleaner1, mCtrSe, mTcSe, tripletOrder); + } + if (mTrack1Track2AreSameSpecies) { auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto trackSlice3 = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice1.size() < nLimitPartitionIdenticalParticles12 || trackSlice3.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col, mcCols); mCtrSe.setMagField(col.magField()); if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice3, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTrackHistManager3, mTripletHistManagerSe, mCleaner1, mCleaner3, mCtrSe, mTcSe, tripletOrder); - } else { - auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto trackSlice3 = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles || trackSlice3.size() < nLimitPartitionParticles) { - return; - } - mColHistManager.template fill(col, mcCols); - mCtrSe.setMagField(col.magField()); - tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackSlice3, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTrackHistManager2, mTrackHistManager3, mTripletHistManagerSe, mCleaner1, mCleaner2, mCleaner3, mCtrSe, mTcSe); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice3, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTrackHistManager3, mTripletHistManagerSe, mCleaner1, mCleaner3, mCtrSe, mTcSe, tripletOrder); } + + auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto trackSlice3 = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles || trackSlice3.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(col, mcCols); + mCtrSe.setMagField(col.magField()); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackSlice3, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTrackHistManager2, mTrackHistManager3, mTripletHistManagerSe, mCleaner1, mCleaner2, mCleaner3, mCtrSe, mTcSe); } // data @@ -513,63 +518,65 @@ class TripletTrackTrackV0Builder } // data + /// \return true if at least one triplet passed all triplet selections (usable as triplet trigger) template - void processSameEvent(T1 const& col, T2& trackTable, T3& partition1, T4& partition2, T5& partition3, T6& cache) + bool processSameEvent(T1 const& col, T2& trackTable, T3& partition1, T4& partition2, T5& partition3, T6& cache) { tripletprocesshelpers::TripletOrder tripletOrder = tripletprocesshelpers::kOrder123; if (mTrack1Track2AreSameSpecies) { auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto v0Slice = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice1.size() < nLimitPartitionIdenticalParticles12 || v0Slice.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col); mCtrSe.setMagField(col.magField()); if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - tripletprocesshelpers::processSameEvent(trackSlice1, v0Slice, trackTable, col, mTrackHistManager1, mV0HistManager, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); - } else { - auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto v0Slice = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles || v0Slice.size() < nLimitPartitionParticles) { - return; - } - mColHistManager.template fill(col); - mCtrSe.setMagField(col.magField()); - tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, v0Slice, trackTable, col, mTrackHistManager1, mTrackHistManager2, mV0HistManager, mTripletHistManagerSe, mCtrSe, mTcSe); + return tripletprocesshelpers::processSameEvent(trackSlice1, v0Slice, trackTable, col, mTrackHistManager1, mV0HistManager, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); + } + + auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto v0Slice = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles || v0Slice.size() < nLimitPartitionParticles) { + return false; } + mColHistManager.template fill(col); + mCtrSe.setMagField(col.magField()); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, v0Slice, trackTable, col, mTrackHistManager1, mTrackHistManager2, mV0HistManager, mTripletHistManagerSe, mCtrSe, mTcSe); } // mc + /// \return true if at least one triplet passed all triplet selections (usable as triplet trigger) template - void processSameEvent(T1 const& col, T2 const& mcCols, T3& trackTable, T4& partition1, T5& partition2, T6& partition3, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) + bool processSameEvent(T1 const& col, T2 const& mcCols, T3& trackTable, T4& partition1, T5& partition2, T6& partition3, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) { tripletprocesshelpers::TripletOrder tripletOrder = tripletprocesshelpers::kOrder123; if (mTrack1Track2AreSameSpecies) { auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto v0Slice = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice1.size() < nLimitPartitionIdenticalParticles12 || v0Slice.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col, mcCols); mCtrSe.setMagField(col.magField()); if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - tripletprocesshelpers::processSameEvent(trackSlice1, v0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mV0HistManager, mTripletHistManagerSe, mCleaner1, mV0Cleaner, mCtrSe, mTcSe, tripletOrder); - } else { - auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto v0Slice = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles || v0Slice.size() < nLimitPartitionParticles) { - return; - } - mColHistManager.template fill(col, mcCols); - mCtrSe.setMagField(col.magField()); - tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, v0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTrackHistManager2, mV0HistManager, mTripletHistManagerSe, mCleaner1, mCleaner2, mV0Cleaner, mCtrSe, mTcSe); + return tripletprocesshelpers::processSameEvent(trackSlice1, v0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mV0HistManager, mTripletHistManagerSe, mCleaner1, mV0Cleaner, mCtrSe, mTcSe, tripletOrder); + } + + auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto v0Slice = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles || v0Slice.size() < nLimitPartitionParticles) { + return false; } + mColHistManager.template fill(col, mcCols); + mCtrSe.setMagField(col.magField()); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, v0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTrackHistManager2, mV0HistManager, mTripletHistManagerSe, mCleaner1, mCleaner2, mV0Cleaner, mCtrSe, mTcSe); } // data @@ -801,63 +808,65 @@ class TripletTrackTrackCascadeBuilder } // data + /// \return true if at least one triplet passed all triplet selections (usable as triplet trigger) template - void processSameEvent(T1 const& col, T2& trackTable, T3& partition1, T4& partition2, T5& partition3, T6& cache) + bool processSameEvent(T1 const& col, T2& trackTable, T3& partition1, T4& partition2, T5& partition3, T6& cache) { tripletprocesshelpers::TripletOrder tripletOrder = tripletprocesshelpers::kOrder123; if (mTrack1Track2AreSameSpecies) { auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto cascadeSlice = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice1.size() < nLimitPartitionIdenticalParticles12 || cascadeSlice.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col); mCtrSe.setMagField(col.magField()); if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - tripletprocesshelpers::processSameEvent(trackSlice1, cascadeSlice, trackTable, col, mTrackHistManager1, mCascadeHistManager, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); - } else { - auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto cascadeSlice = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles || cascadeSlice.size() < nLimitPartitionParticles) { - return; - } - mColHistManager.template fill(col); - mCtrSe.setMagField(col.magField()); - tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, cascadeSlice, trackTable, col, mTrackHistManager1, mTrackHistManager2, mCascadeHistManager, mTripletHistManagerSe, mCtrSe, mTcSe); + return tripletprocesshelpers::processSameEvent(trackSlice1, cascadeSlice, trackTable, col, mTrackHistManager1, mCascadeHistManager, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); } + + auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto cascadeSlice = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles || cascadeSlice.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(col); + mCtrSe.setMagField(col.magField()); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, cascadeSlice, trackTable, col, mTrackHistManager1, mTrackHistManager2, mCascadeHistManager, mTripletHistManagerSe, mCtrSe, mTcSe); } // mc + /// \return true if at least one triplet passed all triplet selections (usable as triplet trigger) template - void processSameEvent(T1 const& col, T2 const& mcCols, T3& trackTable, T4& partition1, T5& partition2, T6& partition3, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) + bool processSameEvent(T1 const& col, T2 const& mcCols, T3& trackTable, T4& partition1, T5& partition2, T6& partition3, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) { tripletprocesshelpers::TripletOrder tripletOrder = tripletprocesshelpers::kOrder123; if (mTrack1Track2AreSameSpecies) { auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto cascadeSlice = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice1.size() < nLimitPartitionIdenticalParticles12 || cascadeSlice.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col, mcCols); mCtrSe.setMagField(col.magField()); if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - tripletprocesshelpers::processSameEvent(trackSlice1, cascadeSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mCascadeHistManager, mTripletHistManagerSe, mTrackCleaner1, mCascadeCleaner, mCtrSe, mTcSe, tripletOrder); - } else { - auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto cascadeSlice = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles || cascadeSlice.size() < nLimitPartitionParticles) { - return; - } - mColHistManager.template fill(col, mcCols); - mCtrSe.setMagField(col.magField()); - tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, cascadeSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTrackHistManager2, mCascadeHistManager, mTripletHistManagerSe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrSe, mTcSe); + return tripletprocesshelpers::processSameEvent(trackSlice1, cascadeSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mCascadeHistManager, mTripletHistManagerSe, mTrackCleaner1, mCascadeCleaner, mCtrSe, mTcSe, tripletOrder); + } + + auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto cascadeSlice = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles || cascadeSlice.size() < nLimitPartitionParticles) { + return false; } + mColHistManager.template fill(col, mcCols); + mCtrSe.setMagField(col.magField()); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, cascadeSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTrackHistManager2, mCascadeHistManager, mTripletHistManagerSe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrSe, mTcSe); } // data diff --git a/PWGCF/Femto/Core/tripletProcessHelpers.h b/PWGCF/Femto/Core/tripletProcessHelpers.h index c277fb80e57..41a73f57096 100644 --- a/PWGCF/Femto/Core/tripletProcessHelpers.h +++ b/PWGCF/Femto/Core/tripletProcessHelpers.h @@ -27,6 +27,10 @@ namespace o2::analysis::femto::tripletprocesshelpers { +// NOTE: all processSameEvent helpers return true if at least one triplet in the event passed +// the triplet cleaner, the close triplet rejection and the triplet cuts. +// This allows to use them as a triplet trigger. + enum TripletOrder : uint8_t { kOrder123, // no swap kOrder213, // swap 1&2: for the case that particle 1 & 2 are the same species, particle 3 is something else @@ -43,7 +47,7 @@ template -void processSameEvent(T1 const& SliceParticle, +bool processSameEvent(T1 const& SliceParticle, T2 const& TrackTable, T3 const& Collision, T4& ParticleHistManager, @@ -53,6 +57,7 @@ void processSameEvent(T1 const& SliceParticle, TripletOrder tripletOrder) { TripletHistManager.resetTrackedParticlesPerEvent(); + bool foundTriplet = false; for (auto const& part : SliceParticle) { ParticleHistManager.template fill(part, TrackTable); @@ -93,17 +98,19 @@ void processSameEvent(T1 const& SliceParticle, if (TripletHistManager.checkTripletCuts()) { TripletHistManager.template fill(); TripletHistManager.trackParticlesPerEvent(p1, p2, p3); + foundTriplet = true; } } TripletHistManager.fillMixingQaSe(); + return foundTriplet; } // process same event for identical 2 particles and 1 other particle template -void processSameEvent(T1 const& SliceParticle1, // 1&2 have same species +bool processSameEvent(T1 const& SliceParticle1, // 1&2 have same species T2 const& SliceParticle3, T3 const& TrackTable, T4 const& Collision, @@ -115,6 +122,7 @@ void processSameEvent(T1 const& SliceParticle1, // 1&2 have same species TripletOrder tripletOrder) { TripletHistManager.resetTrackedParticlesPerEvent(); + bool foundTriplet = false; for (auto const& part : SliceParticle1) { ParticleHistManager1.template fill(part, TrackTable); @@ -152,11 +160,13 @@ void processSameEvent(T1 const& SliceParticle1, // 1&2 have same species if (TripletHistManager.checkTripletCuts()) { TripletHistManager.template fill(); TripletHistManager.trackParticlesPerEvent(p1, p2, p3); + foundTriplet = true; } } } TripletHistManager.fillMixingQaSe(); + return foundTriplet; } // process same event for 3 different particles @@ -172,7 +182,7 @@ template -void processSameEvent(T1 const& SliceParticle1, +bool processSameEvent(T1 const& SliceParticle1, T2 const& SliceParticle2, T3 const& SliceParticle3, T4 const& TrackTable, @@ -185,6 +195,7 @@ void processSameEvent(T1 const& SliceParticle1, T11& TcManager) { TripletHistManager.resetTrackedParticlesPerEvent(); + bool foundTriplet = false; for (auto const& part : SliceParticle1) { ParticleHistManager1.template fill(part, TrackTable); @@ -215,10 +226,12 @@ void processSameEvent(T1 const& SliceParticle1, if (TripletHistManager.checkTripletCuts()) { TripletHistManager.template fill(); TripletHistManager.trackParticlesPerEvent(p1, p2, p3); + foundTriplet = true; } } TripletHistManager.fillMixingQaSe(); + return foundTriplet; } // process same event for 3 identical particles with mc information @@ -235,7 +248,7 @@ template -void processSameEvent(T1 const& SliceParticle, +bool processSameEvent(T1 const& SliceParticle, T2 const& TrackTable, T3 const& mcParticles, T4 const& mcMothers, @@ -250,6 +263,7 @@ void processSameEvent(T1 const& SliceParticle, TripletOrder tripletOrder) { TripletHistManager.resetTrackedParticlesPerEvent(); + bool foundTriplet = false; for (auto const& part : SliceParticle) { if (!Cleaner.isClean(part, mcParticles, mcMothers, mcPartonicMothers)) { @@ -299,17 +313,19 @@ void processSameEvent(T1 const& SliceParticle, if (TripletHistManager.checkTripletCuts()) { TripletHistManager.template fill(); TripletHistManager.trackParticlesPerEvent(p1, p2, p3); + foundTriplet = true; } } TripletHistManager.fillMixingQaSe(); + return foundTriplet; } // process same event for 2 identical particles and one other with mc information template -void processSameEvent(T1 const& SliceParticle1, +bool processSameEvent(T1 const& SliceParticle1, T2 const& SliceParticle3, T3 const& TrackTable, T4 const& mcParticles, @@ -327,6 +343,7 @@ void processSameEvent(T1 const& SliceParticle1, TripletOrder tripletOrder) { TripletHistManager.resetTrackedParticlesPerEvent(); + bool foundTriplet = false; for (auto const& part : SliceParticle1) { if (!Cleaner1.isClean(part, mcParticles, mcMothers, mcPartonicMothers)) { @@ -376,11 +393,13 @@ void processSameEvent(T1 const& SliceParticle1, if (TripletHistManager.checkTripletCuts()) { TripletHistManager.template fill(); TripletHistManager.trackParticlesPerEvent(p1, p2, p3); + foundTriplet = true; } } } TripletHistManager.fillMixingQaSe(); + return foundTriplet; } // process same event for 3 different particles with mc information @@ -404,7 +423,7 @@ template -void processSameEvent(T1 const& SliceParticle1, +bool processSameEvent(T1 const& SliceParticle1, T2 const& SliceParticle2, T3 const& SliceParticle3, T4 const& TrackTable, @@ -424,6 +443,7 @@ void processSameEvent(T1 const& SliceParticle1, T18& TcManager) { TripletHistManager.resetTrackedParticlesPerEvent(); + bool foundTriplet = false; for (auto const& part : SliceParticle1) { if (!Cleaner1.isClean(part, mcParticles, mcMothers, mcPartonicMothers)) { @@ -469,10 +489,12 @@ void processSameEvent(T1 const& SliceParticle1, if (TripletHistManager.checkTripletCuts()) { TripletHistManager.template fill(); TripletHistManager.trackParticlesPerEvent(p1, p2, p3); + foundTriplet = true; } } TripletHistManager.fillMixingQaSe(); + return foundTriplet; } // process mixed event diff --git a/PWGCF/Femto/Core/v0Builder.h b/PWGCF/Femto/Core/v0Builder.h index 295b6ff89c5..6792d395071 100644 --- a/PWGCF/Femto/Core/v0Builder.h +++ b/PWGCF/Femto/Core/v0Builder.h @@ -116,11 +116,11 @@ struct ConfK0shortBits : o2::framework::ConfigurableGroup { o2::framework::Configurable mask{"mask", 0, "Bitmask for v0 selection"}; // base selection for analysis task for lambdas -template +template struct ConfLambdaSelection : o2::framework::ConfigurableGroup { std::string prefix = Prefix; V0_DEFAULT_SELECTIONS(1.0, 1.2, 3122) - o2::framework::Configurable sign{"sign", 1, "Sign of the Lambda (+1: Lambda; -1: Antilambda; 0: both)"}; + o2::framework::Configurable sign{"sign", DefaultSign, "Sign of the Lambda (+1: Lambda; -1: Antilambda; 0: both)"}; }; // base selection for analysis task for k0short @@ -142,6 +142,12 @@ constexpr const char PrefixK0shortSelection2[] = "K0shortSelection2"; using ConfK0shortSelection1 = ConfK0shortSelection; using ConfK0shortSelection2 = ConfK0shortSelection; +// selections for strangeness yields (e.g. strangeness enhancement in dN/deta tasks), independent of any pair/triplet selection +constexpr const char PrefixLambdaSelectionStrangeness[] = "LambdaSelectionStrangeness"; +using ConfLambdaSelectionStrangeness = ConfLambdaSelection; // lambdas and antilambdas by default +constexpr const char PrefixK0shortSelectionStrangeness[] = "K0shortSelectionStrangeness"; +using ConfK0shortSelectionStrangeness = ConfK0shortSelection; + /// The different selections for v0s enum V0Sels { // selections for lambdas diff --git a/PWGCF/Femto/DataModel/FemtoTables.h b/PWGCF/Femto/DataModel/FemtoTables.h index 9ac62087b1d..dd4af0506bb 100644 --- a/PWGCF/Femto/DataModel/FemtoTables.h +++ b/PWGCF/Femto/DataModel/FemtoTables.h @@ -18,6 +18,7 @@ #include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/femtoUtils.h" +#include "PWGCF/Femto/Core/modes.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" @@ -26,6 +27,7 @@ #include #include #include +#include #include #include @@ -168,12 +170,14 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FColPos_001, "FCOLPOS", 1, //! full vertex po using FColPos = FColPos_001; // table for different multiplicity estimators -DECLARE_SOA_TABLE_STAGED_VERSIONED(FColMults_001, "FCOLMULT", 1, //! multiplicities - mult::MultFT0A, mult::MultFT0C, //! FIT detectors - mult::MultNTracksPVeta1, //! number of PV contribs total - mult::MultNTracksPVetaHalf, //! global track multiplicities - evsel::NumTracksInTimeRange, //! occupancy (number of track in time range) - evsel::SumAmpFT0CInTimeRange); //! occupancy (FT0C amplitude in time range) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FColMults_001, "FCOLMULT", 1, //! multiplicities + mult::MultFT0A, //! FT0A detectors + mult::MultFT0C, //! FT0C detectors + collision::NumContrib, //! number of tracks used tto find PV + mult::MultNTracksPVeta1, //! number of PV contribs total + mult::MultNTracksPVetaHalf, //! global track multiplicities + evsel::NumTracksInTimeRange, //! occupancy (number of tracks from different collisions in time range) + evsel::SumAmpFT0CInTimeRange); //! occupancy (FT0C amplitude in time range) using FColMults = FColMults_001; // table for different centrality (multiplicity percentile) estimators @@ -328,6 +332,17 @@ DECLARE_SOA_DYNAMIC_COLUMN(TpcSharedOverFound, tpcSharedOverFound, //! Number of [](uint8_t tpcNclsFound, uint8_t tpcNClsShared) -> float { return static_cast(tpcNClsShared) / static_cast(tpcNclsFound); }); DECLARE_SOA_COLUMN(TpcChi2NCl, tpcChi2NCl, float); //! Tpc chi2 +// detector map and row type +DECLARE_SOA_COLUMN(DetectorMap, detectorMap, uint8_t); //! Detector map of the track (same bit layout as o2::aod::track::DetectorMap) +DECLARE_SOA_DYNAMIC_COLUMN(HasIts, hasIts, [](uint8_t detectorMap) -> bool { return detectorMap & o2::aod::track::ITS; }); //! Track has ITS +DECLARE_SOA_DYNAMIC_COLUMN(HasTpc, hasTpc, [](uint8_t detectorMap) -> bool { return detectorMap & o2::aod::track::TPC; }); //! Track has TPC +DECLARE_SOA_DYNAMIC_COLUMN(HasTof, hasTof, [](uint8_t detectorMap) -> bool { return detectorMap & o2::aod::track::TOF; }); //! Track has TOF +DECLARE_SOA_COLUMN(FillType, fillType, o2::analysis::femto::datatypes::TrackType); //! modes::Track this row was written as (kTrack = selected track, otherwise daughter-only row) +DECLARE_SOA_DYNAMIC_COLUMN(IsDaughterOnly, isDaughterOnly, //! True if the row was only written to resolve a daughter index (not a selected track) + [](o2::analysis::femto::datatypes::TrackType fillType) -> bool { + return fillType != static_cast(o2::analysis::femto::modes::Track::kTrack); + }); + // tof related information DECLARE_SOA_COLUMN(TofBeta, tofBeta, float); //! Tof beta // tof mass will be stored in mass column @@ -444,8 +459,15 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FTrackExtras_001, "FTRACKEXTRA", 1, //! track femtotracks::TpcNClsCrossedRows, femtotracks::TpcNClsShared, femtotracks::TofBeta, + femtotracks::TpcChi2NCl, + femtotracks::DetectorMap, + femtotracks::FillType, femtotracks::TpcCrossedRowsOverFound, - femtotracks::TpcSharedOverFound); + femtotracks::TpcSharedOverFound, + femtotracks::HasIts, + femtotracks::HasTpc, + femtotracks::HasTof, + femtotracks::IsDaughterOnly); using FTrackExtras = FTrackExtras_001; // table for extra PID information diff --git a/PWGCF/Femto/TableProducer/femtoProducer.cxx b/PWGCF/Femto/TableProducer/femtoProducer.cxx index 6a4cd41d469..16bd0b3726e 100644 --- a/PWGCF/Femto/TableProducer/femtoProducer.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducer.cxx @@ -295,6 +295,25 @@ struct FemtoProducer { LOG(warn) << " - particles in pass-through, collisions not: the extra particle rows hang off " << "a candidate-biased event sample, which will bias event-normalised observables."; } + + // in mc pass-through every generated collision is written, but reco collisions of other + // sub-generators are still rejected in checkCollision -> biased event-loss denominator + if (mcBuilder.isPassThrough() && collisionBuilder.isPassThrough() && collisionBuilder.subGeneratorId() >= 0) { + LOG(warn) << "MC and collision pass-through are enabled, but CollisionFilter.subGeneratorId = " + << collisionBuilder.subGeneratorId() << " still rejects reconstructed collisions of other sub-generators, " + << "while all generated collisions are written. Set it to -1 for unbiased event/signal-loss corrections."; + } + + // daughter tracks can be written a second time under the collision of their mother candidate; + // without FTrackExtras (fillType) these rows cannot be told apart from selected tracks downstream + const bool anyDaughterBuilder = k0shortBuilder.fillAnyTable() || lambdaBuilder.fillAnyTable() || antilambdaBuilder.fillAnyTable() || + xiBuilder.fillAnyTable() || omegaBuilder.fillAnyTable() || + sigmaBuilder.fillAnyTable() || sigmaPlusBuilder.fillAnyTable() || + d0Builder.fillAnyTable() || d0barBuilder.fillAnyTable() || lcBuilder.fillAnyTable() || lcBarBuilder.fillAnyTable(); + if (trackBuilder.isPassThrough() && anyDaughterBuilder && !trackBuilder.producingTrackExtras()) { + LOG(warn) << "Track pass-through is enabled together with candidates that register daughter tracks, but FTrackExtras is not produced. " + << "Daughter-only track rows cannot be identified downstream (enable TrackTables.produceTrackExtras)."; + } } // ---- guard: exactly one process function --------------------------------- @@ -671,7 +690,7 @@ struct FemtoProducer { { if (mcBuilder.isPassThrough()) { mcBuilder.reset(mcCols, mcParticles); - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts, pdgDb); + mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); } for (const auto& col : cols) { @@ -694,7 +713,7 @@ struct FemtoProducer { { if (mcBuilder.isPassThrough()) { mcBuilder.reset(mcCols, mcParticles); - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts, pdgDb); + mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); } for (const auto& col : cols) { @@ -718,7 +737,7 @@ struct FemtoProducer { { if (mcBuilder.isPassThrough()) { mcBuilder.reset(mcCols, mcParticles); - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts, pdgDb); + mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); } for (const auto& col : cols) { @@ -745,7 +764,7 @@ struct FemtoProducer { { if (mcBuilder.isPassThrough()) { mcBuilder.reset(mcCols, mcParticles); - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts, pdgDb); + mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); } for (const auto& col : cols) { @@ -773,7 +792,7 @@ struct FemtoProducer { { if (mcBuilder.isPassThrough()) { mcBuilder.reset(mcCols, mcParticles); - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts, pdgDb); + mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); } for (const auto& col : cols) { @@ -804,7 +823,7 @@ struct FemtoProducer { { if (mcBuilder.isPassThrough()) { mcBuilder.reset(mcCols, mcParticles); - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts, pdgDb); + mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); } for (const auto& col : cols) { @@ -834,7 +853,7 @@ struct FemtoProducer { { if (mcBuilder.isPassThrough()) { mcBuilder.reset(mcCols, mcParticles); - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts, pdgDb); + mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); } for (const auto& col : cols) { @@ -862,7 +881,7 @@ struct FemtoProducer { { if (mcBuilder.isPassThrough()) { mcBuilder.reset(mcCols, mcParticles); - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts, pdgDb); + mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); } for (const auto& col : cols) { @@ -892,7 +911,7 @@ struct FemtoProducer { { if (mcBuilder.isPassThrough()) { mcBuilder.reset(mcCols, mcParticles); - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts, pdgDb); + mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); } for (const auto& col : cols) { @@ -919,7 +938,7 @@ struct FemtoProducer { { if (mcBuilder.isPassThrough()) { mcBuilder.reset(mcCols, mcParticles); - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts, pdgDb); + mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); } for (const auto& col : cols) { @@ -976,7 +995,7 @@ struct FemtoProducer { { if (mcBuilder.isPassThrough()) { mcBuilder.reset(mcCols, mcParticles); - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts, pdgDb); + mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); } for (const auto& col : cols) { @@ -1003,7 +1022,7 @@ struct FemtoProducer { { if (mcBuilder.isPassThrough()) { mcBuilder.reset(mcCols, mcParticles); - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts, pdgDb); + mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); } for (const auto& col : cols) { diff --git a/PWGCF/Femto/Tasks/CMakeLists.txt b/PWGCF/Femto/Tasks/CMakeLists.txt index e7c42416b04..b67a1bf5b19 100644 --- a/PWGCF/Femto/Tasks/CMakeLists.txt +++ b/PWGCF/Femto/Tasks/CMakeLists.txt @@ -113,3 +113,8 @@ o2physics_add_dpl_workflow(femto-triplet-track-track-cascade SOURCES femtoTripletTrackTrackCascade.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femto-dndeta-pair + SOURCES femtoDndetaPair.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/Femto/Tasks/femtoDndetaPair.cxx b/PWGCF/Femto/Tasks/femtoDndetaPair.cxx new file mode 100644 index 00000000000..eb70b544157 --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoDndetaPair.cxx @@ -0,0 +1,380 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoDndetaPair.cxx +/// \brief charged-particle pseudorapidity density in events triggered on femto pairs (or minimum bias) +/// \author Anton Riedel, TU München, anton.riedel@cern.ch +/// +/// An event is triggered if at least one pair passes the particle selections, the pair cleaner, +/// the close pair rejection and the pair cuts (e.g. PairCuts.kstarMax). +/// Needs a femto producer run in pass-through mode. + +#include "PWGCF/Femto/Core/closePairRejection.h" +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/dndetaBuilder.h" +#include "PWGCF/Femto/Core/dndetaHistManager.h" +#include "PWGCF/Femto/Core/mcBuilder.h" +#include "PWGCF/Femto/Core/mcParticleHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/pairBuilder.h" +#include "PWGCF/Femto/Core/pairCleaner.h" +#include "PWGCF/Femto/Core/pairHistManager.h" +#include "PWGCF/Femto/Core/particleCleaner.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/trackBuilder.h" +#include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/Core/v0Builder.h" +#include "PWGCF/Femto/Core/v0HistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoDndetaPair { + + // setup tables + // FCols provides the default centrality (FT0M in pp, FT0C in PbPb), FColMults the INEL>0 and correlation estimators + using FemtoCollisions = o2::soa::Join; + using FemtoCollision = FemtoCollisions::iterator; + using FemtoCollisionsWithLabel = o2::soa::Join; + + // same join as femtoTrackQa, the track histogram managers (QA) need mass, DCAs, extras and PID + using FemtoTracks = o2::soa::Join; + using FemtoTracksWithLabel = o2::soa::Join; + + using FemtoLambdas = o2::soa::Join; + using FemtoLambdasWithLabel = o2::soa::Join; + using FemtoK0shorts = o2::soa::Join; + + using FemtoMcParticles = o2::soa::Join; + + o2::framework::SliceCache cache; + o2::framework::Service pdg; + + // setup dndeta + dndetabuilder::ConfDndeta confDndeta; + dndetabuilder::ConfDndetaAcceptance confDndetaAcceptance; + dndetahistmanager::ConfDndetaBinning confDndetaBinning; + dndetabuilder::DndetaBuilder dndetaBuilder; + + o2::framework::Preslice perMcColParticles = o2::aod::femtomcparticle::fMcColId; + + // setup collisions (applied in the dndeta builder, no filter, so the cutflow sees every collision) + collisionbuilder::ConfCollisionSelection collisionSelection; + colhistmanager::ConfCollisionBinning confCollisionBinning; + + // setup tracks for dndeta (bitmask with track quality bits only, see dndetaBuilder.h) + trackbuilder::ConfTrackSelectionDndetaGlobal confDndetaTracksGlobal; + trackhistmanager::ConfDndetaTrackGlobalBinning confDndetaTrackGlobalBinning; + trackhistmanager::ConfDndetaTrackGlobalQaBinning confDndetaTrackGlobalQaBinning; + + trackbuilder::ConfTrackSelectionDndetaItsOnly confDndetaTracksItsOnly; + trackhistmanager::ConfDndetaTrackItsOnlyBinning confDndetaTrackItsOnlyBinning; + trackhistmanager::ConfDndetaTrackItsOnlyQaBinning confDndetaTrackItsOnlyQaBinning; + + o2::framework::Partition dndetaTrackPartitionGlobal = MAKE_TRACK_PARTITION(confDndetaTracksGlobal); + o2::framework::Partition dndetaTrackPartitionItsOnly = MAKE_TRACK_PARTITION(confDndetaTracksItsOnly); + + o2::framework::Partition dndetaTrackWithLabelPartitionGlobal = MAKE_TRACK_PARTITION(confDndetaTracksGlobal); + o2::framework::Partition dndetaTrackWithLabelPartitionItsOnly = MAKE_TRACK_PARTITION(confDndetaTracksItsOnly); + + // setup tracks (trigger) + trackbuilder::ConfTrackSelection1 confTrackSelection1; + trackhistmanager::ConfTrackBinning1 confTrackBinning1; + particlecleaner::ConfTrackCleaner1 confTrackCleaner1; + + trackbuilder::ConfTrackSelection2 confTrackSelection2; + trackhistmanager::ConfTrackBinning2 confTrackBinning2; + particlecleaner::ConfTrackCleaner2 confTrackCleaner2; + + o2::framework::Partition trackPartition1 = MAKE_TRACK_PARTITION(confTrackSelection1); + o2::framework::Partition trackPartition2 = MAKE_TRACK_PARTITION(confTrackSelection2); + + o2::framework::Partition trackWithLabelPartition1 = MAKE_TRACK_PARTITION(confTrackSelection1); + o2::framework::Partition trackWithLabelPartition2 = MAKE_TRACK_PARTITION(confTrackSelection2); + + // setup for daughters + trackhistmanager::ConfV01PosDauBinning confPosDauBinning; + trackhistmanager::ConfV01NegDauBinning confNegDauBinning; + + // setup lambdas (trigger) + v0builder::ConfLambdaSelection1 confLambdaSelection; + v0histmanager::ConfLambdaBinning1 confLambdaBinning; + particlecleaner::ConfLambdaCleaner1 confLambdaCleaner; + + o2::framework::Partition lambdaPartition = MAKE_LAMBDA_PARTITION(confLambdaSelection); + o2::framework::Partition lambdaWithLabelPartition = MAKE_LAMBDA_PARTITION(confLambdaSelection); + + // setup strangeness yields (dedicated selections, independent of the trigger) + v0builder::ConfLambdaSelectionStrangeness confStrangeLambdaSelection; + v0builder::ConfK0shortSelectionStrangeness confStrangeK0shortSelection; + + o2::framework::Partition strangeLambdaPartition = MAKE_LAMBDA_PARTITION(confStrangeLambdaSelection); + o2::framework::Partition strangeK0shortPartition = MAKE_K0SHORT_PARTITION(confStrangeK0shortSelection); + + // setup mc particles (generator-level trigger) + // FMcParticles also contains rows of reconstructed secondaries (created via labels), so set + // McParticleSelection1/2.requireOrigin = true (origin = 2, physical primary) for an unbiased generator-level trigger + mcbuilder::ConfMcParticleSelection1 confMcParticleSelection1; + mcparticlehistmanager::ConfMcParticleBinning1 confMcParticleBinning1; + particlecleaner::ConfMcParticleCleaner1 confMcParticleCleaner1; + + mcbuilder::ConfMcParticleSelection2 confMcParticleSelection2; + mcparticlehistmanager::ConfMcParticleBinning2 confMcParticleBinning2; + particlecleaner::ConfMcParticleCleaner2 confMcParticleCleaner2; + + o2::framework::Partition mcParticlePartition1 = MAKE_MC_PARTICLE_PARTITION(confMcParticleSelection1); + o2::framework::Partition mcParticlePartition2 = MAKE_MC_PARTICLE_PARTITION(confMcParticleSelection2); + + // setup pairs + pairhistmanager::ConfPairBinning confPairBinning; + pairhistmanager::ConfPairCuts confPairCuts; + paircleaner::ConfPairCleanerBinning confPairCleaner; + pairhistmanager::ConfMixing confMixing; // only same event is processed, used for sameSpecies/seed + + closepairrejection::ConfCprTrackTrack confCprTrackTrack; + closepairrejection::ConfCprMcParticleMcParticle confCprMcParticleMcParticle; // generator-level trigger, no cut by default + closepairrejection::ConfCprTrackV0Daughter confCprTrackV0; + + pairbuilder::PairTrackTrackBuilder< + trackhistmanager::PrefixTrack1, + trackhistmanager::PrefixTrack2, + pairhistmanager::PrefixTrackTrackSe, + pairhistmanager::PrefixTrackTrackMe, + closepairrejection::PrefixTrackTrackSe, + closepairrejection::PrefixTrackTrackMe> + pairTrackTrackBuilder; + + pairbuilder::PairTrackV0Builder< + trackhistmanager::PrefixTrack1, + v0histmanager::PrefixLambda1, + trackhistmanager::PrefixV01PosDaughter, + trackhistmanager::PrefixV01NegDaughter, + pairhistmanager::PrefixTrackV0Se, + pairhistmanager::PrefixTrackV0Me, + closepairrejection::PrefixTrackV0DaughterSe, + closepairrejection::PrefixTrackV0DaughterMe, + modes::V0::kLambda> + pairTrackLambdaBuilder; + + pairbuilder::PairMcParticleMcParticleBuilder< + mcparticlehistmanager::PrefixMcParticle1, + mcparticlehistmanager::PrefixMcParticle2, + pairhistmanager::PrefixMcParticleMcParticleSe, + pairhistmanager::PrefixMcParticleMcParticleMe, + closepairrejection::PrefixMcParticleMcParticleSe, + closepairrejection::PrefixMcParticleMcParticleMe> + pairMcParticleMcParticleBuilder; + + // dndeta histograms and trigger QA are kept in separate registries, so the fixed histogram names of the builders cannot clash + o2::framework::HistogramRegistry hRegistry{"FemtoDndetaPair", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + o2::framework::HistogramRegistry hRegistryTrigger{"FemtoDndetaPairTrigger", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + o2::framework::HistogramRegistry hRegistryGenTrigger{"FemtoDndetaPairGenTrigger", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext&) + { + const bool processDataWithStrangeness = doprocessMinimumBiasWithStrangeness || doprocessTrackTrackWithStrangeness || doprocessTrackV0WithStrangeness; + const bool processMc = doprocessMinimumBiasMc || doprocessTrackTrackMc || doprocessTrackV0Mc; + const int nProcess = static_cast(doprocessMinimumBias) + static_cast(doprocessTrackTrack) + static_cast(doprocessTrackV0) + + static_cast(doprocessMinimumBiasWithStrangeness) + static_cast(doprocessTrackTrackWithStrangeness) + static_cast(doprocessTrackV0WithStrangeness) + + static_cast(doprocessMinimumBiasMc) + static_cast(doprocessTrackTrackMc) + static_cast(doprocessTrackV0Mc); + if (nProcess != 1) { + LOG(fatal) << "Exactly one process function has to be activated (found " << nProcess << "). Breaking..."; + } + + modes::Trigger trigger = modes::Trigger::kMinimumBias; + if (doprocessTrackTrack || doprocessTrackTrackWithStrangeness || doprocessTrackTrackMc) { + trigger = modes::Trigger::kTrackTrack; + } else if (doprocessTrackV0 || doprocessTrackV0WithStrangeness || doprocessTrackV0Mc) { + trigger = modes::Trigger::kTrackV0; + } + LOG(info) << "Trigger: " << modes::triggerToString(trigger); + + dndetaBuilder.init(&hRegistry, confDndeta, confDndetaAcceptance, collisionSelection, + confDndetaTracksGlobal, confDndetaTrackGlobalBinning, confDndetaTrackGlobalQaBinning, + confDndetaTracksItsOnly, confDndetaTrackItsOnlyBinning, confDndetaTrackItsOnlyQaBinning, + confDndetaBinning, processMc, processDataWithStrangeness); + if (dndetaBuilder.useGenTrigger() && (!processMc || trigger == modes::Trigger::kMinimumBias)) { + LOG(warn) << "Dndeta.useGenTrigger is only used for mc processing with a pair trigger. Ignoring it."; + } + + // setup histogram specs + std::map> colHistSpec; + std::map> trackHistSpec1; + std::map> trackHistSpec2; + std::map> posDauSpec; + std::map> negDauSpec; + std::map> lambdaHistSpec; + std::map> pairHistSpec; + std::map> pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); + + if (!processMc) { + colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); + trackHistSpec1 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning1); + trackHistSpec2 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning2); + posDauSpec = trackhistmanager::makeTrackHistSpecMap(confPosDauBinning); + negDauSpec = trackhistmanager::makeTrackHistSpecMap(confNegDauBinning); + lambdaHistSpec = v0histmanager::makeV0HistSpecMap(confLambdaBinning); + pairHistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); + } else { + colHistSpec = colhistmanager::makeColMcHistSpecMap(confCollisionBinning); + trackHistSpec1 = trackhistmanager::makeTrackMcHistSpecMap(confTrackBinning1); + trackHistSpec2 = trackhistmanager::makeTrackMcHistSpecMap(confTrackBinning2); + posDauSpec = trackhistmanager::makeTrackMcHistSpecMap(confPosDauBinning); + negDauSpec = trackhistmanager::makeTrackMcHistSpecMap(confNegDauBinning); + lambdaHistSpec = v0histmanager::makeV0McHistSpecMap(confLambdaBinning); + pairHistSpec = pairhistmanager::makePairMcHistSpecMap(confPairBinning, confMixing); + } + + // only the builder of the active trigger is initialized + // only same event processing is used; the mixed event histograms are booked but stay empty + if (trigger == modes::Trigger::kTrackTrack) { + std::map> cprHistSpec = closepairrejection::makeCprHistSpecMap(confCprTrackTrack); + if (!processMc) { + pairTrackTrackBuilder.init(&hRegistryTrigger, confCollisionBinning, confTrackSelection1, confTrackSelection2, confTrackCleaner1, confTrackCleaner2, confCprTrackTrack, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec1, trackHistSpec2, pairHistSpec, cprHistSpec, pairCleanerHistSpec); + } else { + pairTrackTrackBuilder.init(&hRegistryTrigger, confCollisionBinning, confTrackSelection1, confTrackSelection2, confTrackCleaner1, confTrackCleaner2, confCprTrackTrack, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec1, trackHistSpec2, pairHistSpec, cprHistSpec, pairCleanerHistSpec); + } + } else if (trigger == modes::Trigger::kTrackV0) { + std::map> cprHistSpec = closepairrejection::makeCprHistSpecMap(confCprTrackV0); + if (!processMc) { + pairTrackLambdaBuilder.init(&hRegistryTrigger, confCollisionBinning, confTrackSelection1, confTrackCleaner1, confLambdaSelection, confLambdaCleaner, confCprTrackV0, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec1, lambdaHistSpec, posDauSpec, negDauSpec, pairHistSpec, cprHistSpec, pairCleanerHistSpec); + } else { + pairTrackLambdaBuilder.init(&hRegistryTrigger, confCollisionBinning, confTrackSelection1, confTrackCleaner1, confLambdaSelection, confLambdaCleaner, confCprTrackV0, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec1, lambdaHistSpec, posDauSpec, negDauSpec, pairHistSpec, cprHistSpec, pairCleanerHistSpec); + } + } + + // generator-level trigger + if (processMc && trigger != modes::Trigger::kMinimumBias && dndetaBuilder.useGenTrigger()) { + std::map> genColHistSpec = colhistmanager::makeColMcHistSpecMap(confCollisionBinning); + std::map> mcParticleHistSpec1 = mcparticlehistmanager::makeMcParticleHistSpecMap(confMcParticleBinning1); + std::map> mcParticleHistSpec2 = mcparticlehistmanager::makeMcParticleHistSpecMap(confMcParticleBinning2); + std::map> genPairHistSpec = pairhistmanager::makePairMcTruthHistSpecMap(confPairBinning, confMixing); + std::map> genCprHistSpec = closepairrejection::makeCprHistSpecMap(confCprMcParticleMcParticle); + pairMcParticleMcParticleBuilder.init(&hRegistryGenTrigger, confCollisionBinning, confMcParticleSelection1, confMcParticleSelection2, confMcParticleBinning1, confMcParticleBinning2, confMcParticleCleaner1, confMcParticleCleaner2, confCprMcParticleMcParticle, confMixing, confPairBinning, confPairCuts, genColHistSpec, mcParticleHistSpec1, mcParticleHistSpec2, genPairHistSpec, genCprHistSpec, pairCleanerHistSpec); + } + + hRegistry.print(); + hRegistryTrigger.print(); + hRegistryGenTrigger.print(); + }; + + // data + void processMinimumBias(FemtoCollision const& col, FemtoTracks const& /*tracks*/) + { + dndetaBuilder.processData(col, dndetaTrackPartitionGlobal, dndetaTrackPartitionItsOnly, cache, []() { return true; }); + } + PROCESS_SWITCH(FemtoDndetaPair, processMinimumBias, "Data, no trigger", true); + + void processTrackTrack(FemtoCollision const& col, FemtoTracks const& tracks) + { + dndetaBuilder.processData(col, dndetaTrackPartitionGlobal, dndetaTrackPartitionItsOnly, cache, [&]() { return pairTrackTrackBuilder.processSameEvent(col, tracks, trackPartition1, trackPartition2, cache); }); + } + PROCESS_SWITCH(FemtoDndetaPair, processTrackTrack, "Data, track-track trigger", false); + + void processTrackV0(FemtoCollision const& col, FemtoTracks const& tracks, FemtoLambdas const& lambdas) + { + dndetaBuilder.processData(col, dndetaTrackPartitionGlobal, dndetaTrackPartitionItsOnly, cache, [&]() { return pairTrackLambdaBuilder.processSameEvent(col, tracks, trackPartition1, lambdas, lambdaPartition, cache); }); + } + PROCESS_SWITCH(FemtoDndetaPair, processTrackV0, "Data, track-lambda trigger", false); + + // data with strangeness yields + template + void fillStrangeness(T const& col) + { + auto lambdas = strangeLambdaPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto k0shorts = strangeK0shortPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + dndetaBuilder.processStrangeness(col, lambdas, k0shorts); + } + + void processMinimumBiasWithStrangeness(FemtoCollision const& col, FemtoTracks const& /*tracks*/, FemtoLambdas const& /*lambdas*/, FemtoK0shorts const& /*k0shorts*/) + { + if (dndetaBuilder.processData(col, dndetaTrackPartitionGlobal, dndetaTrackPartitionItsOnly, cache, []() { + return true; + })) { + fillStrangeness(col); + } + } + PROCESS_SWITCH(FemtoDndetaPair, processMinimumBiasWithStrangeness, "Data with strangeness yields, no trigger", false); + + void processTrackTrackWithStrangeness(FemtoCollision const& col, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoK0shorts const& /*k0shorts*/) + { + if (dndetaBuilder.processData(col, dndetaTrackPartitionGlobal, dndetaTrackPartitionItsOnly, cache, [&]() { + return pairTrackTrackBuilder.processSameEvent(col, tracks, trackPartition1, trackPartition2, cache); + })) { + fillStrangeness(col); + } + } + PROCESS_SWITCH(FemtoDndetaPair, processTrackTrackWithStrangeness, "Data with strangeness yields, track-track trigger", false); + + void processTrackV0WithStrangeness(FemtoCollision const& col, FemtoTracks const& tracks, FemtoLambdas const& lambdas, FemtoK0shorts const& /*k0shorts*/) + { + if (dndetaBuilder.processData(col, dndetaTrackPartitionGlobal, dndetaTrackPartitionItsOnly, cache, [&]() { + return pairTrackLambdaBuilder.processSameEvent(col, tracks, trackPartition1, lambdas, lambdaPartition, cache); + })) { + fillStrangeness(col); + } + } + PROCESS_SWITCH(FemtoDndetaPair, processTrackV0WithStrangeness, "Data with strangeness yields, track-lambda trigger", false); + + // mc + void processMinimumBiasMc(FemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& /*tracks*/, FemtoMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers) + { + dndetaBuilder.processMc( + cols, mcCols, dndetaTrackWithLabelPartitionGlobal, dndetaTrackWithLabelPartitionItsOnly, cache, mcParticles, mcMothers, perMcColParticles, pdg, + [](auto const& /*col*/) { + return true; + }, + [](auto const& /*mcCol*/) { return true; }); + } + PROCESS_SWITCH(FemtoDndetaPair, processMinimumBiasMc, "MC, no trigger", false); + + void processTrackTrackMc(FemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + dndetaBuilder.processMc( + cols, mcCols, dndetaTrackWithLabelPartitionGlobal, dndetaTrackWithLabelPartitionItsOnly, cache, mcParticles, mcMothers, perMcColParticles, pdg, + [&](auto const& col) { return pairTrackTrackBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache); }, + [&](auto const& mcCol) { return pairMcParticleMcParticleBuilder.processSameEvent(mcCol, mcParticles, mcMothers, mcPartonicMothers, mcParticlePartition1, mcParticlePartition2, cache); }); + } + PROCESS_SWITCH(FemtoDndetaPair, processTrackTrackMc, "MC, track-track trigger", false); + + void processTrackV0Mc(FemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdasWithLabel const& lambdas, FemtoMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + dndetaBuilder.processMc( + cols, mcCols, dndetaTrackWithLabelPartitionGlobal, dndetaTrackWithLabelPartitionItsOnly, cache, mcParticles, mcMothers, perMcColParticles, pdg, + [&](auto const& col) { return pairTrackLambdaBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition1, lambdas, lambdaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); }, + [&](auto const& mcCol) { return pairMcParticleMcParticleBuilder.processSameEvent(mcCol, mcParticles, mcMothers, mcPartonicMothers, mcParticlePartition1, mcParticlePartition2, cache); }); + } + PROCESS_SWITCH(FemtoDndetaPair, processTrackV0Mc, "MC, track-lambda trigger", false); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +}