diff --git a/Analysis/ALICE3/include/ALICE3Analysis/MID.h b/Analysis/ALICE3/include/ALICE3Analysis/MID.h new file mode 100644 index 0000000000000..b6964d056467c --- /dev/null +++ b/Analysis/ALICE3/include/ALICE3Analysis/MID.h @@ -0,0 +1,43 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 MID.h +/// \author Vít Kučera +/// \note Based on RICH.h +/// \brief Set of tables for the ALICE3 MID information +/// + +#ifndef O2_ANALYSIS_ALICE3_MID_H_ +#define O2_ANALYSIS_ALICE3_MID_H_ + +// O2 includes +#include "Framework/AnalysisDataModel.h" + +namespace o2::aod +{ +namespace alice3mid +{ +DECLARE_SOA_INDEX_COLUMN(Collision, collision); //! +DECLARE_SOA_INDEX_COLUMN(Track, track); //! +DECLARE_SOA_COLUMN(MIDIsMuon, midIsMuon, uint8_t); //! FIXME: To be changed to bool once bool columns are groupable. +} // namespace alice3mid + +DECLARE_SOA_TABLE(MIDs, "AOD", "MID", //! + o2::soa::Index<>, + alice3mid::CollisionId, + alice3mid::TrackId, + alice3mid::MIDIsMuon); + +using MID = MIDs::iterator; + +} // namespace o2::aod + +#endif // O2_ANALYSIS_ALICE3_MID_H_ diff --git a/Analysis/Core/include/AnalysisCore/TrackSelectorPID.h b/Analysis/Core/include/AnalysisCore/TrackSelectorPID.h index 1c9cf29269dcf..f2c958b5143fb 100644 --- a/Analysis/Core/include/AnalysisCore/TrackSelectorPID.h +++ b/Analysis/Core/include/AnalysisCore/TrackSelectorPID.h @@ -251,9 +251,151 @@ class TrackSelectorPID } } - /// Returns status of combined PID selection for a given track. + // RICH + + /// Set pT range where RICH PID is applicable. + void setRangePtRICH(float ptMin, float ptMax) + { + mPtRICHMin = ptMin; + mPtRICHMax = ptMax; + } + + /// Set RICH nσ range in which a track should be accepted. + void setRangeNSigmaRICH(float nsMin, float nsMax) + { + mNSigmaRICHMin = nsMin; + mNSigmaRICHMax = nsMax; + } + + /// Set RICH nσ range in which a track should be conditionally accepted if combined with TOF. + void setRangeNSigmaRICHCondTOF(float nsMin, float nsMax) + { + mNSigmaRICHMinCondTOF = nsMin; + mNSigmaRICHMaxCondTOF = nsMax; + } + + /// Checks if track is OK for RICH PID. + /// \param track track + /// \return true if track is OK for RICH PID + template + bool isValidTrackPIDRICH(const T& track) + { + auto pt = track.pt(); + return mPtRICHMin <= pt && pt <= mPtRICHMax; + } + + /// Checks if track is compatible with given particle species hypothesis within given RICH nσ range. /// \param track track - /// \return combined-selection status (see TrackSelectorPID::Status) + /// \param conditionalTOF variable to store the result of selection with looser cuts for conditional accepting of track if combined with TOF + /// \return true if track satisfies RICH PID hypothesis for given RICH nσ range + template + bool isSelectedTrackPIDRICH(const T& track, bool& conditionalTOF) + { + // Accept if selection is disabled via large values. + if (mNSigmaRICHMin < -999. && mNSigmaRICHMax > 999.) { + return true; + } + + // Get nσ for a given particle hypothesis. + double nSigma = 100.; + switch (mPdg) { + case kElectron: { + nSigma = track.rich().richNsigmaEl(); + break; + } + case kMuonMinus: { + nSigma = track.rich().richNsigmaMu(); + break; + } + case kPiPlus: { + nSigma = track.rich().richNsigmaPi(); + break; + } + case kKPlus: { + nSigma = track.rich().richNsigmaKa(); + break; + } + case kProton: { + nSigma = track.rich().richNsigmaPr(); + break; + } + default: { + LOGF(error, "ERROR: RICH PID not implemented for PDG %d", mPdg); + assert(false); + } + } + + if (mNSigmaRICHMinCondTOF < -999. && mNSigmaRICHMaxCondTOF > 999.) { + conditionalTOF = true; + } else { + conditionalTOF = mNSigmaRICHMinCondTOF <= nSigma && nSigma <= mNSigmaRICHMaxCondTOF; + } + return mNSigmaRICHMin <= nSigma && nSigma <= mNSigmaRICHMax; + } + + /// Returns status of RICH PID selection for a given track. + /// \param track track + /// \return RICH selection status (see TrackSelectorPID::Status) + template + int getStatusTrackPIDRICH(const T& track) + { + if (isValidTrackPIDRICH(track)) { + bool condTOF = false; + if (isSelectedTrackPIDRICH(track, condTOF)) { + return Status::PIDAccepted; // accepted + } else if (condTOF) { + return Status::PIDConditional; // potential to be accepted if combined with TOF + } else { + return Status::PIDRejected; // rejected + } + } else { + return Status::PIDNotApplicable; // PID not applicable + } + } + + // MID + + /// Checks if track is compatible with muon hypothesis in the MID detector. + /// \param track track + /// \return true if track has been identified as muon by the MID detector + template + bool isSelectedTrackPIDMID(const T& track) + { + if (mPdg != kMuonMinus) { + //LOGF(info, "isSelectedTrackPIDMID: Not a muon hypothesis"); + return false; + } + //LOGF(info, "isSelectedTrackPIDMID: Getting muon response"); + //LOGF(info, "isSelectedTrackPIDMID: Got: %d", track.mid().midIsMuon()); + //LOGF(info, "isSelectedTrackPIDMID: Return: %d", track.mid().midIsMuon() == 1); + return track.mid().midIsMuon() == 1; // FIXME: change to return track.midIsMuon() once the column is bool. + } + + /// Returns status of MID PID selection for a given track. + /// \param track track + /// \return MID selection status (see TrackSelectorPID::Status) + template + int getStatusTrackPIDMID(const T& track) + { + //LOGF(info, "getStatusTrackPIDMID: Start"); + if (mPdg != kMuonMinus) { + //LOGF(info, "getStatusTrackPIDMID: Not a muon hypothesis"); + return Status::PIDRejected; + } + if (isSelectedTrackPIDMID(track)) { + //LOGF(info, "getStatusTrackPIDMID: Accepted"); + return Status::PIDAccepted; // accepted + } else { + //LOGF(info, "getStatusTrackPIDMID: Rejected"); + return Status::PIDRejected; // rejected + } + } + + // Combined selection (TPC + TOF) + + /// Returns status of combined PID (TPC + TOF) selection for a given track. + /// \param track track + /// \return status of combined PID (TPC + TOF) (see TrackSelectorPID::Status) template int getStatusTrackPIDAll(const T& track) { @@ -290,6 +432,14 @@ class TrackSelectorPID float mNSigmaTOFMax = 3.; ///< maximum number of TOF σ float mNSigmaTOFMinCondTPC = -1000.; ///< minimum number of TOF σ if combined with TPC float mNSigmaTOFMaxCondTPC = 1000.; ///< maximum number of TOF σ if combined with TPC + + // RICH + float mPtRICHMin = 0.; ///< minimum pT for RICH PID [GeV/c] + float mPtRICHMax = 100.; ///< maximum pT for RICH PID [GeV/c] + float mNSigmaRICHMin = -3.; ///< minimum number of RICH σ + float mNSigmaRICHMax = 3.; ///< maximum number of RICH σ + float mNSigmaRICHMinCondTOF = -1000.; ///< minimum number of RICH σ if combined with TOF + float mNSigmaRICHMaxCondTOF = 1000.; ///< maximum number of RICH σ if combined with TOF }; #endif // O2_ANALYSIS_TRACKSELECTORPID_H_ diff --git a/Analysis/Tasks/PWGHF/CMakeLists.txt b/Analysis/Tasks/PWGHF/CMakeLists.txt index dced3cc182a51..6d97dbd3a1e5b 100644 --- a/Analysis/Tasks/PWGHF/CMakeLists.txt +++ b/Analysis/Tasks/PWGHF/CMakeLists.txt @@ -132,4 +132,3 @@ o2_add_dpl_workflow(hf-task-lc-tok0sp SOURCES taskLcK0sP.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore O2::DetectorsVertexing O2::AnalysisTasksUtils COMPONENT_NAME Analysis) - diff --git a/Analysis/Tasks/PWGHF/HFJpsiToEECandidateSelector.cxx b/Analysis/Tasks/PWGHF/HFJpsiToEECandidateSelector.cxx index 932c824de1c30..5d99fa6c1b9bc 100644 --- a/Analysis/Tasks/PWGHF/HFJpsiToEECandidateSelector.cxx +++ b/Analysis/Tasks/PWGHF/HFJpsiToEECandidateSelector.cxx @@ -15,29 +15,31 @@ /// \author Nima Zardoshti , CERN /// \author Vít Kučera , CERN -#include "ALICE3Analysis/RICH.h" -#include "AnalysisCore/TrackSelectorPID.h" -#include "AnalysisDataModel/HFCandidateSelectionTables.h" -#include "AnalysisDataModel/HFSecondaryVertex.h" -#include "Framework/AnalysisTask.h" #include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "AnalysisDataModel/HFSecondaryVertex.h" +#include "AnalysisDataModel/HFCandidateSelectionTables.h" +#include "AnalysisCore/TrackSelectorPID.h" +#include "ALICE3Analysis/RICH.h" +#include "ALICE3Analysis/MID.h" using namespace o2; using namespace o2::framework; using namespace o2::aod::hf_cand_prong2; using namespace o2::analysis::hf_cuts_jpsi_toee; +/* namespace o2::aod { -namespace indices +namespace indices_rich { DECLARE_SOA_INDEX_COLUMN(Track, track); DECLARE_SOA_INDEX_COLUMN(RICH, rich); -} // namespace indices +} // namespace indices_rich DECLARE_SOA_INDEX_TABLE_USER(RICHTracksIndex, Tracks, "RICHTRK", - indices::TrackId, indices::RICHId); + indices_rich::TrackId, indices_rich::RICHId); } // namespace o2::aod struct RICHindexbuilder { @@ -45,47 +47,70 @@ struct RICHindexbuilder { void init(o2::framework::InitContext&) {} }; +namespace o2::aod +{ + +namespace indices_mid +{ +DECLARE_SOA_INDEX_COLUMN(Track, track); +DECLARE_SOA_INDEX_COLUMN(MID, mid); +} // namespace indices_mid + +DECLARE_SOA_INDEX_TABLE_USER(MIDTracksIndex, Tracks, "MIDTRK", + indices_mid::TrackId, indices_mid::MIDId); +} // namespace o2::aod + +struct MidIndexBuilder { + Builds ind; + void init(o2::framework::InitContext&) {} +}; +*/ + +namespace o2::aod +{ + +namespace hf_track_index_alice3_pid +{ +DECLARE_SOA_INDEX_COLUMN(Track, track); //! +DECLARE_SOA_INDEX_COLUMN(RICH, rich); //! +DECLARE_SOA_INDEX_COLUMN(MID, mid); //! +} // namespace hf_track_index_alice3_pid + +DECLARE_SOA_INDEX_TABLE_USER(HfTrackIndexALICE3PID, Tracks, "HFTRKIDXA3PID", //! + hf_track_index_alice3_pid::TrackId, + hf_track_index_alice3_pid::RICHId, + hf_track_index_alice3_pid::MIDId); +} // namespace o2::aod + +struct Alice3PidIndexBuilder { + Builds index; + void init(o2::framework::InitContext&) {} +}; + /// Struct for applying J/ψ → e+ e− selection cuts struct HFJpsiToEECandidateSelector { Produces hfSelJpsiToEECandidate; - Configurable d_pTCandMin{"d_pTCandMin", 0., - "Lower bound of candidate pT"}; - Configurable d_pTCandMax{"d_pTCandMax", 50., - "Upper bound of candidate pT"}; + Configurable d_pTCandMin{"d_pTCandMin", 0., "Lower bound of candidate pT"}; + Configurable d_pTCandMax{"d_pTCandMax", 50., "Upper bound of candidate pT"}; // TPC - Configurable d_pidTPCMinpT{"d_pidTPCMinpT", 0.15, - "Lower bound of track pT for TPC PID"}; - Configurable d_pidTPCMaxpT{"d_pidTPCMaxpT", 10., - "Upper bound of track pT for TPC PID"}; + Configurable d_pidTPCMinpT{"d_pidTPCMinpT", 0.15, "Lower bound of track pT for TPC PID"}; + Configurable d_pidTPCMaxpT{"d_pidTPCMaxpT", 10., "Upper bound of track pT for TPC PID"}; Configurable d_nSigmaTPC{"d_nSigmaTPC", 3., "Nsigma cut on TPC only"}; - - Configurable d_pidTOFMinpT{"d_pidTOFMinpT", 0.15, - "Lower bound of track pT for TOF PID"}; - Configurable d_pidTOFMaxpT{"d_pidTOFMaxpT", 5., - "Upper bound of track pT for TOF PID"}; + //Configurable d_TPCNClsFindablePIDCut{"d_TPCNClsFindablePIDCut", 70., "Lower bound of TPC findable clusters for good PID"}; + // TOF + Configurable d_pidTOFMinpT{"d_pidTOFMinpT", 0.15, "Lower bound of track pT for TOF PID"}; + Configurable d_pidTOFMaxpT{"d_pidTOFMaxpT", 5., "Upper bound of track pT for TOF PID"}; Configurable d_nSigmaTOF{"d_nSigmaTOF", 3., "Nsigma cut on TOF only"}; + Configurable d_nSigmaTOFCombined{"d_nSigmaTOFCombined", 5., "Nsigma cut on TOF combined with TPC"}; + // RICH + Configurable d_pidRICHMinpT{"d_pidRICHMinpT", 0.15, "Lower bound of track pT for RICH PID"}; + Configurable d_pidRICHMaxpT{"d_pidRICHMaxpT", 10., "Upper bound of track pT for RICH PID"}; + Configurable d_nSigmaRICH{"d_nSigmaRICH", 3., "Nsigma cut on RICH only"}; - Configurable d_pidRICHMinpT{"d_pidRICHMinpT", 0.15, - "Lower bound of track pT for RICH PID"}; - Configurable d_pidRICHMaxpT{"d_pidRICHMaxpT", 10., - "Upper bound of track pT for RICH PID"}; - Configurable d_nSigmaRICH{"d_nSigmaRICH", 3., - "Nsigma cut on RICH only"}; - Configurable d_nSigmaTOFCombined{ - "d_nSigmaTOFCombined", 5., "Nsigma cut on TOF combined with TPC"}; - // Configurable - // d_TPCNClsFindablePIDCut{"d_TPCNClsFindablePIDCut", 70., "Lower bound of TPC - // findable clusters for good PID"}; // topological cuts - Configurable> pTBins{ - "pTBins", std::vector{hf_cuts_jpsi_toee::pTBins_v}, - "pT bin limits"}; - Configurable> cuts{ - "Jpsi_to_ee_cuts", - {hf_cuts_jpsi_toee::cuts[0], npTBins, nCutVars, pTBinLabels, - cutVarLabels}, - "Jpsi candidate selection per pT bin"}; + Configurable> pTBins{"pTBins", std::vector{hf_cuts_jpsi_toee::pTBins_v}, "pT bin limits"}; + Configurable> cuts{"Jpsi_to_ee_cuts", {hf_cuts_jpsi_toee::cuts[0], npTBins, nCutVars, pTBinLabels, cutVarLabels}, "Jpsi candidate selection per pT bin"}; /// Conjugate-independent topological cuts /// \param candidate is candidate @@ -93,8 +118,7 @@ struct HFJpsiToEECandidateSelector { /// \param trackElectron is the track with the electron hypothesis /// \return true if candidate passes all cuts template - bool selectionTopol(const T1& candidate, const T2& trackPositron, - const T2& trackElectron) + bool selectionTopol(const T1& candidate, const T2& trackPositron, const T2& trackElectron) { auto candpT = candidate.pt(); auto pTBin = findBin(pTBins, candpT); @@ -108,104 +132,96 @@ struct HFJpsiToEECandidateSelector { } // cut on invariant mass - if (std::abs(InvMassJpsiToEE(candidate) - - RecoDecay::getMassPDG(pdg::Code::kJpsi)) > - cuts->get(pTBin, "m")) { + if (std::abs(InvMassJpsiToEE(candidate) - RecoDecay::getMassPDG(pdg::Code::kJpsi)) > cuts->get(pTBin, "m")) { return false; } // cut on daughter pT - if (trackElectron.pt() < cuts->get(pTBin, "pT El") || - trackPositron.pt() < cuts->get(pTBin, "pT El")) { + if (trackElectron.pt() < cuts->get(pTBin, "pT El") || trackPositron.pt() < cuts->get(pTBin, "pT El")) { return false; } // cut on daughter DCA - need to add secondary vertex constraint here - if (std::abs(trackElectron.dcaPrim0()) > cuts->get(pTBin, "DCA_xy") || - std::abs(trackPositron.dcaPrim0()) > cuts->get(pTBin, "DCA_xy")) { + if (std::abs(trackElectron.dcaPrim0()) > cuts->get(pTBin, "DCA_xy") || std::abs(trackPositron.dcaPrim0()) > cuts->get(pTBin, "DCA_xy")) { return false; } // cut on daughter DCA - need to add secondary vertex constraint here - if (std::abs(trackElectron.dcaPrim1()) > cuts->get(pTBin, "DCA_z") || - std::abs(trackPositron.dcaPrim1()) > cuts->get(pTBin, "DCA_z")) { + if (std::abs(trackElectron.dcaPrim1()) > cuts->get(pTBin, "DCA_z") || std::abs(trackPositron.dcaPrim1()) > cuts->get(pTBin, "DCA_z")) { return false; } // cut on chi2 point of closest approach - if (std::abs(candidate.chi2PCA()) > cuts->get(pTBin, "chi2PCA")){ - return false; - } - return true; - } - template - bool validRICHPID(const T& track) - { - if (track.pt() < d_pidRICHMinpT || track.pt() >= d_pidRICHMaxpT) { + if (std::abs(candidate.chi2PCA()) > cuts->get(pTBin, "chi2PCA")) { return false; } return true; } - using Trks = soa::Join; - void process(aod::HfCandProng2 const& candidates, const Trks& tracks, - aod::RICHs const&) + using TracksPID = soa::Join; + + void process(aod::HfCandProng2 const& candidates, TracksPID const&, aod::RICHs const&, aod::MIDs const&) { TrackSelectorPID selectorElectron(kElectron); + selectorElectron.setRangePtTPC(d_pidTPCMinpT, d_pidTPCMaxpT); + selectorElectron.setRangeNSigmaTPC(-d_nSigmaTPC, d_nSigmaTPC); selectorElectron.setRangePtTOF(d_pidTOFMinpT, d_pidTOFMaxpT); selectorElectron.setRangeNSigmaTOF(-d_nSigmaTOF, d_nSigmaTOF); - selectorElectron.setRangeNSigmaTOFCondTPC(-d_nSigmaTOFCombined, - d_nSigmaTOFCombined); + selectorElectron.setRangeNSigmaTOFCondTPC(-d_nSigmaTOFCombined, d_nSigmaTOFCombined); + selectorElectron.setRangePtRICH(d_pidRICHMinpT, d_pidRICHMaxpT); + selectorElectron.setRangeNSigmaRICH(-d_nSigmaRICH, d_nSigmaRICH); + + TrackSelectorPID selectorMuon(kMuonMinus); + // looping over 2-prong candidates for (auto& candidate : candidates) { - auto trackPos = candidate.index0_as(); // positive daughter - auto trackNeg = candidate.index1_as(); // negative daughter + auto trackPos = candidate.index0_as(); // positive daughter + auto trackNeg = candidate.index1_as(); // negative daughter if (!(candidate.hfflag() & 1 << DecayType::JpsiToEE)) { hfSelJpsiToEECandidate(0); continue; } - // track selection level need to add special cuts (additional cuts on - // decay length and d0 norm) + // track selection level need to add special cuts (additional cuts on decay length and d0 norm) if (!selectionTopol(candidate, trackPos, trackNeg)) { hfSelJpsiToEECandidate(0); continue; } - // track-level PID selection - if (selectorElectron.getStatusTrackPIDTOF(trackPos) == - TrackSelectorPID::Status::PIDRejected || - selectorElectron.getStatusTrackPIDTOF(trackNeg) == - TrackSelectorPID::Status::PIDRejected) { + // track-level PID TPC selection + // FIXME: temporarily disabled for ALICE 3 development + //if (selectorElectron.getStatusTrackPIDTPC(trackPos) == TrackSelectorPID::Status::PIDRejected || + // selectorElectron.getStatusTrackPIDTPC(trackNeg) == TrackSelectorPID::Status::PIDRejected) { + // hfSelJpsiToEECandidate(0); + // continue; + //} + + // track-level PID TOF selection + if (selectorElectron.getStatusTrackPIDTOF(trackPos) == TrackSelectorPID::Status::PIDRejected || + selectorElectron.getStatusTrackPIDTOF(trackNeg) == TrackSelectorPID::Status::PIDRejected) { hfSelJpsiToEECandidate(0); continue; } - bool pidrichPos = validRICHPID(trackPos); - bool pidrichNeg = validRICHPID(trackNeg); - if (pidrichPos && pidrichNeg) { - // LOGF(info, "both good rich tracks"); + // track-level PID RICH selection + if (selectorElectron.getStatusTrackPIDRICH(trackPos) != TrackSelectorPID::Status::PIDAccepted || + selectorElectron.getStatusTrackPIDRICH(trackNeg) != TrackSelectorPID::Status::PIDAccepted) { + hfSelJpsiToEECandidate(0); + continue; } - // LOGF(info, "cut %f, val pos %f, val neg %f", d_nSigmaRICH, - // trackPos.rich().richNsigmaEl(), trackNeg.rich().richNsigmaEl()); - if (pidrichPos && pidrichNeg) { - bool selpidrichPos = abs(trackPos.rich().richNsigmaEl()) < d_nSigmaRICH; - bool selpidrichNeg = abs(trackNeg.rich().richNsigmaEl()) < d_nSigmaRICH; - // LOGF(info, "pos %d, neg %d", selpidrichPos, selpidrichNeg); - bool selpidrich = selpidrichPos && selpidrichNeg; - if (selpidrich) { - // LOGF(info, "both sel rich tracks"); - } - if (!selpidrich) { - hfSelJpsiToEECandidate(0); - // LOGF(info, "not selectedtracks"); - continue; - } + // track-level muon PID MID selection + //LOGF(info, "Muon selection: Start"); + if (selectorMuon.getStatusTrackPIDMID(trackPos) != TrackSelectorPID::Status::PIDAccepted || + selectorMuon.getStatusTrackPIDMID(trackNeg) != TrackSelectorPID::Status::PIDAccepted) { + //LOGF(info, "Muon selection: Rejected"); + hfSelJpsiToEECandidate(0); + continue; } + //LOGF(info, "Muon selection: Selected"); hfSelJpsiToEECandidate(1); } @@ -214,7 +230,9 @@ struct HFJpsiToEECandidateSelector { WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{adaptAnalysisTask(cfgc), - adaptAnalysisTask( - cfgc, TaskName{"hf-jpsi-toee-candidate-selector"})}; + return WorkflowSpec{ + //adaptAnalysisTask(cfgc), + //adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc, TaskName{"hf-jpsi-toee-candidate-selector"})}; }