From 75e17dc29a3ca420405baa95e93f0f3cdae5cdde Mon Sep 17 00:00:00 2001 From: jokonig Date: Sat, 5 Sep 2026 15:21:14 +0200 Subject: [PATCH] [PWGEM] Add Event Skimmer for Dalitz taking both V0 and Elec into account - New skimmer needs tables from photon-conversion-builder and dalitz-electron-skimmer. Then, based no the contents of these tables, the final tables are written into the AO2D - Consequently, the photon-conversion-builder and the dalitz-skimmer need to be able to write temporary tables with the same content as the original tables - Added these temporary tables to the datamodel - Templated photon-conversion-builder and Dalitz-Skimmer to be able to write both types of tables - Added new Dalitz skimmer that reads these tables and then writes the non-temporary ones --- PWGEM/Dilepton/DataModel/dileptonTables.h | 6 + PWGEM/Dilepton/TableProducer/filterEoI.cxx | 13 +- PWGEM/PhotonMeson/DataModel/gammaTables.h | 100 + .../PhotonMeson/TableProducer/CMakeLists.txt | 15 + .../TableProducer/photonconversionbuilder.cxx | 1777 +--------------- .../TableProducer/photonconversionbuilder.h | 1799 +++++++++++++++++ .../photonconversionbuilderTmpTable.cxx | 23 + .../TableProducer/skimmerDalitzEvents.cxx | 147 ++ .../skimmerPrimaryElectronFromDalitzEE.cxx | 996 +-------- .../skimmerPrimaryElectronFromDalitzEE.h | 1023 ++++++++++ ...merPrimaryElectronFromDalitzEETmpTable.cxx | 28 + 11 files changed, 3163 insertions(+), 2764 deletions(-) create mode 100644 PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h create mode 100644 PWGEM/PhotonMeson/TableProducer/photonconversionbuilderTmpTable.cxx create mode 100644 PWGEM/PhotonMeson/TableProducer/skimmerDalitzEvents.cxx create mode 100644 PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h create mode 100644 PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEETmpTable.cxx diff --git a/PWGEM/Dilepton/DataModel/dileptonTables.h b/PWGEM/Dilepton/DataModel/dileptonTables.h index 4178d0c7b89..baa7d797b26 100644 --- a/PWGEM/Dilepton/DataModel/dileptonTables.h +++ b/PWGEM/Dilepton/DataModel/dileptonTables.h @@ -962,6 +962,12 @@ using EMPrimaryElectronsDeDxMC = EMPrimaryElectronsDeDxMC_000; // iterators using EMPrimaryElectronDeDxMC = EMPrimaryElectronsDeDxMC::iterator; +// copy of EMPrimaryElectronDeDxMC to use as intermediate step for Dalitz analysis +DECLARE_SOA_TABLE(EMPrimaryElectronsDeDxMCTmp, "AOD", "EMELDEDXMCTMP", mcpidtpc::DeDxTunedMc, o2::soa::Marker<1>); +using EMPrimaryElectronsDeDxMCTmp = EMPrimaryElectronsDeDxMCTmp; +// iterators +using EMPrimaryElectronDeDxMC = EMPrimaryElectronsDeDxMC::iterator; + DECLARE_SOA_TABLE(EMPrimaryElectronEMEventIds, "AOD", "PRMELMEVENTID", emprimaryelectron::EMEventId); // To be joined with EMPrimaryElectrons table at analysis level. // iterators using EMPrimaryElectronEMEventId = EMPrimaryElectronEMEventIds::iterator; diff --git a/PWGEM/Dilepton/TableProducer/filterEoI.cxx b/PWGEM/Dilepton/TableProducer/filterEoI.cxx index 7664c491a48..8a8aab0d2b6 100644 --- a/PWGEM/Dilepton/TableProducer/filterEoI.cxx +++ b/PWGEM/Dilepton/TableProducer/filterEoI.cxx @@ -135,7 +135,11 @@ struct filterEoI { fRegistry.fill(HIST("hEventCounter"), 8); } - emeoi(does_electron_exist || does_fwdmuon_exist || does_pcm_exist || does_electronda_exist); + if constexpr (static_cast(system & kPCM) && static_cast(system & kElectronFromDalitz)) { + emeoi(does_pcm_exist && does_electronda_exist); + } else { + emeoi(does_electron_exist || does_fwdmuon_exist || does_pcm_exist || does_electronda_exist); + } } // end of collision loop @@ -171,6 +175,12 @@ struct filterEoI { selectEoI(collisions, electrons, muons, v0s, nullptr); } + void process_ElectronFromDalitz(aod::Collisions const& collisions, aod::EMPrimaryElectronsFromDalitz const& electronsda) + { + const uint8_t sysflag = kElectronFromDalitz; + selectEoI(collisions, nullptr, nullptr, nullptr, electronsda); + } + void process_PCM_ElectronFromDalitz(aod::Collisions const& collisions, aod::V0PhotonsKF const& v0s, aod::EMPrimaryElectronsFromDalitz const& electronsda) { const uint8_t sysflag = kPCM | kElectronFromDalitz; @@ -189,6 +199,7 @@ struct filterEoI { PROCESS_SWITCH(filterEoI, process_PCM, "create filter bit for PCM", false); PROCESS_SWITCH(filterEoI, process_Electron_FwdMuon, "create filter bit for Electron, FwdMuon", false); PROCESS_SWITCH(filterEoI, process_Electron_FwdMuon_PCM, "create filter bit for Electron, FwdMuon, PCM", false); + PROCESS_SWITCH(filterEoI, process_ElectronFromDalitz, "create filter bit for ElectronFromDalitz", false); PROCESS_SWITCH(filterEoI, process_PCM_ElectronFromDalitz, "create filter bit for PCM, ElectronFromDalitz", false); PROCESS_SWITCH(filterEoI, processDummy, "processDummy", true); }; diff --git a/PWGEM/PhotonMeson/DataModel/gammaTables.h b/PWGEM/PhotonMeson/DataModel/gammaTables.h index 1a5ae7e4a84..40b7534bb92 100644 --- a/PWGEM/PhotonMeson/DataModel/gammaTables.h +++ b/PWGEM/PhotonMeson/DataModel/gammaTables.h @@ -242,16 +242,56 @@ using V0Legs = V0Legs_001; // iterators using V0Leg = V0Legs::iterator; +// copy of V0Legs table +DECLARE_SOA_TABLE(V0LegsTmp, "AOD", "V0LEGTMP", //! + o2::soa::Index<>, v0leg::CollisionId, v0leg::TrackId, v0leg::Sign, + v0leg::Px, v0leg::Py, v0leg::Pz, + track::DcaXY, track::DcaZ, + track::TPCNClsFindable, track::TPCNClsFindableMinusFound, track::TPCNClsFindableMinusCrossedRows, track::TPCNClsShared, + track::TPCChi2NCl, track::TPCInnerParam, + track::TPCSignal, pidtpc::TPCNSigmaEl, pidtpc::TPCNSigmaPi, + track::ITSClusterSizes, track::ITSChi2NCl, track::DetectorMap, + + // dynamic column + v0leg::P, + v0leg::Pt, + v0leg::Eta, + v0leg::Phi, + v0leg::Tgl, + + track::TPCNClsFound, + track::TPCNClsCrossedRows, + track::TPCCrossedRowsOverFindableCls, + track::TPCFoundOverFindableCls, + track::TPCFractionSharedCls, + track::v001::ITSClusterMap, track::v001::ITSNCls, track::v001::ITSNClsInnerBarrel, + track::HasITS, track::HasTPC, track::HasTRD, track::HasTOF, + v0leg::MeanClusterSizeITS, + v0leg::MeanClusterSizeITSib, + v0leg::MeanClusterSizeITSob); +// iterators +using V0LegTmp = V0LegsTmp::iterator; + DECLARE_SOA_TABLE_VERSIONED(V0LegsXYZ_000, "AOD", "V0LEGXYZ", 0, track::X, track::Y, track::Z); using V0LegsXYZ = V0LegsXYZ_000; // iterators using V0LegXYZ = V0LegsXYZ::iterator; +// copy of V0Legs table +DECLARE_SOA_TABLE(V0LegsXYZTmp, "AOD", "V0LEGXYZTMP", track::X, track::Y, track::Z); +// iterators +using V0LegXYZTmp = V0LegsXYZTmp::iterator; + DECLARE_SOA_TABLE_VERSIONED(V0LegsDeDxMC_000, "AOD", "V0LEGDEDXMC", 0, mcpidtpc::DeDxTunedMc, o2::soa::Marker<2>); using V0LegsDeDxMC = V0LegsDeDxMC_000; // iterators using V0LegDeDxMC = V0LegsDeDxMC::iterator; +// copy of V0Legs table +DECLARE_SOA_TABLE_VERSIONED(V0LegsDeDxMCTmp, "AOD", "V0LEGDEDXMCTMP", 0, mcpidtpc::DeDxTunedMc, o2::soa::Marker<2>); +// iterators +using V0LegDeDxMCTmp = V0LegsDeDxMCTmp::iterator; + namespace emevent { DECLARE_SOA_COLUMN(NgPCM, ngpcm, int); @@ -347,6 +387,27 @@ using V0PhotonsKF = V0PhotonsKF_001; // iterators using V0PhotonKF = V0PhotonsKF::iterator; +// Clone of V0PhotonKF table for intermediate processing needed for dalitz +DECLARE_SOA_TABLE(V0PhotonsKFTmp, "AOD", "V0PHOTONKFTMP", //! + o2::soa::Index<>, v0photonkf::CollisionId, v0photonkf::V0Id, v0photonkf::PosTrackId, v0photonkf::NegTrackId, + v0photonkf::Vx, v0photonkf::Vy, v0photonkf::Vz, + v0photonkf::Px, v0photonkf::Py, v0photonkf::Pz, + v0photonkf::MGamma, + v0photonkf::DCAxyToPV, v0photonkf::DCAzToPV, + v0photonkf::CosPA, v0photonkf::CosPAXY, v0photonkf::CosPARZ, v0photonkf::PCA, + v0photonkf::Alpha, v0photonkf::QtArm, + v0photonkf::ChiSquareNDF, + + // dynamic column + v0photonkf::E, + v0photonkf::Pt, + v0photonkf::Eta, + v0photonkf::Phi, + v0photonkf::P, + v0photonkf::V0Radius); +// iterators +using V0PhotonKFTmp = V0PhotonsKFTmp::iterator; + DECLARE_SOA_TABLE(V0KFEMEventIds_000, "AOD", "V0KFEMEVENTID", v0photonkf::EMEventId); // To be joined with V0PhotonsKF table at analysis level. DECLARE_SOA_TABLE_VERSIONED(V0KFEMEventIds_001, "AOD", "V0KFEMEVENTID", 1, v0photonkf::PMEventId); // To be joined with V0PhotonsKF table at analysis level. using V0KFEMEventIds = V0KFEMEventIds_001; @@ -423,6 +484,39 @@ using EMPrimaryElectronsFromDalitz = EMPrimaryElectronsFromDalitz_001; // iterators using EMPrimaryElectronFromDalitz = EMPrimaryElectronsFromDalitz::iterator; +// Table that is a clone of EMPrimaryELectronsFromDalitz but needed for event selection purposes +DECLARE_SOA_TABLE(EMPrimaryElectronsFromDalitzTmp, "AOD", "EMPRIMARYELDATMP", //! + o2::soa::Index<>, emprimaryelectron::CollisionId, + emprimaryelectron::TrackId, emprimaryelectron::Sign, + track::Pt, track::Eta, track::Phi, track::DcaXY, track::DcaZ, track::CYY, track::CZY, track::CZZ, + track::TPCNClsFindable, track::TPCNClsFindableMinusFound, track::TPCNClsFindableMinusCrossedRows, track::TPCNClsShared, + track::TPCChi2NCl, track::TPCInnerParam, + track::TPCSignal, pidtpc::TPCNSigmaEl, pidtpc::TPCNSigmaPi, + pidtofbeta::Beta, pidtof::TOFNSigmaEl, + track::ITSClusterSizes, track::ITSChi2NCl, track::TOFChi2, track::DetectorMap, + + // dynamic column + track::TPCNClsFound, + track::TPCNClsCrossedRows, + track::TPCCrossedRowsOverFindableCls, + track::TPCFoundOverFindableCls, + track::v001::ITSClusterMap, track::v001::ITSNCls, track::v001::ITSNClsInnerBarrel, + track::TPCFractionSharedCls, + track::HasITS, track::HasTPC, track::HasTRD, track::HasTOF, + + emprimaryelectron::Signed1Pt, + emprimaryelectron::P, + emprimaryelectron::Px, + emprimaryelectron::Py, + emprimaryelectron::Pz, + emprimaryelectron::Tgl, + emprimaryelectron::MeanClusterSizeITS, + emprimaryelectron::MeanClusterSizeITSib, + emprimaryelectron::MeanClusterSizeITSob); + +// iterators +using EMPrimaryElectronFromDalitzTmp = EMPrimaryElectronsFromDalitzTmp::iterator; + namespace emprimaryelectronda { DECLARE_SOA_INDEX_COLUMN(EMEvent, emevent); //! @@ -445,6 +539,12 @@ DECLARE_SOA_TABLE(V0PhotonsPhiVPsi, "AOD", "V0PHOTONPHIVPSI", //! // iterators using V0PhotonsPhiVPsi = V0PhotonsPhiVPsi; +// clone for temporary table +DECLARE_SOA_TABLE(V0PhotonsPhiVPsiTmp, "AOD", "V0PHOTONPHIVTMP", //! + o2::soa::Index<>, v0photonsphivpsi::PhiV, v0photonsphivpsi::PsiPair); +// iterators +using V0PhotonsPhiVPsiTmp = V0PhotonsPhiVPsiTmp; + namespace dalitzee { DECLARE_SOA_INDEX_COLUMN(EMEvent, emevent); //! diff --git a/PWGEM/PhotonMeson/TableProducer/CMakeLists.txt b/PWGEM/PhotonMeson/TableProducer/CMakeLists.txt index 4088e60c489..b7eaafafcaf 100644 --- a/PWGEM/PhotonMeson/TableProducer/CMakeLists.txt +++ b/PWGEM/PhotonMeson/TableProducer/CMakeLists.txt @@ -16,6 +16,11 @@ o2physics_add_dpl_workflow(photon-conversion-builder PUBLIC_LINK_LIBRARIES O2::Framework O2::DCAFitter O2Physics::AnalysisCore O2Physics::MLCore KFParticle::KFParticle O2Physics::TPCDriftManager COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(photon-conversion-builder-tmptable + SOURCES photonconversionbuilderTmpTable.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DCAFitter O2Physics::AnalysisCore O2Physics::MLCore KFParticle::KFParticle O2Physics::TPCDriftManager + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(create-emevent-photon SOURCES createEMEventPhoton.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGJECore @@ -46,6 +51,16 @@ o2physics_add_dpl_workflow(skimmer-primary-electron-from-dalitzee PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(skimmer-primary-electron-from-dalitzee-tmptable + SOURCES skimmerPrimaryElectronFromDalitzEETmpTable.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(skimmer-dalitz-events + SOURCES skimmerDalitzEvents.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(non-lin-producer SOURCES nonLinProducer.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore diff --git a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.cxx b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.cxx index 738d18a4415..714a3ca3f72 100644 --- a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.cxx +++ b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.cxx @@ -10,1781 +10,14 @@ // or submit itself to any jurisdiction. // -/// \file photonconversionbuilder.cxx +/// \file photonconversionbuilder.ch /// \brief this task produces photon data table with KFParticle. /// \author Daiki Sekihata , Tokyo -#ifndef HomogeneousField -#define HomogeneousField // o2-linter: disable=name/macro (name coming from KFParticle, not us, needed for KFParticle::SetField) -#endif +#include "PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h" -#include "PWGEM/Dilepton/Utils/PairUtilities.h" -#include "PWGEM/PhotonMeson/Core/EmMlResponsePCM.h" -#include "PWGEM/PhotonMeson/Core/V0PhotonCandidate.h" -#include "PWGEM/PhotonMeson/Core/V0PhotonCut.h" -#include "PWGEM/PhotonMeson/DataModel/EventTables.h" -#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include "PWGEM/PhotonMeson/Utils/PCMUtilities.h" -#include "PWGEM/PhotonMeson/Utils/TrackSelection.h" - -#include "Common/Core/RecoDecay.h" -#include "Common/Core/TPCVDriftManager.h" -#include "Common/Core/trackUtilities.h" -#include "Common/DataModel/Centrality.h" -#include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/PIDResponseTPC.h" -#include "Tools/KFparticle/KFUtilities.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace o2; -using namespace o2::soa; -using namespace o2::framework; -using namespace o2::framework::expressions; -using namespace o2::constants::physics; -using namespace o2::pwgem::photonmeson; -using std::array; - -using MyCollisions = soa::Join; -// using MyCollisionsWithSWT = soa::Join; -using MyCollisionsMC = soa::Join; -using MyBCs = soa::Join; - -using MyTracksIU = soa::Join; -using MyTracksIUMC = soa::Join; - -enum MatCorrType { - None = 0, - TGeo = 1, - LUT = 2 -}; - -enum TrackPropMode { - kProper = 0, - kFast = 1, - kBoth = 2 -}; - -enum V0DeduplicationMode { - Pairwise = 0, // old algorithm, V0 with best PCA + cosPA at the same time in a group wins - GreedyMatching = 1, // new algorithm, best score wins (score combines PCA and cosPA -- see detailed implementation in PCMUtilities.cxx) - GroupMatching = 2, // new algorithm, best combination of leg-disjoint V0s wins, not the single best V0 - KeepAll = 3 // no deduplication, keep all V0s -}; - -// Struct needed for deduplication mode 1. Used to keep track of v0 properties and a score based on pca and cosPA -struct V0CandidateHelper { - int v0ID = -1; - int colID = -1; - int posID = -1; - int eleID = -1; - float cosPA = -1.f; - float pca = -1.f; - float score = -1.f; - float mee = 0.f; // e+e- mass at the secondary vertex (GeV/c^2) - float pxAtPV = 0.f; - float pyAtPV = 0.f; - float pzAtPV = 0.f; - - V0CandidateHelper() = default; - - V0CandidateHelper(int v0, int col, int pos, int ele, float cpa, float p, float s, float m = 0.f) - : v0ID(v0), colID(col), posID(pos), eleID(ele), cosPA(cpa), pca(p), score(s), mee(m) - { - } - - bool operator==(const V0CandidateHelper& other) const { return score == other.score; } - bool operator<(const V0CandidateHelper& other) const { return score < other.score; } - bool operator>(const V0CandidateHelper& other) const { return score > other.score; } -}; - -// (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) -using CandKey = std::tuple; - -// Everything the truth diagnosis need, -struct DedupDiag { - std::map> blockersByKey; // rejected candidate -> ALL candidates that took one of its legs - std::vector snapshot; // ALL candidates, before deduplication - std::vector stored; // the survivors - std::map> lossMargin; -}; - -struct PhotonConversionBuilder { - Produces v0photonskf; - Produces v0legs; - Produces v0legsXYZ; - Produces v0legsDeDxMC; - Produces v0photonsphivpsi; - // Produces v0photonskfcov; - // Produces events_ngpcm; - - // CCDB options - Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; - Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; - - // Operation and minimisation criteria - Configurable d_bz_input{"d_bz", -999, "bz field, -999 is automatic"}; - Configurable useMatCorrType{"useMatCorrType", 0, "0: none, 1: TGeo, 2: LUT"}; - Configurable modeTrackPropagation{"modeTrackPropagation", 0, "0: use real track propagation, including material, 1: use fast approximation using only geometry, 2: Use real track propagation and make comparison to fast propagation (only for debugging and testing)"}; - Configurable propV0LegsRadius{"propV0LegsRadius", 60.f, "Radius to which the V0 legs are propagated to calculate psipair and phiV"}; - - // deduplication settings - struct : ConfigurableGroup { - std::string prefix = "dedup_group"; - Configurable deduplicationMode{"deduplicationMode", 0, "0: Pairwise deduplication, 1: Based on Greedy matching (best score wins), 2: Based on Group matching (crossed pairs are both kept), 3: Keep all V0s, our default in the config is mode 0, however if a wrong configuration is used, the default will be mode 1 (Greedy matching) to avoid crashes"}; - Configurable deduplicationScoreWeight{"deduplicationScoreWeight", 0.5f, "0.:only pca goes into the score, 1: only cosPA goes itno score, any number in between is a mix of pca and cosPA"}; - Configurable cfgDedupTruthMaps{"cfgDedupTruthMaps", true, "fill the fine (dEta, dPhi, q) truth maps in MC"}; - Configurable dedupMaxGroupSize{"dedupMaxGroupSize", 12, "group matching (mode 2): conflict groups up to this size are solved exactly, larger ones greedily (capped at 16)"}; - } dedup; - - // event-level skim for derived data - struct : ConfigurableGroup { - std::string prefix = "skim_group"; - Configurable minNv0PerCollision{"minNv0PerCollision", -1, "store V0 photons only for collisions with at least this many surviving candidates (event-level skim for derived data). negative = store all"}; - Configurable maxMinvPairPerCollision{"maxMinvPairPerCollision", -1.f, "store a collision's photons only if at least one photon pair has qinv below this (GeV/c^2). negative = disabled"}; - } skim; - - // single track cuts - struct : ConfigurableGroup { - std::string prefix = "trackcut_group"; - Configurable min_ncluster_tpc{"min_ncluster_tpc", 0, "min ncluster tpc"}; - Configurable mincrossedrows{"mincrossedrows", 40, "min crossed rows"}; - Configurable moveTPCTracks{"moveTPCTracks", true, "Move TPC-only tracks under the collision assumption"}; - Configurable disableITSonlyTracks{"disableITSonlyTracks", false, "disable ITSonly tracks in V0 legs"}; - Configurable disableTPConlyTracks{"disableTPConlyTracks", false, "disable TPConly tracks in V0 legs"}; - Configurable requireITShit{"requireITShit", false, "require ITS hit to V0 legs"}; - Configurable maxchi2tpc{"maxchi2tpc", 5.0, "max chi2/NclsTPC"}; // default 4.0 + 1.0 - Configurable maxchi2its{"maxchi2its", 6.0, "max chi2/NclsITS"}; // default 5.0 + 1.0 - Configurable maxpt_itsonly{"maxpt_itsonly", 0.15, "max pT for ITSonly tracks at SV"}; - Configurable maxTPCNsigmaEl{"maxTPCNsigmaEl", 4.0, "max. TPC n sigma for electron"}; - Configurable minTPCNsigmaEl{"minTPCNsigmaEl", -4.0, "min. TPC n sigma for electron"}; - Configurable max_frac_shared_clusters_tpc{"max_frac_shared_clusters_tpc", 999.f, "max fraction of shared clusters in TPC"}; - Configurable dcanegtopv{"dcanegtopv", 0.1, "DCA Neg To PV"}; - Configurable dcapostopv{"dcapostopv", 0.1, "DCA Pos To PV"}; - Configurable maxX{"maxX", 83.1, "max X for track IU"}; - Configurable min_pt_trackiu{"min_pt_trackiu", 0.05, "min pT for trackiu"}; // this comes from online processing. pT of track seed is above 50 MeV/c in B = 0.5 T, 20 MeV/c in B = 0.2 T. - } trackcuts; - - // v0 photon cuts - struct : ConfigurableGroup { - std::string prefix = "v0cut_group"; - Configurable min_v0cospa_tpconly{"min_v0cospa_tpconly", 0.99, "min V0 CosPA to V0s with TPConly tracks"}; // double -> N.B. dcos(x)/dx = 0 at x=0) - Configurable min_v0cospa_its{"min_v0cospa_its", 0.99, "min V0 CosPA to V0s with ITs hits"}; // double -> N.B. dcos(x)/dx = 0 at x=0) - Configurable max_dcav0dau_tpconly{"max_dcav0dau_tpconly", 3.0, "max distance btween 2 legs to V0s with TPConly tracks"}; - Configurable max_dcav0dau_its{"max_dcav0dau_its", 0.5, "max distance btween 2 legs to V0s with ITS hits"}; - Configurable max_dcav0dau_itsibss{"max_dcav0dau_itsibss", 1.0, "max distance btween 2 legs to V0s with ITS hits on ITSib SS"}; - Configurable max_dcav0dau_tpc_inner_fc{"max_dcav0dau_tpc_inner_fc", 1.5, "max distance btween 2 legs to V0s with ITS hits on TPC inner FC"}; - Configurable min_v0radius{"min_v0radius", 1.0, "min v0 radius"}; - Configurable max_v0radius{"max_v0radius", 90.0, "max v0 radius"}; - Configurable margin_r_its{"margin_r_its", 3.0, "margin for r cut in cm"}; - Configurable margin_r_tpc{"margin_r_tpc", 7.0, "margin for r cut in cm"}; - Configurable margin_r_itstpc_tpc{"margin_r_itstpc_tpc", 7.0, "margin for r cut in cm"}; - Configurable margin_z{"margin_z", 7.0, "margin for z cut in cm"}; - Configurable max_alpha_ap{"max_alpha_ap", 0.95, "max alpha for AP cut"}; - Configurable max_qt_ap{"max_qt_ap", 0.01, "max qT for AP cut"}; - Configurable min_pt_v0{"min_pt_v0", 0.1, "min pT for v0 photons at PV"}; - Configurable max_pt_v0_itsonly{"max_pt_v0_itsonly", 0.3, "max pT for v0 photons wth 2 ITSonly tracks at PV"}; - Configurable max_eta_v0{"max_eta_v0", 0.9, "max eta for v0 photons at PV"}; - Configurable kfMassConstrain{"kfMassConstrain", -1.f, "mass constrain for the KFParticle mother particle"}; - Configurable max_r_req_its{"max_r_req_its", 16.0, "max Rxy for V0 with ITS hits"}; - Configurable min_r_tpconly{"min_r_tpconly", 32.0, "min Rxy for V0 with TPConly tracks"}; - Configurable max_r_itsmft_ss{"max_r_itsmft_ss", 66.0, "max Rxy for ITS/MFT SS"}; - Configurable max_dcatopv_xy_v0{"max_dcatopv_xy_v0", +1e+10, "max. DCAxy to PV for V0"}; - Configurable max_dcatopv_z_v0{"max_dcatopv_z_v0", +1e+10, "max. DCAz to PV for V0"}; - Configurable reject_v0_on_itsib{"reject_v0_on_itsib", true, "flag to reject v0s on ITSib"}; - } v0cuts; - - // PCM ML inference - struct : ConfigurableGroup { - std::string prefix = "mlcut_group"; - Configurable applyPCMMl{"applyPCMMl", false, "Flag to apply ML selections"}; - Configurable use2DBinning{"use2DBinning", false, "Flag to enable/disable 2D binning for ML application"}; - Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; - Configurable nClassesPCMMl{"nClassesPCMMl", static_cast(o2::analysis::em_cuts_ml::NCutScores), "Number of classes in ML model"}; - Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; - Configurable centTypePCMMl{"centTypePCMMl", 2, "Centrality type for 2D ML application: FT0M:0, FT0A:1, FT0C:2"}; - Configurable> cutDirPCMMl{"cutDirPCMMl", std::vector{o2::analysis::em_cuts_ml::vecCutDir}, "Whether to reject score values greater or smaller than the threshold"}; - Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature1", "feature2"}, "Names of ML model input features"}; - Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{"path_ccdb/BDT_PCM/"}, "Paths of models on CCDB"}; - Configurable> onnxFileNames{"onnxFileNames", std::vector{"ModelHandler_onnx_PCM.onnx"}, "ONNX file names for each pT bin (if not from CCDB full path)"}; - Configurable> labelsBinsPCMMl{"labelsBinsPCMMl", std::vector{"bin 0", "bin 1"}, "Labels for bins"}; - Configurable> labelsCutScoresPCMMl{"labelsCutScoresPCMMl", std::vector{o2::analysis::em_cuts_ml::labelsCutScore}, "Labels for cut scores"}; - Configurable> binsPtPCMMl{"binsPtPCMMl", std::vector{0.0, +1e+10}, "pT bin limits for ML application"}; - Configurable> binsCentPCMMl{"binsCentPCMMl", std::vector{0.0, 100.0}, "Centrality bin limits for ML application"}; - Configurable> cutsPCMMlFlat{"cutsPCMMlFlat", {0.5}, "Flattened ML cuts: [bin0_score0, bin0_score1, ..., binN_scoreM]"}; - } mlcuts; - - o2::analysis::EmMlResponsePCM emMlResponse; - std::vector outputML; - V0PhotonCandidate v0photoncandidate; - o2::ccdb::CcdbApi ccdbApi; - - int mRunNumber{}; - float d_bz{}; - float maxSnp{}; // max sine phi for propagation - float maxStep{}; // max step size (cm) for propagation - Service ccdb{}; - o2::base::MatLayerCylSet* lut = nullptr; - o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; - o2::aod::common::TPCVDriftManager mVDriftMgr; - - HistogramRegistry registry{ - "registry", - { - {"hCollisionCounter", "hCollisionCounter", {HistType::kTH1F, {{1, 0.5f, 1.5f}}}}, - {"V0/hAP", "Armenteros Podolanski;#alpha;q_{T} (GeV/c)", {HistType::kTH2F, {{200, -1.0f, 1.0f}, {250, 0, 0.25}}}}, - {"V0/hConversionPointXY", "conversion point in XY;X (cm);Y (cm)", {HistType::kTH2F, {{400, -100.0f, 100.0f}, {400, -100.f, 100.f}}}}, - {"V0/hConversionPointRZ", "conversion point in RZ;Z (cm);R_{xy} (cm)", {HistType::kTH2F, {{200, -100.0f, 100.0f}, {200, 0.f, 100.f}}}}, - {"V0/hPt", "pT of V0 at PV;p_{T,#gamma} (GeV/c)", {HistType::kTH1F, {{1000, 0.0f, 10.0f}}}}, - {"V0/hEtaPhi", "#eta vs. #varphi of V0 at PV;#varphi (rad.);#eta", {HistType::kTH2F, {{72, 0.0f, o2::constants::math::TwoPI}, {200, -1, +1}}}}, - {"V0/hCosPA", "cosine of pointing angle;cosine of pointing angle", {HistType::kTH1F, {{100, 0.99f, 1.f}}}}, - {"V0/hCosPA_Rxy", "cosine of pointing angle;r_{xy} (cm);cosine of pointing angle", {HistType::kTH2F, {{200, 0, 100}, {100, 0.99f, 1.f}}}}, - {"V0/hCosPAXY_Rxy", "cosine of pointing angle;r_{xy} (cm);cosine of pointing angle", {HistType::kTH2F, {{200, 0, 100}, {100, 0.99f, 1.f}}}}, - {"V0/hCosPARZ_Rxy", "cosine of pointing angle;r_{xy} (cm);cosine of pointing angle", {HistType::kTH2F, {{200, 0, 100}, {100, 0.99f, 1.f}}}}, - {"V0/hPCA", "distance between 2 legs at SV;PCA (cm)", {HistType::kTH1F, {{500, 0.0f, 5.f}}}}, - {"V0/hPCA_Rxy", "distance between 2 legs at SV;R_{xy} (cm);PCA (cm)", {HistType::kTH2F, {{200, 0, 100}, {500, 0.0f, 5.f}}}}, - {"V0/hPCA_CosPA", "distance between 2 legs at SV vs. cosPA;cosine of pointing angle;PCA (cm)", {HistType::kTH2F, {{100, 0.99, 1}, {500, 0.0f, 5.f}}}}, - {"V0/hDCAxyz", "DCA to PV;DCA_{xy} (cm);DCA_{z} (cm)", {HistType::kTH2F, {{200, -5.f, +5.f}, {200, -5.f, +5.f}}}}, - {"V0/hMeeSV_Rxy", "mee at SV vs. R_{xy};R_{xy} (cm);m_{ee} at SV (GeV/c^{2})", {HistType::kTH2F, {{200, 0.0f, 100.f}, {100, 0, 0.1f}}}}, - {"V0/hRxy_minX_ITSonly_ITSonly", "min trackiu X vs. R_{xy};trackiu X (cm);min trackiu X - R_{xy} (cm)", {HistType::kTH2F, {{100, 0.0f, 100.f}, {100, -50.0, 50.0f}}}}, - {"V0/hRxy_minX_ITSTPC_ITSTPC", "min trackiu X vs. R_{xy};trackiu X (cm);min trackiu X - R_{xy} (cm)", {HistType::kTH2F, {{100, 0.0f, 100.f}, {100, -50.0, 50.0f}}}}, - {"V0/hRxy_minX_ITSTPC_ITSonly", "min trackiu X vs. R_{xy};trackiu X (cm);min trackiu X - R_{xy} (cm)", {HistType::kTH2F, {{100, 0.0f, 100.f}, {100, -50.0, 50.0f}}}}, - {"V0/hRxy_minX_ITSTPC_TPC", "min trackiu X vs. R_{xy};trackiu X (cm);min trackiu X - R_{xy} (cm)", {HistType::kTH2F, {{100, 0.0f, 100.f}, {100, -50.0, 50.0f}}}}, - {"V0/hRxy_minX_TPC_TPC", "min trackiu X vs. R_{xy};trackiu X (cm);min trackiu X - R_{xy} (cm)", {HistType::kTH2F, {{100, 0.0f, 100.f}, {100, -50.0, 50.0f}}}}, - {"V0/hPCA_diffX", "PCA vs. trackiu X - R_{xy};distance btween 2 legs (cm);min trackiu X - R_{xy} (cm)", {HistType::kTH2F, {{500, 0.0f, 5.f}, {100, -50.0, 50.0f}}}}, - {"V0/hPhiVPsiPair", "phiV vs. psi pair;#psi_{pair} (rad.);#phi_{V} (rad.)", {HistType::kTH2F, {{500, -o2::constants::math::PI, o2::constants::math::PI}, {500, 0.0f, o2::constants::math::TwoPI}}}}, - {"V0Leg/hPt", "pT of leg at SV;p_{T,e} (GeV/c)", {HistType::kTH1F, {{1000, 0.0f, 10.0f}}}}, - {"V0Leg/hEtaPhi", "#eta vs. #varphi of leg at SV;#varphi (rad.);#eta", {HistType::kTH2F, {{72, 0.0f, o2::constants::math::TwoPI}, {200, -1, +1}}}}, - {"V0Leg/hRelDeltaPt", "pT resolution;p_{T} (GeV/c);#Deltap_{T}/p_{T}", {HistType::kTH2F, {{1000, 0.f, 10.f}, {100, 0, 1}}}}, - {"V0Leg/hDCAxyz", "DCA xy vs. z to PV;DCA_{xy} (cm);DCA_{z} (cm)", {HistType::kTH2F, {{200, -50.f, 50.f}, {200, -50.f, +50.f}}}}, - {"V0Leg/hdEdx_Pin", "TPC dE/dx vs. p_{in};p_{in} (GeV/c);TPC dE/dx", {HistType::kTH2F, {{1000, 0.f, 10.f}, {200, 0.f, 200.f}}}}, - {"V0Leg/hTPCNsigmaEl", "TPC dE/dx vs. p_{in};p_{in} (GeV/c);n #sigma_{e}^{TPC}", {HistType::kTH2F, {{1000, 0.f, 10.f}, {100, -5.f, +5.f}}}}, - {"V0Leg/hXZ", "track iu x vs. z;z (cm);x (cm)", {HistType::kTH2F, {{200, -100.f, 100.f}, {200, 0.f, 100.f}}}}, - }}; - - void init(InitContext&) - { - mRunNumber = 0; - d_bz = 0; - maxSnp = 0.85f; // could be changed later - maxStep = 2.00f; // could be changed later - - static constexpr std::array DedupNames = {"pairwise", "greedy matching", "group matching", "keep all"}; - if (dedup.deduplicationMode < 0 || dedup.deduplicationMode >= static_cast(DedupNames.size())) { - LOG(fatal) << "unknown deduplicationMode " << dedup.deduplicationMode.value; - } - LOGF(info, "photon-conversion-builder: deduplicationMode = %d (%s), score weight = %.2f", - dedup.deduplicationMode.value, DedupNames[dedup.deduplicationMode.value], dedup.deduplicationScoreWeight.value); - LOGF(info, "photon-conversion-builder: minNv0PerCollision = %d, maxMinvPairPerCollision = %f", - skim.minNv0PerCollision.value, skim.maxMinvPairPerCollision.value); - - ccdb->setURL(ccdburl); - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); - ccdb->setFatalWhenNull(false); - switch (useMatCorrType) { - case MatCorrType::TGeo: - LOGF(info, "TGeo correction requested, loading geometry"); - if (!o2::base::GeometryManager::isGeometryLoaded()) { - ccdb->get(geoPath); - } - matCorr = o2::base::Propagator::MatCorrType::USEMatCorrTGeo; - break; - case MatCorrType::LUT: - LOGF(info, "LUT correction requested, loading LUT"); - lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get(lutPath)); - matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; - break; - default: - LOGF(info, "no correction requested, loading LUT by default!"); - lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get(lutPath)); - matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; - break; - } - - if (mlcuts.applyPCMMl) { - if (mlcuts.use2DBinning) { - int binsNPt = static_cast(mlcuts.binsPtPCMMl->size()) - 1; - int binsNCent = static_cast(mlcuts.binsCentPCMMl->size()) - 1; - int binsN = binsNPt * binsNCent; - if (binsN * static_cast(mlcuts.cutDirPCMMl->size()) != static_cast(mlcuts.cutsPCMMlFlat->size())) { - LOG(fatal) << "Mismatch in number of bins and cuts provided for 2D ML application: binsN * cutDirPCMMl: " << binsN * static_cast(mlcuts.cutDirPCMMl->size()) << " bins vs. cutsPCMMlFlat: " << mlcuts.cutsPCMMlFlat->size() << " cuts"; - } - if (binsN != static_cast(mlcuts.onnxFileNames->size())) { - LOG(fatal) << "Mismatch in number of bins and ONNX files provided for 2D ML application: binsN " << binsN << " bins vs. onnxFileNames: " << mlcuts.onnxFileNames->size() << " ONNX files"; - } - if (binsN != static_cast(mlcuts.labelsBinsPCMMl->size())) { - LOG(fatal) << "Mismatch in number of bins and labels provided for 2D ML application: binsN:" << binsN << " bins vs. labelsBinsPCMMl: " << mlcuts.labelsBinsPCMMl->size() << " labels"; - } - if (static_cast(mlcuts.cutDirPCMMl->size()) != mlcuts.nClassesPCMMl) { - LOG(fatal) << "Mismatch in number of classes and cut directions provided for 2D ML application: nClassesPCMMl: " << mlcuts.nClassesPCMMl << " classes vs. cutDirPCMMl: " << mlcuts.cutDirPCMMl->size() << " cut directions"; - } - if (static_cast(mlcuts.labelsCutScoresPCMMl->size()) != mlcuts.nClassesPCMMl) { - LOG(fatal) << "Mismatch in number of labels for cut scores and number of classes provided for 2D ML application: nClassesPCMMl: " << mlcuts.nClassesPCMMl << " classes vs. labelsCutScoresPCMMl: " << mlcuts.labelsCutScoresPCMMl->size() << " labels"; - } - LabeledArray cutsPCMMl(mlcuts.cutsPCMMlFlat->data(), binsN, mlcuts.nClassesPCMMl, mlcuts.labelsBinsPCMMl, mlcuts.labelsCutScoresPCMMl); - emMlResponse.configure2D(mlcuts.binsPtPCMMl, mlcuts.binsCentPCMMl, cutsPCMMl, mlcuts.cutDirPCMMl, mlcuts.nClassesPCMMl); - } else { - int binsNPt = static_cast(mlcuts.binsPtPCMMl->size()) - 1; - if (binsNPt * static_cast(mlcuts.cutDirPCMMl->size()) != static_cast(mlcuts.cutsPCMMlFlat->size())) { - LOG(fatal) << "Mismatch in number of pT bins and cuts provided for ML application: binsNPt * cutDirPCMMl:" << binsNPt * mlcuts.cutDirPCMMl->size() << " bins vs. cutsPCMMlFlat: " << mlcuts.cutsPCMMlFlat->size() << " cuts"; - } - if (binsNPt != static_cast(mlcuts.onnxFileNames->size())) { - LOG(fatal) << "Mismatch in number of pT bins and ONNX files provided for ML application: binsNPt " << binsNPt << " bins vs. onnxFileNames: " << mlcuts.onnxFileNames->size() << " ONNX files"; - } - if (binsNPt != static_cast(mlcuts.labelsBinsPCMMl->size())) { - LOG(fatal) << "Mismatch in number of pT bins and labels provided for ML application: binsNPt:" << binsNPt << " bins vs. labelsBinsPCMMl: " << mlcuts.labelsBinsPCMMl->size() << " labels"; - } - if (mlcuts.nClassesPCMMl != static_cast(mlcuts.cutDirPCMMl->size())) { - LOG(fatal) << "Mismatch in number of classes and cut directions provided for ML application: nClassesPCMMl: " << mlcuts.nClassesPCMMl << " classes vs. cutDirPCMMl: " << mlcuts.cutDirPCMMl->size() << " cut directions"; - } - if (static_cast(mlcuts.labelsCutScoresPCMMl->size()) != mlcuts.nClassesPCMMl) { - LOG(fatal) << "Mismatch in number of labels for cut scores and number of classes provided for ML application: nClassesPCMMl:" << mlcuts.nClassesPCMMl << " classes vs. labelsCutScoresPCMMl: " << mlcuts.labelsCutScoresPCMMl->size() << " labels"; - } - LabeledArray cutsPCMMl(mlcuts.cutsPCMMlFlat->data(), binsNPt, mlcuts.nClassesPCMMl, mlcuts.labelsBinsPCMMl, mlcuts.labelsCutScoresPCMMl); - emMlResponse.configure(mlcuts.binsPtPCMMl, cutsPCMMl, mlcuts.cutDirPCMMl, mlcuts.nClassesPCMMl); - } - if (mlcuts.loadModelsFromCCDB) { - ccdbApi.init(ccdburl); - emMlResponse.setModelPathsCCDB(mlcuts.onnxFileNames, ccdbApi, mlcuts.modelPathsCCDB, mlcuts.timestampCCDB); - } else { - emMlResponse.setModelPathsLocal(mlcuts.onnxFileNames); - } - emMlResponse.cacheInputFeaturesIndices(mlcuts.namesInputFeatures); - emMlResponse.init(); - if (mlcuts.nClassesPCMMl == o2::analysis::NClassesML::kTwo) { - registry.add("V0/hBDTBackgroundScoreBeforeCutVsPt", "BDT background score before cut vs pT; pT (GeV/c); BDT background score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); - registry.add("V0/hBDTBackgroundScoreAfterCutVsPt", "BDT background score after cut vs pT; pT (GeV/c); BDT background score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); - registry.add("V0/hBDTSignalScoreBeforeCutVsPt", "BDT signal score before cut vs pT; pT (GeV/c); BDT signal score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); - registry.add("V0/hBDTSignalScoreAfterCutVsPt", "BDT signal score after cut vs pT; pT (GeV/c); BDT signal score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); - } else if (mlcuts.nClassesPCMMl == o2::analysis::NClassesML::kThree) { - registry.add("V0/hBDTBackgroundScoreBeforeCutVsPt", "BDT background score before cut vs pT; pT (GeV/c); BDT background score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); - registry.add("V0/hBDTBackgroundScoreAfterCutVsPt", "BDT background score after cut vs pT; pT (GeV/c); BDT background score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); - registry.add("V0/hBDTPrimaryPhotonScoreBeforeCutVsPt", "BDT primary photon score before cut vs pT; pT (GeV/c); BDT primary photon score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); - registry.add("V0/hBDTPrimaryPhotonScoreAfterCutVsPt", "BDT primary photon score after cut vs pT; pT (GeV/c); BDT primary photon score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); - registry.add("V0/hBDTSecondaryPhotonScoreBeforeCutVsPt", "BDT secondary photon score before cut vs pT; pT (GeV/c); BDT secondary photon score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); - registry.add("V0/hBDTSecondaryPhotonScoreAfterCutVsPt", "BDT secondary photon score after cut vs pT; pT (GeV/c); BDT secondary photon score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); - } else { - registry.add("V0/hBDTScoreBeforeCutVsPt", "BDT score before cut vs pT; pT (GeV/c); BDT score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); - registry.add("V0/hBDTScoreAfterCutVsPt", "BDT score after cut vs pT; pT (GeV/c); BDT score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); - } - } - - // Compare proper propagation and fast geometrical propagation - if (modeTrackPropagation == TrackPropMode::kBoth) { - registry.add("V0Leg/hDCAxyPropagationCompare", "Comparison of DCA_{xy} propagation;DCA_{xy} (cm) proper propagation; DCA_{xy} (cm) geom. propagation", {HistType::kTH2F, {{200, -10., 10.}, {200, -10., 10.}}}); - registry.add("V0Leg/hDCAzPropagationCompare", "Comparison of DCA_{z} propagation;DCA_{z} (cm) proper propagation; DCA_{z} (cm) geom. propagation", {HistType::kTH2F, {{200, -10., 10.}, {200, -10., 10.}}}); - registry.add("V0/hPhivPropagationCompare", "Comparison of #phi_{v};#phi_{v} proper propagation; #phi_{v} geom. propagation", {HistType::kTH2F, {{100, 0., 1.6}, {100, 0., 1.6}}}); - registry.add("V0/hPsiPairPropagationCompare", "Comparison of #Psi_{pair};#Psi_{pair} proper propagation; #Psi_{pair} geom. propagation", {HistType::kTH2F, {{100, 0., 1.6}, {100, 0., 1.6}}}); - } - - // Make sure the dedup.deduplicationScoreWeight is between 0 and 1 - if (dedup.deduplicationScoreWeight > 1.f) { // o2-linter: disable=magic-number (score has to be below unity) - LOG(warning) << "deduplicationScoreWeight is larger than unity which is not allowed"; - } - if (dedup.deduplicationScoreWeight < 0.f) { // o2-linter: disable=magic-number (score has to be above zero) - LOG(warning) << "deduplicationScoreWeight is smaller than zero which is not allowed"; - } - - if (doprocessMC) { - const AxisSpec axQ{60, 0.f, 0.3f, "q_{inv}^{true} (GeV/c)"}; - const AxisSpec axDEta{80, -1.6f, 1.6f, "#Delta#eta_{#gamma#gamma}^{true}"}; - const AxisSpec axClass{3, -0.5f, 2.5f, "0 = true, 1 = cross-leg fake, 2 = other fake"}; - // lostByPartnerFake is a subset of lostByCrossFake, and a pair can appear in several of them. - for (const auto& nm : {"before", "bothSurvive", "bothSurviveSameColl", "oneLost", "bothLost", - "lostByCrossFake", "lostByPartnerFake", "lostByOtherFake", "lostByTrue"}) { - const std::string dir = std::string("MCDedup/Pairs/") + nm + "/"; - registry.add((dir + "hQ").c_str(), "truth photon pairs with >=1 true V0 candidate before deduplication;q_{inv}^{true} (GeV/c);pairs", kTH1F, {axQ}, true); - registry.add((dir + "hDEta").c_str(), "truth photon pairs with >=1 true V0 candidate before deduplication;#Delta#eta^{true};pairs", kTH1F, {axDEta}, true); - } - registry.add("MCDedup/Candidates/hClassStage", "candidate composition;class;0 = before, 1 = stored", kTH2F, {axClass, {2, -0.5f, 1.5f}}, true); - - const AxisSpec axBlocker{4, -0.5f, 3.5f, "0 = true, 1 = cross-leg fake, 2 = other fake, 3 = no blocker (cut, not dedup)"}; - - registry.add("MCDedup/Photons/hFate", "fate of the true photons;0 = alive, 1 = lost (blocked), 2 = lost (no blocker);photons", kTH1F, {{3, -0.5f, 2.5f}}, true); - registry.add("MCDedup/Photons/hBlockerClass", "blockers of a killed true photon (ENTRIES = blockers, not photons);class of the blocker;blocker entries", kTH1F, {axBlocker}, true); - registry.add("MCDedup/Photons/hKillMargin", "mode 0/1 only;S_{victim} - S_{winner};kills", kTH1F, {{200, 0.f, 1.f}}, true); - registry.add("MCDedup/Photons/hVictimVsWinnerPCA", "PCA of the two;PCA_{victim} (cm);PCA_{winner} (cm)", kTH2F, {{60, 0.f, 3.f}, {60, 0.f, 3.f}}, true); - // Reason for photon building failure - // x = 0 -> an equally large solution containing it existed - // x > 0 -> keeping it would have cost x candidates - registry.add("MCDedup/Photons/hLostMargin", "why the true photon lost (mode 2);cardinality deficit;#Sigma S(best set with it) - #Sigma S(winner)", kTH2F, {{5, -0.5f, 4.5f}, {100, 0.f, 2.f}}, true); - if (dedup.cfgDedupTruthMaps) { - const AxisSpec axDEtaFine{640, -1.6f, 1.6f, "#Delta#eta^{true}"}; - const AxisSpec axDPhi{144, -o2::constants::math::PI, o2::constants::math::PI, "#Delta#varphi^{true} (rad)"}; - for (const auto& nm : {"before", "bothSurvive", "bothLost", "lostByPartnerFake"}) { - registry.add((std::string("MCDedup/Pairs/") + nm + "/hMap").c_str(), "truth photon pairs", kTHnSparseF, {axDEtaFine, axDPhi, axQ}, true); - } - } - } - } - - void initCCDB(MyBCs::iterator const& bc) - { - if (mRunNumber == bc.runNumber()) { - return; - } - mRunNumber = bc.runNumber(); - // In case override, don't proceed, please - no CCDB access required - if (d_bz_input > -990) { // o2-linter: disable=magic-number (override value) - d_bz = d_bz_input; - o2::parameters::GRPMagField grpmag; - if (std::fabs(d_bz) > 1e-5) { // o2-linter: disable=magic-number (override value) - grpmag.setL3Current(30000.f / (d_bz / 5.0f)); // o2-linter: disable=magic-number (override value) - } - o2::base::Propagator::initFieldFromGRP(&grpmag); - - return; - } - - auto run3grp_timestamp = bc.timestamp(); - o2::base::Propagator::initFieldFromGRP(&bc.grpMagField()); - // Fetch magnetic field from ccdb for current collision - d_bz = bc.grpMagField().getNominalL3Field(); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; - - if (useMatCorrType == 2) { // o2-linter: disable=magic-number (material budget correction) - // setMatLUT only after magfield has been initalized (setMatLUT has implicit and problematic init field call if not) - o2::base::Propagator::Instance()->setMatLUT(lut); - } - /// Set magnetic field for KF vertexing - const float magneticField = o2::base::Propagator::Instance()->getNominalBz(); - KFParticle::SetField(magneticField); - - mVDriftMgr.init(&ccdb->instance()); - } - - void updateCCDB(MyBCs::iterator const& bc) - { - auto timestamp = bc.timestamp(); - - mVDriftMgr.update(timestamp); - } - - std::pair> its_ib_Requirement = {0, {0, 1, 2}}; // no hit on 3 ITS ib layers. - template - bool checkV0leg(TTrack const& track) - { - if constexpr (isMC) { - if (!track.has_mcParticle()) { - return false; - } - } - - if (trackcuts.disableITSonlyTracks && isITSonlyTrack(track)) { - return false; - } - - if (trackcuts.disableTPConlyTracks && isTPConlyTrack(track)) { - return false; - } - - if (trackcuts.requireITShit && !track.hasITS()) { - return false; - } - - if (track.x() > trackcuts.maxX) { - return false; - } - - if (!track.hasITS() && !track.hasTPC()) { - return false; - } - - if (track.hasITS() && !track.hasTPC() && (track.hasTRD() || track.hasTOF())) { // remove unrealistic track. this should not happen. - return false; - } - - if (track.hasTPC()) { - if (track.tpcNClsFound() < trackcuts.min_ncluster_tpc) { - return false; - } - if (track.tpcNClsCrossedRows() < trackcuts.mincrossedrows || track.tpcChi2NCl() > trackcuts.maxchi2tpc) { - return false; - } - if (track.tpcFractionSharedCls() > trackcuts.max_frac_shared_clusters_tpc) { - return false; - } - if (track.tpcNSigmaEl() < trackcuts.minTPCNsigmaEl || trackcuts.maxTPCNsigmaEl < track.tpcNSigmaEl()) { - return false; - } - } - - if (track.hasITS()) { - if (track.itsChi2NCl() > trackcuts.maxchi2its) { - return false; - } - - if (v0cuts.reject_v0_on_itsib) { - auto hits_ib = std::count_if(its_ib_Requirement.second.begin(), its_ib_Requirement.second.end(), [&](auto&& requiredLayer) { return track.itsClusterMap() & (1 << requiredLayer); }); - bool its_ob_only = hits_ib <= its_ib_Requirement.first; - if (!its_ob_only) { - return false; - } - } - } - - return true; - } - - float cospaXY_KF(const KFParticle& kfp, const KFParticle& PV) - { - float lx = kfp.GetX() - PV.GetX(); // flight length X - float ly = kfp.GetY() - PV.GetY(); // flight length Y - - float px = kfp.GetPx(); - float py = kfp.GetPy(); - float cospaXY = RecoDecay::dotProd(std::array{lx, ly}, std::array{px, py}) / (RecoDecay::sqrtSumOfSquares(lx, ly) * RecoDecay::sqrtSumOfSquares(px, py)); - if (cospaXY < -1.f) { - return -1.f; - } - if (cospaXY > 1.f) { - return 1.f; - } - return cospaXY; - } - - float cospaRZ_KF(const KFParticle& kfp, const KFParticle& PV) - { - float lx = kfp.GetX() - PV.GetX(); // flight length X - float ly = kfp.GetY() - PV.GetY(); // flight length Y - float lz = kfp.GetZ() - PV.GetZ(); // flight length Z - float lt = RecoDecay::sqrtSumOfSquares(lx, ly); // flight length R, i.e. transverse plane. - - float pt = RecoDecay::sqrtSumOfSquares(kfp.GetPx(), kfp.GetPy()); - float pz = kfp.GetPz(); - - float cospaRZ = RecoDecay::dotProd(std::array{lt, lz}, std::array{pt, pz}) / (RecoDecay::sqrtSumOfSquares(lt, lz) * RecoDecay::sqrtSumOfSquares(pt, pz)); - if (cospaRZ < -1.f) { - return -1.f; - } - if (cospaRZ > 1.f) { - return 1.f; - } - return cospaRZ; - } - - template - void fillTrackTable(TTrack const& track, TShiftedTrack const& shiftedtrack, const float dcaXY, const float dcaZ) - { - v0legs(track.collisionId(), track.globalIndex(), track.sign(), - shiftedtrack.GetPx(), shiftedtrack.GetPy(), shiftedtrack.GetPz(), dcaXY, dcaZ, - track.tpcNClsFindable(), track.tpcNClsFindableMinusFound(), track.tpcNClsFindableMinusCrossedRows(), track.tpcNClsShared(), - track.tpcChi2NCl(), track.tpcInnerParam(), track.tpcSignal(), - track.tpcNSigmaEl(), track.tpcNSigmaPi(), - track.itsClusterSizes(), track.itsChi2NCl(), track.detectorMap()); - - v0legsXYZ(shiftedtrack.GetX(), shiftedtrack.GetY(), shiftedtrack.GetZ()); - - if constexpr (isMC) { - v0legsDeDxMC(track.mcTunedTPCSignal()); - } - } - - template - void fillV0Table(TV0 const& v0, const bool filltable) - { - // Get tracks - const auto& pos = v0.template posTrack_as(); - const auto& ele = v0.template negTrack_as(); - const auto& collision = v0.template collision_as(); // collision where this v0 belongs to. - // LOGF(info, "v0.collisionId() = %d, pos.collisionId() = %d, ele.collisionId() = %d", v0.collisionId(), pos.collisionId(), ele.collisionId()); - - if (pos.sign() * ele.sign() > 0) { // reject same sign pair - return; - } - - if (pos.pt() < trackcuts.min_pt_trackiu || ele.pt() < trackcuts.min_pt_trackiu) { - return; - } - - if (pos.globalIndex() == ele.globalIndex()) { - return; - } - - if (isITSonlyTrack(pos) && !ele.hasITS()) { - return; - } - - if (isITSonlyTrack(ele) && !pos.hasITS()) { - return; - } - - if (!checkV0leg(pos) || !checkV0leg(ele)) { - return; - } - - // LOGF(info, "v0.collisionId() = %d , v0.posTrackId() = %d , v0.negTrackId() = %d", v0.collisionId(), v0.posTrackId(), v0.negTrackId()); - - // if(isTPConlyTrack(ele)){ - // // LOGF(info, "TPConly: ele.globalIndex() = %d, ele.x() = %f, ele.y() = %f, ele.z() = %f, ele.tgl() = %f, ele.alpha() = %f, ele.snp() = %f, ele.signed1Pt() = %f", ele.globalIndex(), ele.x(), ele.y(), ele.z(), ele.tgl(), ele.alpha(), ele.snp(), ele.signed1Pt()); - // // LOGF(info, "TPConly: ele.globalIndex() = %d, ele.cYY() = %f, ele.cZY() = %f, ele.cZZ() = %f, ele.cSnpY() = %f, ele.cSnpZ() = %f, ele.cSnpSnp() = %f, ele.cTglY() = %f, ele.cTglZ() = %f, ele.cTglSnp() = %f, ele.cTglTgl() = %f, ele.c1PtY() = %f, ele.c1PtZ() = %f, ele.c1PtSnp() = %f, ele.c1PtTgl() = %f, ele.c1Pt21Pt2() = %f", ele.globalIndex(), ele.cYY(), ele.cZY(), ele.cZZ(), ele.cSnpY(), ele.cSnpZ(), ele.cSnpSnp(), ele.cTglY(), ele.cTglZ(), ele.cTglSnp(), ele.cTglTgl(), ele.c1PtY(), ele.c1PtZ(), ele.c1PtSnp(), ele.c1PtTgl(), ele.c1Pt21Pt2()); - // } - - // Calculate DCA with respect to the collision associated to the v0, not individual tracks - std::array dcaInfo{}; - - auto pTrack = getTrackParCov(pos); - if (trackcuts.moveTPCTracks && isTPConlyTrack(pos) && !mVDriftMgr.moveTPCTrack(collision, pos, pTrack)) { - LOGP(error, "failed correction for positive tpc track"); - return; - } - auto pTrackC = pTrack; - o2::math_utils::Point3D vtxPrim{ - collision.posX(), - collision.posY(), - collision.posZ()}; - if (modeTrackPropagation != TrackPropMode::kProper) { - dcaInfo = CalculateDCAFast(pTrackC, vtxPrim, d_bz); - } - pTrackC.setPID(o2::track::PID::Electron); - - std::array dcaInfoFast = dcaInfo; - if (modeTrackPropagation != TrackPropMode::kFast) { - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, pTrackC, 2.f, matCorr, &dcaInfo); - if (filltable && modeTrackPropagation == TrackPropMode::kBoth) { - registry.fill(HIST("V0Leg/hDCAxyPropagationCompare"), dcaInfo[0], dcaInfoFast[0]); - registry.fill(HIST("V0Leg/hDCAzPropagationCompare"), dcaInfo[1], dcaInfoFast[1]); - } - } - auto posdcaXY = dcaInfo[0]; - auto posdcaZ = dcaInfo[1]; - - auto nTrack = getTrackParCov(ele); - if (trackcuts.moveTPCTracks && isTPConlyTrack(ele) && !mVDriftMgr.moveTPCTrack(collision, ele, nTrack)) { - LOGP(error, "failed correction for negative tpc track"); - return; - } - auto nTrackC = nTrack; - if (modeTrackPropagation != TrackPropMode::kProper) { - dcaInfo = CalculateDCAFast(nTrackC, vtxPrim, d_bz); - } - nTrackC.setPID(o2::track::PID::Electron); - - dcaInfoFast = dcaInfo; - if (modeTrackPropagation != TrackPropMode::kFast) { - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, nTrackC, 2.f, matCorr, &dcaInfo); - if (filltable && modeTrackPropagation == TrackPropMode::kBoth) { - registry.fill(HIST("V0Leg/hDCAxyPropagationCompare"), dcaInfo[0], dcaInfoFast[0]); - registry.fill(HIST("V0Leg/hDCAzPropagationCompare"), dcaInfo[1], dcaInfoFast[1]); - } - } - auto eledcaXY = dcaInfo[0]; - auto eledcaZ = dcaInfo[1]; - - if (std::fabs(posdcaXY) < trackcuts.dcapostopv || std::fabs(eledcaXY) < trackcuts.dcanegtopv) { - return; - } - - std::array xyz = {0.f, 0.f, 0.f}; - Vtx_recalculationParCov(o2::base::Propagator::Instance(), pTrack, nTrack, xyz, matCorr); - float rxy_tmp = RecoDecay::sqrtSumOfSquares(xyz[0], xyz[1]); - if (rxy_tmp > trackcuts.maxX + v0cuts.margin_r_tpc) { - return; - } - if (rxy_tmp < std::fabs(xyz[2]) * std::tan(2 * std::atan(std::exp(-v0cuts.max_eta_v0))) - v0cuts.margin_z) { - return; // RZ line cut - } - - float phiv = 999.f; - float psipair = 999.f; - float phivFast = 999.f; - float psipairFast = 999.f; - float baseR = std::hypot(xyz[0], xyz[1]); - // This method uses the track Helix instead of the full propagation. - // Hence, it is only an approximation but much faster - if (modeTrackPropagation != TrackPropMode::kProper) { - - o2::track::TrackAuxPar helixPosEle(nTrack, d_bz); - o2::track::TrackAuxPar helixPosPos(pTrack, d_bz); - - float diffX = helixPosEle.xC - helixPosPos.xC; - float diffY = helixPosEle.yC - helixPosPos.yC; - auto phiHelix = RecoDecay::constrainAngle(std::atan2(diffY, diffX) - o2::constants::math::PI / 2.); - - // Electron - float arcLenghtEle = helixPosEle.rC * 0.9 > propV0LegsRadius ? std::asin(propV0LegsRadius / helixPosEle.rC) * helixPosEle.rC : o2::constants::math::PI / 2.2 * helixPosEle.rC; // This assumes that the photon momentum vector is a tangent of the circle // o2-linter: disable=magic-number (geometrical assumption for propagation) - auto propTrackEle = getPropMomentumFromTrackHelix(arcLenghtEle, ele, helixPosEle, d_bz / 10., phiHelix - ele.phi()); - // Positron - float arcLenghtPos = helixPosPos.rC * 0.9 > propV0LegsRadius ? std::asin(propV0LegsRadius / helixPosPos.rC) * helixPosPos.rC : o2::constants::math::PI / 2.2 * helixPosPos.rC; // This assumes that the photon momentum vector is a tangent of the circle // o2-linter: disable=magic-number (geometrical assumption for propagation) - auto propTrackPos = getPropMomentumFromTrackHelix(arcLenghtPos, pos, helixPosPos, d_bz / 10., phiHelix - pos.phi()); - - phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(propTrackPos[0], propTrackPos[1], propTrackPos[2], propTrackEle[0], propTrackEle[1], propTrackEle[2], pos.sign(), ele.sign(), d_bz); - psipair = o2::aod::pwgem::dilepton::utils::pairutil::getPsiPair(propTrackPos[0], propTrackPos[1], propTrackPos[2], propTrackEle[0], propTrackEle[1], propTrackEle[2]); - - // Store values for later comparison - phivFast = phiv; - psipairFast = psipair; - } - // This uses the full propagation including material effects - if (modeTrackPropagation != TrackPropMode::kFast) { - std::array offsetsR = {propV0LegsRadius, 30.f, 10.f}; - bool pPropagatedSuccess = false; - bool nPropagatedSuccess = false; - auto pTrackProp = pTrack; - auto nTrackProp = nTrack; - for (const auto& offsetR : offsetsR) { - pTrackProp = pTrack; - pTrackProp.setPID(o2::track::PID::Electron); - nTrackProp = nTrack; - nTrackProp.setPID(o2::track::PID::Electron); - - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, pTrackProp, 2.f, matCorr, &dcaInfo); - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, nTrackProp, 2.f, matCorr, &dcaInfo); - - pPropagatedSuccess = o2::base::Propagator::Instance()->propagateToR(pTrackProp, baseR + offsetR); - nPropagatedSuccess = o2::base::Propagator::Instance()->propagateToR(nTrackProp, baseR + offsetR); - - if (pPropagatedSuccess && nPropagatedSuccess) { - KFPTrack kfp_track_posProp = createKFPTrackFromTrackParCov(pTrackProp, pos.sign(), pos.tpcNClsFound(), pos.tpcChi2NCl()); - KFPTrack kfp_track_eleProp = createKFPTrackFromTrackParCov(nTrackProp, ele.sign(), ele.tpcNClsFound(), ele.tpcChi2NCl()); - phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(kfp_track_posProp.GetPx(), kfp_track_posProp.GetPy(), kfp_track_posProp.GetPz(), kfp_track_eleProp.GetPx(), kfp_track_eleProp.GetPy(), kfp_track_eleProp.GetPz(), pos.sign(), ele.sign(), d_bz); - psipair = o2::aod::pwgem::dilepton::utils::pairutil::getPsiPair(kfp_track_posProp.GetPx(), kfp_track_posProp.GetPy(), kfp_track_posProp.GetPz(), kfp_track_eleProp.GetPx(), kfp_track_eleProp.GetPy(), kfp_track_eleProp.GetPz()); - break; - } - LOG(debug) << "Propagation to offset" << offsetR << " cm failed for " << (pPropagatedSuccess ? "negative" : "positive") << " track. Trying smaller offset."; - } - if (filltable && modeTrackPropagation == TrackPropMode::kBoth) { - registry.fill(HIST("V0/hPhivPropagationCompare"), phiv, phivFast); - registry.fill(HIST("V0/hPsiPairPropagationCompare"), psipair, psipairFast); - } - } - - if (phiv == 999.f || psipair == 999.f) { // o2-linter: disable=magic-number (nonsensical default value) - LOG(debug) << "Propagation failed for all radii (" << propV0LegsRadius << ", 30, 10 cm). Using default values for phiv and psipair (999.f)."; - } - - KFPTrack kfp_track_pos = createKFPTrackFromTrackParCov(pTrack, pos.sign(), pos.tpcNClsFound(), pos.tpcChi2NCl()); - KFPTrack kfp_track_ele = createKFPTrackFromTrackParCov(nTrack, ele.sign(), ele.tpcNClsFound(), ele.tpcChi2NCl()); - KFParticle kfp_pos(kfp_track_pos, kPositron); - KFParticle kfp_ele(kfp_track_ele, kElectron); - std::array GammaDaughters{&kfp_pos, &kfp_ele}; - - KFParticle gammaKF; - gammaKF.SetConstructMethod(2); - gammaKF.Construct(GammaDaughters.data(), 2); - if (v0cuts.kfMassConstrain > -0.1) { // o2-linter: disable=magic-number (nonsensical default value) - gammaKF.SetNonlinearMassConstraint(v0cuts.kfMassConstrain); - } - KFPVertex kfpVertex = createKFPVertexFromCollision(collision); - KFParticle KFPV(kfpVertex); - - // Transport the gamma to the recalculated decay vertex - KFParticle gammaKF_DecayVtx = gammaKF; // with respect to (0,0,0) - gammaKF_DecayVtx.TransportToPoint(xyz.data()); - - float cospa_kf = cpaFromKF(gammaKF_DecayVtx, KFPV); - if (!ele.hasITS() && !pos.hasITS()) { - if (cospa_kf < v0cuts.min_v0cospa_tpconly) { - return; - } - } else { - if (cospa_kf < v0cuts.min_v0cospa_its) { - return; - } - } - - float rxy = RecoDecay::sqrtSumOfSquares(gammaKF_DecayVtx.GetX(), gammaKF_DecayVtx.GetY()); - if (rxy > trackcuts.maxX + v0cuts.margin_r_tpc) { - return; - } - if (rxy < std::fabs(gammaKF_DecayVtx.GetZ()) * std::tan(2 * std::atan(std::exp(-v0cuts.max_eta_v0))) - v0cuts.margin_z) { - return; // RZ line cut - } - if (rxy < v0cuts.min_v0radius || v0cuts.max_v0radius < rxy) { - return; - } - - if (!filltable) { - if (isITSTPCTrack(pos) && isITSTPCTrack(ele)) { - registry.fill(HIST("V0/hRxy_minX_ITSTPC_ITSTPC"), std::min(pTrack.getX(), nTrack.getX()), std::min(pTrack.getX(), nTrack.getX()) - rxy); // trackiu.x() - rxy should be positive - } else if (isITSonlyTrack(pos) && isITSonlyTrack(ele)) { - registry.fill(HIST("V0/hRxy_minX_ITSonly_ITSonly"), std::min(pTrack.getX(), nTrack.getX()), std::min(pTrack.getX(), nTrack.getX()) - rxy); // trackiu.x() - rxy should be positive - } else if ((isITSTPCTrack(pos) && isITSonlyTrack(ele)) || (isITSTPCTrack(ele) && isITSonlyTrack(pos))) { - registry.fill(HIST("V0/hRxy_minX_ITSTPC_ITSonly"), std::min(pTrack.getX(), nTrack.getX()), std::min(pTrack.getX(), nTrack.getX()) - rxy); // trackiu.x() - rxy should be positive - } else if (isITSTPCTrack(pos) && !ele.hasITS()) { - registry.fill(HIST("V0/hRxy_minX_ITSTPC_TPC"), std::min(pTrack.getX(), 83.f), std::min(pTrack.getX(), 83.f) - rxy); // trackiu.x() - rxy should be positive - } else if (isITSTPCTrack(ele) && !pos.hasITS()) { - registry.fill(HIST("V0/hRxy_minX_ITSTPC_TPC"), std::min(nTrack.getX(), 83.f), std::min(nTrack.getX(), 83.f) - rxy); // trackiu.x() - rxy should be positive - } else { - registry.fill(HIST("V0/hRxy_minX_TPC_TPC"), std::min(83.f, 83.f), std::min(83.f, 83.f) - rxy); // trackiu.x() - rxy should be positive - } - } - - if (pos.hasITS() && ele.hasITS()) { // ITSonly-ITSonly, ITSTPC-ITSTPC, ITSTPC-ITSonly - if (rxy > std::min(pTrack.getX(), nTrack.getX()) + v0cuts.margin_r_its) { - return; - } - } else if (!pos.hasITS() && ele.hasITS()) { // ITSTPC-TPC - if (rxy > std::min(83.f, nTrack.getX()) + v0cuts.margin_r_itstpc_tpc) { - return; - } - } else if (pos.hasITS() && !ele.hasITS()) { // ITSTPC-TPC - if (rxy > std::min(pTrack.getX(), 83.f) + v0cuts.margin_r_itstpc_tpc) { - return; - } - } else if (!pos.hasITS() && !ele.hasITS()) { // TPC-TPC - if (rxy > std::min(83.f, 83.f) + v0cuts.margin_r_tpc) { - return; - } - } - - if ((!pos.hasITS() || !ele.hasITS()) && rxy < v0cuts.max_r_req_its) { // conversion points smaller than v0cuts.max_r_req_its have to be detected with ITS hits. - return; - } - - if ((!pos.hasITS() && !ele.hasITS()) && rxy < v0cuts.min_r_tpconly) { // TPConly tracks can detect conversion points larger than v0cuts.min_r_tpconly. - return; - } - - // Apply a topological constraint of the gamma to the PV. Parameters will be given at the primary vertex. - KFParticle gammaKF_PV = gammaKF; - gammaKF_PV.SetProductionVertex(KFPV); - float v0pt = RecoDecay::sqrtSumOfSquares(gammaKF_PV.GetPx(), gammaKF_PV.GetPy()); - float v0eta = RecoDecay::eta(std::array{gammaKF_PV.GetPx(), gammaKF_PV.GetPy(), gammaKF_PV.GetPz()}); - float v0phi = RecoDecay::constrainAngle(RecoDecay::phi(gammaKF_PV.GetPx(), gammaKF_PV.GetPy())); - - // KFParticle gammaKF_DecayVtx2 = gammaKF; - // gammaKF_DecayVtx2.SetProductionVertex(KFPV); - // gammaKF_DecayVtx2.TransportToPoint(xyz); - // LOGF(info, "gammaKF_PV.GetPx() = %f, gammaKF_DecayVtx.GetPx() = %f, gammaKF_DecayVtx2.GetPx() = %f", gammaKF_PV.GetPx(), gammaKF_DecayVtx.GetPx(), gammaKF_DecayVtx2.GetPx()); - // LOGF(info, "gammaKF_PV.GetPy() = %f, gammaKF_DecayVtx.GetPy() = %f, gammaKF_DecayVtx2.GetPy() = %f", gammaKF_PV.GetPy(), gammaKF_DecayVtx.GetPy(), gammaKF_DecayVtx2.GetPy()); - // LOGF(info, "gammaKF_PV.GetPz() = %f, gammaKF_DecayVtx.GetPz() = %f, gammaKF_DecayVtx2.GetPz() = %f", gammaKF_PV.GetPz(), gammaKF_DecayVtx.GetPz(), gammaKF_DecayVtx2.GetPz()); - - if (std::fabs(v0eta) > v0cuts.max_eta_v0 || v0pt < v0cuts.min_pt_v0) { - return; - } - - if (isITSonlyTrack(ele) && isITSonlyTrack(pos) && v0pt > v0cuts.max_pt_v0_itsonly) { - return; - } - - KFParticle kfp_pos_DecayVtx = kfp_pos; // Don't set Primary Vertex - KFParticle kfp_ele_DecayVtx = kfp_ele; // Don't set Primary Vertex - kfp_pos_DecayVtx.TransportToPoint(xyz.data()); // Don't set Primary Vertex - kfp_ele_DecayVtx.TransportToPoint(xyz.data()); // Don't set Primary Vertex - - float cospaXYKF = cospaXY_KF(gammaKF_DecayVtx, KFPV); - float cospaRZKF = cospaRZ_KF(gammaKF_DecayVtx, KFPV); - auto centType = static_cast(mlcuts.centTypePCMMl.value); - v0photoncandidate.setPhotonCandidate(gammaKF_DecayVtx, gammaKF_PV, pos, kfp_pos_DecayVtx, ele, kfp_ele_DecayVtx, collision, cospaXYKF, cospaRZKF, cospaXYKF, psipair, phiv, centType, posdcaXY, eledcaXY, posdcaZ, eledcaZ); - - if (!ele.hasITS() && !pos.hasITS()) { // V0s with TPConly-TPConly - if (v0cuts.max_r_itsmft_ss < rxy && rxy < trackcuts.maxX + v0cuts.margin_r_tpc) { - if (v0photoncandidate.getPCA() > v0cuts.max_dcav0dau_tpc_inner_fc) { - return; - } - } else { - if (v0photoncandidate.getPCA() > v0cuts.max_dcav0dau_tpconly) { - return; - } - } - } else { // V0s with ITS hits - if (rxy < v0cuts.max_r_req_its) { - if (v0photoncandidate.getPCA() > v0cuts.max_dcav0dau_itsibss) { - return; - } - } else { - if (v0photoncandidate.getPCA() > v0cuts.max_dcav0dau_its) { - return; - } - } - } - - if (isITSonlyTrack(pos) && v0photoncandidate.getPosPt() > trackcuts.maxpt_itsonly) { - return; - } - - if (isITSonlyTrack(ele) && v0photoncandidate.getElePt() > trackcuts.maxpt_itsonly) { - return; - } - - if (v0photoncandidate.getChi2NDF() > 6e+3) { // protection for uint16. // o2-linter: disable=magic-number (protection for uint16) - return; - } - - if (std::fabs(v0photoncandidate.getDcaXYToPV()) > v0cuts.max_dcatopv_xy_v0 || std::fabs(v0photoncandidate.getDcaZToPV()) > v0cuts.max_dcatopv_z_v0) { - return; - } - - if (!checkAP(v0photoncandidate.getAlpha(), v0photoncandidate.getQt(), v0cuts.max_alpha_ap, v0cuts.max_qt_ap)) { // store only photon conversions - return; - } - pca_map[std::make_tuple(v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex())] = v0photoncandidate.getPCA(); - cospa_map[std::make_tuple(v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex())] = v0photoncandidate.getCosPA(); - - ROOT::Math::PxPyPzMVector vposDedup(kfp_pos_DecayVtx.GetPx(), kfp_pos_DecayVtx.GetPy(), kfp_pos_DecayVtx.GetPz(), o2::constants::physics::MassElectron); - ROOT::Math::PxPyPzMVector veleDedup(kfp_ele_DecayVtx.GetPx(), kfp_ele_DecayVtx.GetPy(), kfp_ele_DecayVtx.GetPz(), o2::constants::physics::MassElectron); - const auto meeSV = static_cast((vposDedup + veleDedup).M()); - - const auto score = getScoreV0(v0photoncandidate.getCosPA(), v0photoncandidate.getPCA(), dedup.deduplicationScoreWeight); - V0CandidateHelper v0Helper(v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex(), - v0photoncandidate.getCosPA(), v0photoncandidate.getPCA(), score, meeSV); - v0Helper.pxAtPV = gammaKF_PV.GetPx(); - v0Helper.pyAtPV = gammaKF_PV.GetPy(); - v0Helper.pzAtPV = gammaKF_PV.GetPz(); - vecV0Dedup.emplace_back(v0Helper); - - if (mlcuts.applyPCMMl) { - bool isSelectedML = false; - std::vector mlInputFeatures = emMlResponse.getInputFeatures(v0photoncandidate, pos, ele); - if (mlcuts.use2DBinning) { - isSelectedML = emMlResponse.isSelectedMl(mlInputFeatures, v0photoncandidate.getPt(), v0photoncandidate.getCent(), outputML); - } else { - isSelectedML = emMlResponse.isSelectedMl(mlInputFeatures, v0photoncandidate.getPt(), outputML); - } - if (filltable) { - if (mlcuts.nClassesPCMMl == o2::analysis::NClassesML::kTwo) { - registry.fill(HIST("V0/hBDTBackgroundScoreBeforeCutVsPt"), v0photoncandidate.getPt(), outputML[0]); - registry.fill(HIST("V0/hBDTSignalScoreBeforeCutVsPt"), v0photoncandidate.getPt(), outputML[1]); - } else if (mlcuts.nClassesPCMMl == o2::analysis::NClassesML::kThree) { - registry.fill(HIST("V0/hBDTPrimaryPhotonScoreBeforeCutVsPt"), v0photoncandidate.getPt(), outputML[0]); - registry.fill(HIST("V0/hBDTSecondaryPhotonScoreBeforeCutVsPt"), v0photoncandidate.getPt(), outputML[1]); - registry.fill(HIST("V0/hBDTBackgroundScoreBeforeCutVsPt"), v0photoncandidate.getPt(), outputML[2]); - } else { - registry.fill(HIST("V0/hBDTScoreBeforeCutVsPt"), v0photoncandidate.getPt(), outputML[0]); - } - } - if (!isSelectedML) { - return; - } - if (filltable) { - if (mlcuts.nClassesPCMMl == o2::analysis::NClassesML::kTwo) { - registry.fill(HIST("V0/hBDTBackgroundScoreAfterCutVsPt"), v0photoncandidate.getPt(), outputML[0]); - registry.fill(HIST("V0/hBDTSignalScoreAfterCutVsPt"), v0photoncandidate.getPt(), outputML[1]); - } else if (mlcuts.nClassesPCMMl == o2::analysis::NClassesML::kThree) { - registry.fill(HIST("V0/hBDTPrimaryPhotonScoreAfterCutVsPt"), v0photoncandidate.getPt(), outputML[0]); - registry.fill(HIST("V0/hBDTSecondaryPhotonScoreAfterCutVsPt"), v0photoncandidate.getPt(), outputML[1]); - registry.fill(HIST("V0/hBDTBackgroundScoreAfterCutVsPt"), v0photoncandidate.getPt(), outputML[2]); - } else { - registry.fill(HIST("V0/hBDTScoreAfterCutVsPt"), v0photoncandidate.getPt(), outputML[0]); - } - } - } - - if (filltable) { - registry.fill(HIST("V0/hAP"), v0photoncandidate.getAlpha(), v0photoncandidate.getQt()); - registry.fill(HIST("V0/hConversionPointXY"), gammaKF_DecayVtx.GetX(), gammaKF_DecayVtx.GetY()); - registry.fill(HIST("V0/hConversionPointRZ"), gammaKF_DecayVtx.GetZ(), rxy); - registry.fill(HIST("V0/hPt"), v0photoncandidate.getPt()); - registry.fill(HIST("V0/hEtaPhi"), v0phi, v0eta); - registry.fill(HIST("V0/hCosPA"), v0photoncandidate.getCosPA()); - registry.fill(HIST("V0/hCosPA_Rxy"), rxy, v0photoncandidate.getCosPA()); - registry.fill(HIST("V0/hPCA"), v0photoncandidate.getPCA()); - registry.fill(HIST("V0/hPCA_CosPA"), v0photoncandidate.getCosPA(), v0photoncandidate.getPCA()); - registry.fill(HIST("V0/hPCA_Rxy"), rxy, v0photoncandidate.getPCA()); - registry.fill(HIST("V0/hDCAxyz"), v0photoncandidate.getDcaXYToPV(), v0photoncandidate.getDcaZToPV()); - registry.fill(HIST("V0/hPCA_diffX"), v0photoncandidate.getPCA(), std::min(pTrack.getX(), nTrack.getX()) - rxy); // trackiu.x() - rxy should be positive - registry.fill(HIST("V0/hPhiVPsiPair"), v0photoncandidate.getPsiPair(), v0photoncandidate.getPhiV()); - - // LOGF(info, "cospa_kf = %f, cospaXY_kf = %f, cospaRZ_kf = %f", cospa_kf, cospaXY_kf, cospaRZ_kf); - registry.fill(HIST("V0/hCosPAXY_Rxy"), rxy, cospaXYKF); - registry.fill(HIST("V0/hCosPARZ_Rxy"), rxy, cospaRZKF); - - for (const auto& leg : {kfp_pos_DecayVtx, kfp_ele_DecayVtx}) { - float legpt = RecoDecay::sqrtSumOfSquares(leg.GetPx(), leg.GetPy()); - float legeta = RecoDecay::eta(std::array{leg.GetPx(), leg.GetPy(), leg.GetPz()}); - float legphi = RecoDecay::constrainAngle(RecoDecay::phi(leg.GetPx(), leg.GetPy())); - registry.fill(HIST("V0Leg/hPt"), legpt); - registry.fill(HIST("V0Leg/hEtaPhi"), legphi, legeta); - } // end of leg loop - for (const auto& leg : {pos, ele}) { - registry.fill(HIST("V0Leg/hdEdx_Pin"), leg.tpcInnerParam(), leg.tpcSignal()); - registry.fill(HIST("V0Leg/hTPCNsigmaEl"), leg.tpcInnerParam(), leg.tpcNSigmaEl()); - } // end of leg loop - for (const auto& leg : {pTrack, nTrack}) { - registry.fill(HIST("V0Leg/hXZ"), leg.getZ(), leg.getX()); - registry.fill(HIST("V0Leg/hRelDeltaPt"), leg.getPt(), leg.getPt() * std::sqrt(leg.getSigma1Pt2())); - } // end of leg loop - registry.fill(HIST("V0Leg/hDCAxyz"), posdcaXY, posdcaZ); - registry.fill(HIST("V0Leg/hDCAxyz"), eledcaXY, eledcaZ); - - ROOT::Math::PxPyPzMVector vpos_sv(kfp_pos_DecayVtx.GetPx(), kfp_pos_DecayVtx.GetPy(), kfp_pos_DecayVtx.GetPz(), o2::constants::physics::MassElectron); - ROOT::Math::PxPyPzMVector vele_sv(kfp_ele_DecayVtx.GetPx(), kfp_ele_DecayVtx.GetPy(), kfp_ele_DecayVtx.GetPz(), o2::constants::physics::MassElectron); - ROOT::Math::PxPyPzMVector v0_sv = vpos_sv + vele_sv; - registry.fill(HIST("V0/hMeeSV_Rxy"), rxy, v0_sv.M()); - - v0photonskf(collision.globalIndex(), v0.globalIndex(), v0legs.lastIndex() + 1, v0legs.lastIndex() + 2, - gammaKF_DecayVtx.GetX(), gammaKF_DecayVtx.GetY(), gammaKF_DecayVtx.GetZ(), - gammaKF_PV.GetPx(), gammaKF_PV.GetPy(), gammaKF_PV.GetPz(), - v0_sv.M(), v0photoncandidate.getDcaXYToPV(), v0photoncandidate.getDcaZToPV(), - cospa_kf, cospaXYKF, cospaRZKF, - v0photoncandidate.getPCA(), v0photoncandidate.getAlpha(), v0photoncandidate.getQt(), v0photoncandidate.getChi2NDF()); - v0photonsphivpsi(v0photoncandidate.getPhiV(), v0photoncandidate.getPsiPair()); - - // v0photonskfcov(gammaKF_PV.GetCovariance(9), gammaKF_PV.GetCovariance(14), gammaKF_PV.GetCovariance(20), gammaKF_PV.GetCovariance(13), gammaKF_PV.GetCovariance(19), gammaKF_PV.GetCovariance(18)); - - fillTrackTable(pos, kfp_pos_DecayVtx, posdcaXY, posdcaZ); // positive leg first - fillTrackTable(ele, kfp_ele_DecayVtx, eledcaXY, eledcaZ); // negative leg second - } // end of fill table - } - - Preslice perCollision = o2::aod::v0::collisionId; - std::map, float> pca_map; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) -> pca - std::map, float> cospa_map; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) -> cospa - std::vector vecV0Dedup; // vector with all V0Candidates that is used to sort them by score (see struct V0CandidateHelper for more details of content) - std::vector> stored_v0Ids; // (pos.globalIndex(), ele.globalIndex()) - std::vector> stored_fullv0Ids; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) - std::unordered_map nv0_map; // map collisionId -> nv0 - - template - void build(TCollisions const& collisions, TV0s const& v0s, TTracks const& tracks, TBCs const&, DedupDiag* diag = nullptr) - { - for (const auto& collision : collisions) { - if constexpr (isMC) { - if (!collision.has_mcCollision()) { - continue; - } - } - - if (!collision.isSelected()) { - continue; - } - - // if constexpr (isTriggerAnalysis) { - // if (collision.triggerMask_raw() == 0) { - // continue; - // } - // } - - nv0_map[collision.globalIndex()] = 0; - - const auto& bc = collision.template foundBC_as(); - initCCDB(bc); - registry.fill(HIST("hCollisionCounter"), 1); - - updateCCDB(bc); // delay update until is needed - - vecV0Dedup.reserve(30000); // rough estimate for number of V0s per DF - const auto& v0s_per_coll = v0s.sliceBy(perCollision, collision.globalIndex()); - // LOGF(info, "n v0 = %d", v0s_per_coll.size()); - for (const auto& v0 : v0s_per_coll) { - // LOGF(info, "collision.globalIndex() = %d, v0.globalIndex() = %d, v0.posTrackId() = %d, v0.negTrackId() = %d", collision.globalIndex(), v0.globalIndex(), v0.posTrackId() , v0.negTrackId()); - fillV0Table(v0, false); - } // end of v0 loop - } // end of collision loop - - stored_v0Ids.reserve(pca_map.size()); // number of photon candidates per DF - stored_fullv0Ids.reserve(pca_map.size()); // number of photon candidates per DF - - // find minimal pca - if (dedup.deduplicationMode == V0DeduplicationMode::Pairwise) { - for (const auto& [key, value] : pca_map) { - auto v0Id = std::get<0>(key); - auto collisionId = std::get<1>(key); - auto posId = std::get<2>(key); - auto eleId = std::get<3>(key); - float v0pca = value; - float cospa = cospa_map[key]; - bool is_closest_v0 = true; - bool is_most_aligned_v0 = true; - - for (const auto& [key_tmp, value_tmp] : pca_map) { - auto v0Id_tmp = std::get<0>(key_tmp); - auto collisionId_tmp = std::get<1>(key_tmp); - auto posId_tmp = std::get<2>(key_tmp); - auto eleId_tmp = std::get<3>(key_tmp); - float v0pca_tmp = value_tmp; - float cospa_tmp = cospa_map[key_tmp]; - - if (v0Id == v0Id_tmp) { // skip exactly the same v0 - continue; - } - - if (collisionId != collisionId_tmp && eleId == eleId_tmp && posId == posId_tmp && cospa < cospa_tmp) { - if (diag != nullptr) { - diag->blockersByKey[key].push_back(key_tmp); - } - is_most_aligned_v0 = false; - break; - } - if ((eleId == eleId_tmp || posId == posId_tmp) && v0pca > v0pca_tmp) { - if (diag != nullptr) { - diag->blockersByKey[key].push_back(key_tmp); - } - is_closest_v0 = false; - break; - } - } // end of pca_map tmp loop - - bool is_stored = std::find(stored_v0Ids.begin(), stored_v0Ids.end(), std::make_pair(posId, eleId)) != stored_v0Ids.end(); - if (is_closest_v0 && is_most_aligned_v0 && !is_stored) { - // auto v0 = v0s.rawIteratorAt(v0Id); - // auto collision = collisions.rawIteratorAt(collisionId); - // auto pos = tracks.rawIteratorAt(posId); - // auto ele = tracks.rawIteratorAt(eleId); - // LOGF(info, "!accept! | collision id = %d | v0id1 = %d , posid1 = %d , eleid1 = %d , pca1 = %f , cospa = %f", collisionId, v0Id, posId, eleId, v0pca, cospa); - - // fillV0Table(v0, true); - stored_v0Ids.emplace_back(std::make_pair(posId, eleId)); - stored_fullv0Ids.emplace_back(std::make_tuple(v0Id, collisionId, posId, eleId)); - nv0_map[collisionId]++; - } - } // end of pca_map loop - // LOGF(info, "pca_map.size() = %d", pca_map.size()); - - } else if (dedup.deduplicationMode == V0DeduplicationMode::KeepAll) { - // Keep-all: Every candidate that survived the quality cuts is stored, including all duplicates - stored_v0Ids.clear(); - stored_fullv0Ids.clear(); - nv0_map.clear(); - for (const auto& v0Cand : vecV0Dedup) { - stored_v0Ids.emplace_back(v0Cand.posID, v0Cand.eleID); - stored_fullv0Ids.emplace_back(v0Cand.v0ID, v0Cand.colID, v0Cand.posID, v0Cand.eleID); - nv0_map[v0Cand.colID]++; - } - std::sort(stored_fullv0Ids.begin(), stored_fullv0Ids.end(), - [](const auto& a, const auto& b) { - return std::get<1>(a) < std::get<1>(b); - }); - } else if (dedup.deduplicationMode == V0DeduplicationMode::GroupMatching) { - const int maxEnum = std::min(dedup.dedupMaxGroupSize.value, 16); // o2-linter: disable=magic-number (subset check of photons) - const int nCand = static_cast(vecV0Dedup.size()); - stored_v0Ids.clear(); - stored_fullv0Ids.clear(); - nv0_map.clear(); - int nGreedyFallback = 0; - - // ---- conflict groups: candidates connected through a shared leg ----- - std::vector parent(nCand); - for (int i = 0; i < nCand; ++i) { - parent[i] = i; - } - auto findRoot = [&parent](int x) { - while (parent[x] != x) { - parent[x] = parent[parent[x]]; // path halving - x = parent[x]; - } - return x; - }; - std::unordered_map firstCandOfTrack; - for (int i = 0; i < nCand; ++i) { - for (const int& trackId : {vecV0Dedup[i].posID, vecV0Dedup[i].eleID}) { - auto [it, inserted] = firstCandOfTrack.try_emplace(trackId, i); - if (!inserted) { - parent[findRoot(i)] = findRoot(it->second); - } - } - } - std::map> groups; - for (int i = 0; i < nCand; ++i) { - groups[findRoot(i)].push_back(i); - } - - // ---- decide each group on its own ---------------------------------- - for (auto& [root, members] : groups) { // o2-linter: disable=const-ref-in-for-loop (members is sorted in place below) - const int nMembers = static_cast(members.size()); - std::vector selected(nMembers, 0); - - // Fix the order inside the group: best score first - std::sort(members.begin(), members.end(), [this](int a, int b) { - if (vecV0Dedup[a].score != vecV0Dedup[b].score) { - return vecV0Dedup[a].score < vecV0Dedup[b].score; - } - return vecV0Dedup[a].v0ID < vecV0Dedup[b].v0ID; - }); - - if (nMembers == 1) { - selected[0] = 1; - } else if (nMembers <= maxEnum) { - - std::unordered_map legBit; - std::vector legMask(nMembers, 0); - for (int k = 0; k < nMembers; ++k) { - for (const int& trackId : {vecV0Dedup[members[k]].posID, vecV0Dedup[members[k]].eleID}) { - const int bit = legBit.try_emplace(trackId, static_cast(legBit.size())).first->second; - legMask[k] |= (1ull << bit); // at most 2 * 16 = 32 legs, fits into 64 bit - } - } - uint32_t bestMask = 0; - int bestCount = 0; - float bestScore = 0.f; - - std::vector bestWithCount(nMembers, -1); - std::vector bestWithScore(nMembers, 0.f); - for (uint32_t mask = 1; mask < (1u << nMembers); ++mask) { - uint64_t usedLegsLocal = 0; - float scoreSum = 0.f; - int count = 0; - bool disjoint = true; - for (int k = 0; k < nMembers; ++k) { - if ((mask & (1u << k)) == 0) { - continue; - } - if ((usedLegsLocal & legMask[k]) != 0) { - disjoint = false; - break; - } - usedLegsLocal |= legMask[k]; - scoreSum += vecV0Dedup[members[k]].score; - ++count; - } - if (!disjoint) { - continue; - } - - if (count > bestCount || (count == bestCount && scoreSum < bestScore)) { - bestCount = count; - bestScore = scoreSum; - bestMask = mask; - } - if (diag != nullptr) { - for (int k = 0; k < nMembers; ++k) { - if ((mask & (1u << k)) == 0) { - continue; - } - if (count > bestWithCount[k] || (count == bestWithCount[k] && scoreSum < bestWithScore[k])) { - bestWithCount[k] = count; - bestWithScore[k] = scoreSum; - } - } - } - } - for (int k = 0; k < nMembers; ++k) { - selected[k] = ((bestMask & (1u << k)) != 0) ? 1 : 0; - if (diag != nullptr && selected[k] == 0 && bestWithCount[k] >= 0) { - const auto& cand = vecV0Dedup[members[k]]; - diag->lossMargin[std::make_tuple(static_cast(cand.v0ID), static_cast(cand.colID), - static_cast(cand.posID), static_cast(cand.eleID))] = - std::make_pair(bestCount - bestWithCount[k], bestWithScore[k] - bestScore); - } - } - } else { - // Too large to enumerate: greedy by score (members is already sorted that way) - ++nGreedyFallback; - std::vector usedTracks; - usedTracks.reserve(2 * nMembers); - for (int k = 0; k < nMembers; ++k) { - const auto& cand = vecV0Dedup[members[k]]; - if (std::find(usedTracks.begin(), usedTracks.end(), cand.posID) != usedTracks.end() || - std::find(usedTracks.begin(), usedTracks.end(), cand.eleID) != usedTracks.end()) { - continue; - } - usedTracks.push_back(cand.posID); - usedTracks.push_back(cand.eleID); - selected[k] = 1; - } - } - - // ---- store the winners, book the blockers of the losers ---------- - for (int k = 0; k < nMembers; ++k) { - const auto& cand = vecV0Dedup[members[k]]; - if (selected[k] != 0) { - stored_v0Ids.emplace_back(cand.posID, cand.eleID); - stored_fullv0Ids.emplace_back(cand.v0ID, cand.colID, cand.posID, cand.eleID); - nv0_map[cand.colID]++; - continue; - } - if (diag == nullptr) { - continue; - } - // all winners that took one leg - const CandKey victimKey = std::make_tuple(static_cast(cand.v0ID), static_cast(cand.colID), - static_cast(cand.posID), static_cast(cand.eleID)); - for (int j = 0; j < nMembers; ++j) { - if (selected[j] == 0) { - continue; - } - const auto& winner = vecV0Dedup[members[j]]; - if (winner.posID == cand.posID || winner.eleID == cand.eleID) { - diag->blockersByKey[victimKey].emplace_back(static_cast(winner.v0ID), static_cast(winner.colID), - static_cast(winner.posID), static_cast(winner.eleID)); - } - } - } - } - - if (nGreedyFallback > 0) { - LOGF(warning, "group matching: %d conflict group(s) larger than dedupMaxGroupSize = %d were solved greedily", nGreedyFallback, maxEnum); - } - - // the table expects candidates ordered by collision - std::sort(stored_fullv0Ids.begin(), stored_fullv0Ids.end(), - [](const auto& a, const auto& b) { - return std::get<1>(a) < std::get<1>(b); - }); - - } else { - // Sort best candidates first, depending on score - std::sort(vecV0Dedup.begin(), vecV0Dedup.end()); - // container to keep track of which electron and positron tracks have already been used - std::vector usedLegs(tracks.size(), false); - std::unordered_map ownerOfLeg; // diagnostics only - stored_v0Ids.clear(); - stored_fullv0Ids.clear(); - nv0_map.clear(); - for (const auto& v0Cand : vecV0Dedup) { - const CandKey candKey = std::make_tuple(static_cast(v0Cand.v0ID), static_cast(v0Cand.colID), - static_cast(v0Cand.posID), static_cast(v0Cand.eleID)); - if (usedLegs[v0Cand.posID] || usedLegs[v0Cand.eleID]) { - if (diag != nullptr) { - // both legs, not just the first one found: a cross-leg fake is usually blocked by - // two different winners, and that is exactly the case being measured here - auto& blockers = diag->blockersByKey[candKey]; - for (const int& legId : {v0Cand.posID, v0Cand.eleID}) { - const auto itOwner = ownerOfLeg.find(legId); - if (itOwner == ownerOfLeg.end()) { - continue; - } - if (std::find(blockers.begin(), blockers.end(), itOwner->second) == blockers.end()) { - blockers.push_back(itOwner->second); // the same winner may hold both legs - } - } - } - continue; - } - usedLegs[v0Cand.posID] = true; - usedLegs[v0Cand.eleID] = true; - if (diag != nullptr) { - ownerOfLeg[v0Cand.posID] = candKey; - ownerOfLeg[v0Cand.eleID] = candKey; - } - stored_v0Ids.emplace_back(v0Cand.posID, v0Cand.eleID); - stored_fullv0Ids.emplace_back(v0Cand.v0ID, v0Cand.colID, v0Cand.posID, v0Cand.eleID); - nv0_map[v0Cand.colID]++; - } - - // sort accepted candidates by collision ID to be compatible with table - std::sort(stored_fullv0Ids.begin(), stored_fullv0Ids.end(), - [](const auto& a, const auto& b) { - return std::get<1>(a) < std::get<1>(b); - }); - - // End of deduplication - } - - if (diag != nullptr) { - diag->snapshot = vecV0Dedup; - diag->stored = stored_fullv0Ids; - } - - std::set skimRejected; - if (skim.minNv0PerCollision > 0 || skim.maxMinvPairPerCollision > 0.f) { - const std::set storedSetSkim(stored_fullv0Ids.begin(), stored_fullv0Ids.end()); - std::unordered_map> candsPerColl; - for (const auto& c : vecV0Dedup) { - if (storedSetSkim.contains(std::make_tuple(static_cast(c.v0ID), static_cast(c.colID), - static_cast(c.posID), static_cast(c.eleID)))) { - candsPerColl[c.colID].push_back(&c); - } - } - for (const auto& [colId, cands] : candsPerColl) { - if (skim.minNv0PerCollision > 0 && static_cast(cands.size()) < skim.minNv0PerCollision) { - skimRejected.insert(colId); - continue; - } - if (skim.maxMinvPairPerCollision > 0.f) { - bool hasLowMPair = false; - for (size_t i = 0; i < cands.size() && !hasLowMPair; ++i) { - for (size_t j = i + 1; j < cands.size(); ++j) { - ROOT::Math::PxPyPzMVector v1(cands[i]->pxAtPV, cands[i]->pyAtPV, cands[i]->pzAtPV, 0.f); - ROOT::Math::PxPyPzMVector v2(cands[j]->pxAtPV, cands[j]->pyAtPV, cands[j]->pzAtPV, 0.f); - if ((v1 + v2).M() < skim.maxMinvPairPerCollision) { - hasLowMPair = true; - break; - } - } - } - if (!hasLowMPair) { - skimRejected.insert(colId); - } - } - } - } - - for (const auto& fullv0Id : stored_fullv0Ids) { - auto v0Id = std::get<0>(fullv0Id); - const auto collisionId = std::get<1>(fullv0Id); - if (skimRejected.contains(collisionId)) { - continue; // collision failed the skim - do not store its photons/legs - } - // auto posId = std::get<2>(fullv0Id); - // auto eleId = std::get<3>(fullv0Id); - // LOGF(info, "!accept! | collision id = %d | v0id = %d , posid = %d , eleid = %d", collisionId, v0Id, posId, eleId); - - auto v0 = v0s.rawIteratorAt(v0Id); - if constexpr (enableFilter) { - auto collision_tmp = v0.template collision_as(); // collision where this v0 belongs. - if (!(collision_tmp.neeuls() >= 1 || collision_tmp.neeuls() + nv0_map[collision_tmp.globalIndex()] >= 2)) { // o2-linter: disable=magic-number (nonsensical default value) - continue; - } - // LOGF(info, "collision_tmp.globalIndex() = %d, collision_tmp.neeuls() = %d, nv0_map = %d", collision_tmp.globalIndex(), collision_tmp.neeuls(), nv0_map[collision_tmp.globalIndex()]); - } - - fillV0Table(v0, true); - } // end of fullv0Id loop - - // for (const auto& collision : collisions) { - // if constexpr (isMC) { - // if (!collision.has_mcCollision()) { - // continue; - // } - // } - // // events_ngpcm(nv0_map[collision.globalIndex()]); - // } // end of collision loop - - pca_map.clear(); - cospa_map.clear(); - nv0_map.clear(); - stored_v0Ids.clear(); - stored_v0Ids.shrink_to_fit(); - stored_fullv0Ids.clear(); - stored_fullv0Ids.shrink_to_fit(); - vecV0Dedup.clear(); - } // end of build - - Filter v0Filter = o2::aod::v0::v0Type > (uint8_t)0; - using FilteredV0s = soa::Filtered; - - enum PairFate { - kBefore = 0, - kBothSurvive, - kOneLost, - kBothLost, - kLostByCrossFake, - kLostByPartnerFake, - kLostByOtherFake, - kLostByTrue - }; - - void fillTruthPair(int fate, float q, float dEta) - { - switch (fate) { - case kBefore: - registry.fill(HIST("MCDedup/Pairs/before/hQ"), q); - registry.fill(HIST("MCDedup/Pairs/before/hDEta"), dEta); - break; - case kBothSurvive: - registry.fill(HIST("MCDedup/Pairs/bothSurvive/hQ"), q); - registry.fill(HIST("MCDedup/Pairs/bothSurvive/hDEta"), dEta); - break; - case kOneLost: - registry.fill(HIST("MCDedup/Pairs/oneLost/hQ"), q); - registry.fill(HIST("MCDedup/Pairs/oneLost/hDEta"), dEta); - break; - case kBothLost: - registry.fill(HIST("MCDedup/Pairs/bothLost/hQ"), q); - registry.fill(HIST("MCDedup/Pairs/bothLost/hDEta"), dEta); - break; - case kLostByCrossFake: - registry.fill(HIST("MCDedup/Pairs/lostByCrossFake/hQ"), q); - registry.fill(HIST("MCDedup/Pairs/lostByCrossFake/hDEta"), dEta); - break; - case kLostByPartnerFake: - registry.fill(HIST("MCDedup/Pairs/lostByPartnerFake/hQ"), q); - registry.fill(HIST("MCDedup/Pairs/lostByPartnerFake/hDEta"), dEta); - break; - case kLostByOtherFake: - registry.fill(HIST("MCDedup/Pairs/lostByOtherFake/hQ"), q); - registry.fill(HIST("MCDedup/Pairs/lostByOtherFake/hDEta"), dEta); - break; - case kLostByTrue: - registry.fill(HIST("MCDedup/Pairs/lostByTrue/hQ"), q); - registry.fill(HIST("MCDedup/Pairs/lostByTrue/hDEta"), dEta); - break; - default: - break; - } - } - - void fillTruthPairMap(int fate, float dEta, float dPhi, float q) - { - if (!dedup.cfgDedupTruthMaps) { - return; - } - switch (fate) { - case kBefore: - registry.fill(HIST("MCDedup/Pairs/before/hMap"), dEta, dPhi, q); - break; - case kBothSurvive: - registry.fill(HIST("MCDedup/Pairs/bothSurvive/hMap"), dEta, dPhi, q); - break; - case kBothLost: - registry.fill(HIST("MCDedup/Pairs/bothLost/hMap"), dEta, dPhi, q); - break; - case kLostByPartnerFake: - registry.fill(HIST("MCDedup/Pairs/lostByPartnerFake/hMap"), dEta, dPhi, q); - break; - default: - break; - } - } - - template - void fillDedupTruthDiagnostics(TTracks const& tracks, TMCParticles const& mcparticles, DedupDiag const& diag) - { - const int nCand = static_cast(diag.snapshot.size()); - if (nCand == 0) { - return; - } - const std::set storedSet(diag.stored.begin(), diag.stored.end()); - - std::vector key(nCand); - std::vector cls(nCand, static_cast(kV0OtherFake)); - std::vector motherPos(nCand, -1), motherEle(nCand, -1); - std::map indexOfKey; - for (int i = 0; i < nCand; ++i) { - const auto& c = diag.snapshot[i]; - key[i] = std::make_tuple(static_cast(c.v0ID), static_cast(c.colID), - static_cast(c.posID), static_cast(c.eleID)); - indexOfKey[key[i]] = i; - cls[i] = static_cast(classifyV0Truth(tracks.rawIteratorAt(c.posID), tracks.rawIteratorAt(c.eleID), - mcparticles, motherPos[i], motherEle[i])); - registry.fill(HIST("MCDedup/Candidates/hClassStage"), static_cast(cls[i]), 0.f); - if (storedSet.contains(key[i]) > 0) { - registry.fill(HIST("MCDedup/Candidates/hClassStage"), static_cast(cls[i]), 1.f); - } - } - - struct PhotonFate { - bool alive = false; - int best = -1; // best-score candidate, only used for the margin plots - std::vector cands; // all true candidates of this photon - std::set aliveColls; // collisions with a surviving candidate - }; - std::map fate; - for (int i = 0; i < nCand; ++i) { - if (cls[i] != kV0True) { - continue; - } - auto& f = fate[motherPos[i]]; - f.cands.push_back(i); - if (storedSet.contains(key[i]) > 0) { - f.alive = true; - f.aliveColls.insert(std::get<1>(key[i])); - } - if (f.best < 0 || diag.snapshot[i].score < diag.snapshot[f.best].score) { - f.best = i; - } - } - - std::map> blockersOfPhoton; - for (const auto& [mid, f] : fate) { - if (f.alive) { - registry.fill(HIST("MCDedup/Photons/hFate"), 0.f); - continue; - } - auto& blockers = blockersOfPhoton[mid]; - for (const int& ci : f.cands) { - const auto itBlocker = diag.blockersByKey.find(key[ci]); - if (itBlocker == diag.blockersByKey.end()) { - continue; - } - for (const auto& bKey : itBlocker->second) { - const auto itIndex = indexOfKey.find(bKey); - if (itIndex == indexOfKey.end()) { - continue; - } - if (std::find(blockers.begin(), blockers.end(), itIndex->second) == blockers.end()) { - blockers.push_back(itIndex->second); - } - } - } - // Why was it not in the winning set? Take the most favourable of its candidates: the one - // that could have been kept at the smallest cost in cardinality. - int bestDeficit = -1; - float bestExcess = 0.f; - for (const int& ci : f.cands) { - const auto itMargin = diag.lossMargin.find(key[ci]); - if (itMargin == diag.lossMargin.end()) { - continue; - } - if (bestDeficit < 0 || itMargin->second.first < bestDeficit || - (itMargin->second.first == bestDeficit && itMargin->second.second < bestExcess)) { - bestDeficit = itMargin->second.first; - bestExcess = itMargin->second.second; - } - } - if (bestDeficit >= 0) { - registry.fill(HIST("MCDedup/Photons/hLostMargin"), static_cast(bestDeficit), bestExcess); - } - // hVictimBlockerClass below counts one entry per blocker and a victim can have two. - registry.fill(HIST("MCDedup/Photons/hFate"), blockers.empty() ? 2.f : 1.f); - - if (blockers.empty()) { - registry.fill(HIST("MCDedup/Photons/hBlockerClass"), 3.f); // o2-linter: disable=magic-number (bin 3 = no blocker) - continue; - } - for (const int& w : blockers) { - registry.fill(HIST("MCDedup/Photons/hBlockerClass"), static_cast(cls[w])); - } - // margin and PCA comparison relation the best candidate to its blocker - - if (f.best >= 0 && dedup.deduplicationMode.value < static_cast(V0DeduplicationMode::GroupMatching)) { - const int w = blockers.front(); - registry.fill(HIST("MCDedup/Photons/hKillMargin"), diag.snapshot[f.best].score - diag.snapshot[w].score); - registry.fill(HIST("MCDedup/Photons/hVictimVsWinnerPCA"), diag.snapshot[f.best].pca, diag.snapshot[w].pca); - } - } - - // ---- truth pairs, grouped by MC collision ---------------------------- - std::map> mothersByMcColl; - for (const auto& [mid, f] : fate) { - mothersByMcColl[mcparticles.iteratorAt(mid).mcCollisionId()].push_back(mid); - } - constexpr float MaxQTruth = 0.3f; - for (const auto& [mcCol, mids] : mothersByMcColl) { - for (size_t a = 0; a < mids.size(); ++a) { - for (size_t b = a + 1; b < mids.size(); ++b) { - const auto g1 = mcparticles.iteratorAt(mids[a]); - const auto g2 = mcparticles.iteratorAt(mids[b]); - const float e1 = std::hypot(g1.px(), g1.py(), g1.pz()); - const float e2 = std::hypot(g2.px(), g2.py(), g2.pz()); - const float qTrue = std::sqrt(std::max(0.f, 2.f * (e1 * e2 - g1.px() * g2.px() - g1.py() * g2.py() - g1.pz() * g2.pz()))); - if (qTrue > MaxQTruth) { - continue; - } - const float dEta = g1.eta() - g2.eta(); - const float dPhi = RecoDecay::constrainAngle(static_cast(g1.phi() - g2.phi()), -o2::constants::math::PI); - fillTruthPair(kBefore, qTrue, dEta); - fillTruthPairMap(kBefore, dEta, dPhi, qTrue); - - const auto& f1 = fate.at(mids[a]); - const auto& f2 = fate.at(mids[b]); - const int nLost = (f1.alive ? 0 : 1) + (f2.alive ? 0 : 1); - if (nLost == 0) { - fillTruthPair(kBothSurvive, qTrue, dEta); - fillTruthPairMap(kBothSurvive, dEta, dPhi, qTrue); - bool sameColl = false; - for (const auto& c1 : f1.aliveColls) { - sameColl = sameColl || (f2.aliveColls.count(c1) > 0); - } - if (sameColl) { - registry.fill(HIST("MCDedup/Pairs/bothSurviveSameColl/hQ"), qTrue); - } - continue; - } - if (nLost == 1) { - fillTruthPair(kOneLost, qTrue, dEta); - } else { - fillTruthPair(kBothLost, qTrue, dEta); - fillTruthPairMap(kBothLost, dEta, dPhi, qTrue); - } - - // Reason for photon building failure - bool byCross = false, byPartner = false, byOther = false, byTrue = false; - auto attribute = [&](int64_t victim, int64_t partner) { - if (fate.at(victim).alive) { - return; - } - const auto itB = blockersOfPhoton.find(victim); - if (itB == blockersOfPhoton.end()) { - return; - } - for (const int& w : itB->second) { - if (cls[w] == kV0CrossLegFake) { - byCross = true; - // partner fake = a cross-leg fake using a leg of the other photon - if (motherPos[w] == partner || motherEle[w] == partner) { - byPartner = true; - } - } else if (cls[w] == kV0OtherFake) { - byOther = true; - } else if (cls[w] == kV0True) { - byTrue = true; // real photon against real photon - } - } - }; - attribute(mids[a], mids[b]); - attribute(mids[b], mids[a]); - if (byCross) { - fillTruthPair(kLostByCrossFake, qTrue, dEta); - } - if (byPartner) { - fillTruthPair(kLostByPartnerFake, qTrue, dEta); - fillTruthPairMap(kLostByPartnerFake, dEta, dPhi, qTrue); - } - if (byOther) { - fillTruthPair(kLostByOtherFake, qTrue, dEta); - } - if (byTrue) { - fillTruthPair(kLostByTrue, qTrue, dEta); - } - } - } - } - } - - void processRec(MyCollisions const& collisions, FilteredV0s const& v0s, MyTracksIU const& tracks, MyBCs const& bcs) - { - build(collisions, v0s, tracks, bcs); - } - PROCESS_SWITCH(PhotonConversionBuilder, processRec, "process reconstructed info for data", true); - - // void processRec_SWT(MyCollisionsWithSWT const& collisions, FilteredV0s const& v0s, MyTracksIU const& tracks, MyBCs const& bcs) - // { - // build(collisions, v0s, tracks, bcs); - // } - // PROCESS_SWITCH(PhotonConversionBuilder, processRec_SWT, "process reconstructed info for data", false); - - void processMC(MyCollisionsMC const& collisions, FilteredV0s const& v0s, MyTracksIUMC const& tracks, - MyBCs const& bcs, aod::McParticles const& mcparticles) - { - DedupDiag diag; - build(collisions, v0s, tracks, bcs, &diag); - fillDedupTruthDiagnostics(tracks, mcparticles, diag); - } - PROCESS_SWITCH(PhotonConversionBuilder, processMC, "process reconstructed info for MC", false); - - void processRec_OnlyIfDielectron(soa::Join const& collisions, FilteredV0s const& v0s, MyTracksIU const& tracks, MyBCs const& bcs) - { - build(collisions, v0s, tracks, bcs); - } - PROCESS_SWITCH(PhotonConversionBuilder, processRec_OnlyIfDielectron, "process reconstructed info for data", false); - - // void processRec_SWT_OnlyIfDielectron(soa::Join const& collisions, FilteredV0s const& v0s, MyTracksIU const& tracks, MyBCs const& bcs) - // { - // build(collisions, v0s, tracks, bcs); - // } - // PROCESS_SWITCH(PhotonConversionBuilder, processRec_SWT_OnlyIfDielectron, "process reconstructed info for data", false); -}; - -WorkflowSpec defineDataProcessing(ConfigContext const& context) +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { - return WorkflowSpec{ - adaptAnalysisTask(context, TaskName{"photon-conversion-builder"})}; + return o2::framework::WorkflowSpec{ + adaptAnalysisTask>(context, o2::framework::TaskName{"photon-conversion-builder"})}; } diff --git a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h new file mode 100644 index 00000000000..d793c59483c --- /dev/null +++ b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h @@ -0,0 +1,1799 @@ +// 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 photonconversionbuilder.ch +/// \brief this task produces photon data table with KFParticle. +/// \author Daiki Sekihata , Tokyo + +#ifndef HomogeneousField +#define HomogeneousField // o2-linter: disable=name/macro (name coming from KFParticle, not us, needed for KFParticle::SetField) +#endif + +#include "PWGEM/Dilepton/Utils/PairUtilities.h" +#include "PWGEM/PhotonMeson/Core/EmMlResponsePCM.h" +#include "PWGEM/PhotonMeson/Core/V0PhotonCandidate.h" +#include "PWGEM/PhotonMeson/Core/V0PhotonCut.h" +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" +#include "PWGEM/PhotonMeson/Utils/PCMUtilities.h" +#include "PWGEM/PhotonMeson/Utils/TrackSelection.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/Core/TPCVDriftManager.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Tools/KFparticle/KFUtilities.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using o2::framework::AxisSpec; +using o2::framework::Configurable; +using o2::framework::ConfigurableGroup; +using o2::framework::HistType; +using o2::framework::kTH1D; +using o2::framework::kTH1F; +using o2::framework::kTH2F; +using o2::framework::kTHnSparseF; +using o2::framework::Preslice; +using o2::framework::Produces; + +using MyCollisions = o2::soa::Join; +// using MyCollisionsWithSWT = soa::Join; +using MyCollisionsMC = o2::soa::Join; +using MyBCs = o2::soa::Join; + +using MyTracksIU = o2::soa::Join; +using MyTracksIUMC = o2::soa::Join; + +enum MatCorrType { + None = 0, + TGeo = 1, + LUT = 2 +}; + +enum TrackPropMode { + kProper = 0, + kFast = 1, + kBoth = 2 +}; + +enum V0DeduplicationMode { + Pairwise = 0, // old algorithm, V0 with best PCA + cosPA at the same time in a group wins + GreedyMatching = 1, // new algorithm, best score wins (score combines PCA and cosPA -- see detailed implementation in PCMUtilities.cxx) + GroupMatching = 2, // new algorithm, best combination of leg-disjoint V0s wins, not the single best V0 + KeepAll = 3 // no deduplication, keep all V0s +}; + +// Struct needed for deduplication mode 1. Used to keep track of v0 properties and a score based on pca and cosPA +struct V0CandidateHelper { + int v0ID = -1; + int colID = -1; + int posID = -1; + int eleID = -1; + float cosPA = -1.f; + float pca = -1.f; + float score = -1.f; + float mee = 0.f; // e+e- mass at the secondary vertex (GeV/c^2) + float pxAtPV = 0.f; + float pyAtPV = 0.f; + float pzAtPV = 0.f; + + V0CandidateHelper() = default; + + V0CandidateHelper(int v0, int col, int pos, int ele, float cpa, float p, float s, float m = 0.f) + : v0ID(v0), colID(col), posID(pos), eleID(ele), cosPA(cpa), pca(p), score(s), mee(m) + { + } + + bool operator==(const V0CandidateHelper& other) const { return score == other.score; } + bool operator<(const V0CandidateHelper& other) const { return score < other.score; } + bool operator>(const V0CandidateHelper& other) const { return score > other.score; } +}; + +// (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) +using CandKey = std::tuple; + +// Everything the truth diagnosis need, +struct DedupDiag { + std::map> blockersByKey; // rejected candidate -> ALL candidates that took one of its legs + std::vector snapshot; // ALL candidates, before deduplication + std::vector stored; // the survivors + std::map> lossMargin; +}; + +template +struct PhotonConversionBuilder { + + // using namespace o2; + // using namespace o2::soa; + // using namespace o2::framework; + // using namespace o2::framework::expressions; + // using namespace o2::constants::physics; + // using namespace o2::pwgem::photonmeson; + + Produces v0photonskf; + Produces v0legs; + Produces v0legsXYZ; + Produces v0legsDeDxMC; + Produces v0photonsphivpsi; + + // Produces v0photonskfcov; + // Produces events_ngpcm; + + // CCDB options + Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; + Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; + + // Operation and minimisation criteria + Configurable d_bz_input{"d_bz", -999, "bz field, -999 is automatic"}; + Configurable useMatCorrType{"useMatCorrType", 0, "0: none, 1: TGeo, 2: LUT"}; + Configurable modeTrackPropagation{"modeTrackPropagation", 0, "0: use real track propagation, including material, 1: use fast approximation using only geometry, 2: Use real track propagation and make comparison to fast propagation (only for debugging and testing)"}; + Configurable propV0LegsRadius{"propV0LegsRadius", 60.f, "Radius to which the V0 legs are propagated to calculate psipair and phiV"}; + + // deduplication settings + struct : ConfigurableGroup { + std::string prefix = "dedup_group"; + Configurable deduplicationMode{"deduplicationMode", 0, "0: Pairwise deduplication, 1: Based on Greedy matching (best score wins), 2: Based on Group matching (crossed pairs are both kept), 3: Keep all V0s, our default in the config is mode 0, however if a wrong configuration is used, the default will be mode 1 (Greedy matching) to avoid crashes"}; + Configurable deduplicationScoreWeight{"deduplicationScoreWeight", 0.5f, "0.:only pca goes into the score, 1: only cosPA goes itno score, any number in between is a mix of pca and cosPA"}; + Configurable cfgDedupTruthMaps{"cfgDedupTruthMaps", true, "fill the fine (dEta, dPhi, q) truth maps in MC"}; + Configurable dedupMaxGroupSize{"dedupMaxGroupSize", 12, "group matching (mode 2): conflict groups up to this size are solved exactly, larger ones greedily (capped at 16)"}; + } dedup; + + // event-level skim for derived data + struct : ConfigurableGroup { + std::string prefix = "skim_group"; + Configurable minNv0PerCollision{"minNv0PerCollision", -1, "store V0 photons only for collisions with at least this many surviving candidates (event-level skim for derived data). negative = store all"}; + Configurable maxMinvPairPerCollision{"maxMinvPairPerCollision", -1.f, "store a collision's photons only if at least one photon pair has qinv below this (GeV/c^2). negative = disabled"}; + } skim; + + // single track cuts + struct : ConfigurableGroup { + std::string prefix = "trackcut_group"; + Configurable min_ncluster_tpc{"min_ncluster_tpc", 0, "min ncluster tpc"}; + Configurable mincrossedrows{"mincrossedrows", 40, "min crossed rows"}; + Configurable moveTPCTracks{"moveTPCTracks", true, "Move TPC-only tracks under the collision assumption"}; + Configurable disableITSonlyTracks{"disableITSonlyTracks", false, "disable ITSonly tracks in V0 legs"}; + Configurable disableTPConlyTracks{"disableTPConlyTracks", false, "disable TPConly tracks in V0 legs"}; + Configurable requireITShit{"requireITShit", false, "require ITS hit to V0 legs"}; + Configurable maxchi2tpc{"maxchi2tpc", 5.0, "max chi2/NclsTPC"}; // default 4.0 + 1.0 + Configurable maxchi2its{"maxchi2its", 6.0, "max chi2/NclsITS"}; // default 5.0 + 1.0 + Configurable maxpt_itsonly{"maxpt_itsonly", 0.15, "max pT for ITSonly tracks at SV"}; + Configurable maxTPCNsigmaEl{"maxTPCNsigmaEl", 4.0, "max. TPC n sigma for electron"}; + Configurable minTPCNsigmaEl{"minTPCNsigmaEl", -4.0, "min. TPC n sigma for electron"}; + Configurable max_frac_shared_clusters_tpc{"max_frac_shared_clusters_tpc", 999.f, "max fraction of shared clusters in TPC"}; + Configurable dcanegtopv{"dcanegtopv", 0.1, "DCA Neg To PV"}; + Configurable dcapostopv{"dcapostopv", 0.1, "DCA Pos To PV"}; + Configurable maxX{"maxX", 83.1, "max X for track IU"}; + Configurable min_pt_trackiu{"min_pt_trackiu", 0.05, "min pT for trackiu"}; // this comes from online processing. pT of track seed is above 50 MeV/c in B = 0.5 T, 20 MeV/c in B = 0.2 T. + } trackcuts; + + // v0 photon cuts + struct : ConfigurableGroup { + std::string prefix = "v0cut_group"; + Configurable min_v0cospa_tpconly{"min_v0cospa_tpconly", 0.99, "min V0 CosPA to V0s with TPConly tracks"}; // double -> N.B. dcos(x)/dx = 0 at x=0) + Configurable min_v0cospa_its{"min_v0cospa_its", 0.99, "min V0 CosPA to V0s with ITs hits"}; // double -> N.B. dcos(x)/dx = 0 at x=0) + Configurable max_dcav0dau_tpconly{"max_dcav0dau_tpconly", 3.0, "max distance btween 2 legs to V0s with TPConly tracks"}; + Configurable max_dcav0dau_its{"max_dcav0dau_its", 0.5, "max distance btween 2 legs to V0s with ITS hits"}; + Configurable max_dcav0dau_itsibss{"max_dcav0dau_itsibss", 1.0, "max distance btween 2 legs to V0s with ITS hits on ITSib SS"}; + Configurable max_dcav0dau_tpc_inner_fc{"max_dcav0dau_tpc_inner_fc", 1.5, "max distance btween 2 legs to V0s with ITS hits on TPC inner FC"}; + Configurable min_v0radius{"min_v0radius", 1.0, "min v0 radius"}; + Configurable max_v0radius{"max_v0radius", 90.0, "max v0 radius"}; + Configurable margin_r_its{"margin_r_its", 3.0, "margin for r cut in cm"}; + Configurable margin_r_tpc{"margin_r_tpc", 7.0, "margin for r cut in cm"}; + Configurable margin_r_itstpc_tpc{"margin_r_itstpc_tpc", 7.0, "margin for r cut in cm"}; + Configurable margin_z{"margin_z", 7.0, "margin for z cut in cm"}; + Configurable max_alpha_ap{"max_alpha_ap", 0.95, "max alpha for AP cut"}; + Configurable max_qt_ap{"max_qt_ap", 0.01, "max qT for AP cut"}; + Configurable min_pt_v0{"min_pt_v0", 0.1, "min pT for v0 photons at PV"}; + Configurable max_pt_v0_itsonly{"max_pt_v0_itsonly", 0.3, "max pT for v0 photons wth 2 ITSonly tracks at PV"}; + Configurable max_eta_v0{"max_eta_v0", 0.9, "max eta for v0 photons at PV"}; + Configurable kfMassConstrain{"kfMassConstrain", -1.f, "mass constrain for the KFParticle mother particle"}; + Configurable max_r_req_its{"max_r_req_its", 16.0, "max Rxy for V0 with ITS hits"}; + Configurable min_r_tpconly{"min_r_tpconly", 32.0, "min Rxy for V0 with TPConly tracks"}; + Configurable max_r_itsmft_ss{"max_r_itsmft_ss", 66.0, "max Rxy for ITS/MFT SS"}; + Configurable max_dcatopv_xy_v0{"max_dcatopv_xy_v0", +1e+10, "max. DCAxy to PV for V0"}; + Configurable max_dcatopv_z_v0{"max_dcatopv_z_v0", +1e+10, "max. DCAz to PV for V0"}; + Configurable reject_v0_on_itsib{"reject_v0_on_itsib", true, "flag to reject v0s on ITSib"}; + } v0cuts; + + // PCM ML inference + struct : ConfigurableGroup { + std::string prefix = "mlcut_group"; + Configurable applyPCMMl{"applyPCMMl", false, "Flag to apply ML selections"}; + Configurable use2DBinning{"use2DBinning", false, "Flag to enable/disable 2D binning for ML application"}; + Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + Configurable nClassesPCMMl{"nClassesPCMMl", static_cast(o2::analysis::em_cuts_ml::NCutScores), "Number of classes in ML model"}; + Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; + Configurable centTypePCMMl{"centTypePCMMl", 2, "Centrality type for 2D ML application: FT0M:0, FT0A:1, FT0C:2"}; + Configurable> cutDirPCMMl{"cutDirPCMMl", std::vector{o2::analysis::em_cuts_ml::vecCutDir}, "Whether to reject score values greater or smaller than the threshold"}; + Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature1", "feature2"}, "Names of ML model input features"}; + Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{"path_ccdb/BDT_PCM/"}, "Paths of models on CCDB"}; + Configurable> onnxFileNames{"onnxFileNames", std::vector{"ModelHandler_onnx_PCM.onnx"}, "ONNX file names for each pT bin (if not from CCDB full path)"}; + Configurable> labelsBinsPCMMl{"labelsBinsPCMMl", std::vector{"bin 0", "bin 1"}, "Labels for bins"}; + Configurable> labelsCutScoresPCMMl{"labelsCutScoresPCMMl", std::vector{o2::analysis::em_cuts_ml::labelsCutScore}, "Labels for cut scores"}; + Configurable> binsPtPCMMl{"binsPtPCMMl", std::vector{0.0, +1e+10}, "pT bin limits for ML application"}; + Configurable> binsCentPCMMl{"binsCentPCMMl", std::vector{0.0, 100.0}, "Centrality bin limits for ML application"}; + Configurable> cutsPCMMlFlat{"cutsPCMMlFlat", {0.5}, "Flattened ML cuts: [bin0_score0, bin0_score1, ..., binN_scoreM]"}; + } mlcuts; + + o2::analysis::EmMlResponsePCM emMlResponse; + std::vector outputML; + V0PhotonCandidate v0photoncandidate; + o2::ccdb::CcdbApi ccdbApi; + + int mRunNumber{}; + float d_bz{}; + float maxSnp{}; // max sine phi for propagation + float maxStep{}; // max step size (cm) for propagation + o2::framework::Service ccdb{}; + o2::base::MatLayerCylSet* lut = nullptr; + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; + o2::aod::common::TPCVDriftManager mVDriftMgr; + + o2::framework::HistogramRegistry registry{ + "registry", + { + {"hCollisionCounter", "hCollisionCounter", {HistType::kTH1F, {{1, 0.5f, 1.5f}}}}, + {"V0/hAP", "Armenteros Podolanski;#alpha;q_{T} (GeV/c)", {HistType::kTH2F, {{200, -1.0f, 1.0f}, {250, 0, 0.25}}}}, + {"V0/hConversionPointXY", "conversion point in XY;X (cm);Y (cm)", {HistType::kTH2F, {{400, -100.0f, 100.0f}, {400, -100.f, 100.f}}}}, + {"V0/hConversionPointRZ", "conversion point in RZ;Z (cm);R_{xy} (cm)", {HistType::kTH2F, {{200, -100.0f, 100.0f}, {200, 0.f, 100.f}}}}, + {"V0/hPt", "pT of V0 at PV;p_{T,#gamma} (GeV/c)", {HistType::kTH1F, {{1000, 0.0f, 10.0f}}}}, + {"V0/hEtaPhi", "#eta vs. #varphi of V0 at PV;#varphi (rad.);#eta", {HistType::kTH2F, {{72, 0.0f, o2::constants::math::TwoPI}, {200, -1, +1}}}}, + {"V0/hCosPA", "cosine of pointing angle;cosine of pointing angle", {HistType::kTH1F, {{100, 0.99f, 1.f}}}}, + {"V0/hCosPA_Rxy", "cosine of pointing angle;r_{xy} (cm);cosine of pointing angle", {HistType::kTH2F, {{200, 0, 100}, {100, 0.99f, 1.f}}}}, + {"V0/hCosPAXY_Rxy", "cosine of pointing angle;r_{xy} (cm);cosine of pointing angle", {HistType::kTH2F, {{200, 0, 100}, {100, 0.99f, 1.f}}}}, + {"V0/hCosPARZ_Rxy", "cosine of pointing angle;r_{xy} (cm);cosine of pointing angle", {HistType::kTH2F, {{200, 0, 100}, {100, 0.99f, 1.f}}}}, + {"V0/hPCA", "distance between 2 legs at SV;PCA (cm)", {HistType::kTH1F, {{500, 0.0f, 5.f}}}}, + {"V0/hPCA_Rxy", "distance between 2 legs at SV;R_{xy} (cm);PCA (cm)", {HistType::kTH2F, {{200, 0, 100}, {500, 0.0f, 5.f}}}}, + {"V0/hPCA_CosPA", "distance between 2 legs at SV vs. cosPA;cosine of pointing angle;PCA (cm)", {HistType::kTH2F, {{100, 0.99, 1}, {500, 0.0f, 5.f}}}}, + {"V0/hDCAxyz", "DCA to PV;DCA_{xy} (cm);DCA_{z} (cm)", {HistType::kTH2F, {{200, -5.f, +5.f}, {200, -5.f, +5.f}}}}, + {"V0/hMeeSV_Rxy", "mee at SV vs. R_{xy};R_{xy} (cm);m_{ee} at SV (GeV/c^{2})", {HistType::kTH2F, {{200, 0.0f, 100.f}, {100, 0, 0.1f}}}}, + {"V0/hRxy_minX_ITSonly_ITSonly", "min trackiu X vs. R_{xy};trackiu X (cm);min trackiu X - R_{xy} (cm)", {HistType::kTH2F, {{100, 0.0f, 100.f}, {100, -50.0, 50.0f}}}}, + {"V0/hRxy_minX_ITSTPC_ITSTPC", "min trackiu X vs. R_{xy};trackiu X (cm);min trackiu X - R_{xy} (cm)", {HistType::kTH2F, {{100, 0.0f, 100.f}, {100, -50.0, 50.0f}}}}, + {"V0/hRxy_minX_ITSTPC_ITSonly", "min trackiu X vs. R_{xy};trackiu X (cm);min trackiu X - R_{xy} (cm)", {HistType::kTH2F, {{100, 0.0f, 100.f}, {100, -50.0, 50.0f}}}}, + {"V0/hRxy_minX_ITSTPC_TPC", "min trackiu X vs. R_{xy};trackiu X (cm);min trackiu X - R_{xy} (cm)", {HistType::kTH2F, {{100, 0.0f, 100.f}, {100, -50.0, 50.0f}}}}, + {"V0/hRxy_minX_TPC_TPC", "min trackiu X vs. R_{xy};trackiu X (cm);min trackiu X - R_{xy} (cm)", {HistType::kTH2F, {{100, 0.0f, 100.f}, {100, -50.0, 50.0f}}}}, + {"V0/hPCA_diffX", "PCA vs. trackiu X - R_{xy};distance btween 2 legs (cm);min trackiu X - R_{xy} (cm)", {HistType::kTH2F, {{500, 0.0f, 5.f}, {100, -50.0, 50.0f}}}}, + {"V0/hPhiVPsiPair", "phiV vs. psi pair;#psi_{pair} (rad.);#phi_{V} (rad.)", {HistType::kTH2F, {{500, -o2::constants::math::PI, o2::constants::math::PI}, {500, 0.0f, o2::constants::math::TwoPI}}}}, + {"V0Leg/hPt", "pT of leg at SV;p_{T,e} (GeV/c)", {HistType::kTH1F, {{1000, 0.0f, 10.0f}}}}, + {"V0Leg/hEtaPhi", "#eta vs. #varphi of leg at SV;#varphi (rad.);#eta", {HistType::kTH2F, {{72, 0.0f, o2::constants::math::TwoPI}, {200, -1, +1}}}}, + {"V0Leg/hRelDeltaPt", "pT resolution;p_{T} (GeV/c);#Deltap_{T}/p_{T}", {HistType::kTH2F, {{1000, 0.f, 10.f}, {100, 0, 1}}}}, + {"V0Leg/hDCAxyz", "DCA xy vs. z to PV;DCA_{xy} (cm);DCA_{z} (cm)", {HistType::kTH2F, {{200, -50.f, 50.f}, {200, -50.f, +50.f}}}}, + {"V0Leg/hdEdx_Pin", "TPC dE/dx vs. p_{in};p_{in} (GeV/c);TPC dE/dx", {HistType::kTH2F, {{1000, 0.f, 10.f}, {200, 0.f, 200.f}}}}, + {"V0Leg/hTPCNsigmaEl", "TPC dE/dx vs. p_{in};p_{in} (GeV/c);n #sigma_{e}^{TPC}", {HistType::kTH2F, {{1000, 0.f, 10.f}, {100, -5.f, +5.f}}}}, + {"V0Leg/hXZ", "track iu x vs. z;z (cm);x (cm)", {HistType::kTH2F, {{200, -100.f, 100.f}, {200, 0.f, 100.f}}}}, + }}; + + void init(o2::framework::InitContext&) + { + mRunNumber = 0; + d_bz = 0; + maxSnp = 0.85f; // could be changed later + maxStep = 2.00f; // could be changed later + + static constexpr std::array DedupNames = {"pairwise", "greedy matching", "group matching", "keep all"}; + if (dedup.deduplicationMode < 0 || dedup.deduplicationMode >= static_cast(DedupNames.size())) { + LOG(fatal) << "unknown deduplicationMode " << dedup.deduplicationMode.value; + } + LOGF(info, "photon-conversion-builder: deduplicationMode = %d (%s), score weight = %.2f", + dedup.deduplicationMode.value, DedupNames[dedup.deduplicationMode.value], dedup.deduplicationScoreWeight.value); + LOGF(info, "photon-conversion-builder: minNv0PerCollision = %d, maxMinvPairPerCollision = %f", + skim.minNv0PerCollision.value, skim.maxMinvPairPerCollision.value); + + ccdb->setURL(ccdburl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + switch (useMatCorrType) { + case MatCorrType::TGeo: + LOGF(info, "TGeo correction requested, loading geometry"); + if (!o2::base::GeometryManager::isGeometryLoaded()) { + ccdb->get(geoPath); + } + matCorr = o2::base::Propagator::MatCorrType::USEMatCorrTGeo; + break; + case MatCorrType::LUT: + LOGF(info, "LUT correction requested, loading LUT"); + lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get(lutPath)); + matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; + break; + default: + LOGF(info, "no correction requested, loading LUT by default!"); + lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get(lutPath)); + matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; + break; + } + + if (mlcuts.applyPCMMl) { + if (mlcuts.use2DBinning) { + int binsNPt = static_cast(mlcuts.binsPtPCMMl->size()) - 1; + int binsNCent = static_cast(mlcuts.binsCentPCMMl->size()) - 1; + int binsN = binsNPt * binsNCent; + if (binsN * static_cast(mlcuts.cutDirPCMMl->size()) != static_cast(mlcuts.cutsPCMMlFlat->size())) { + LOG(fatal) << "Mismatch in number of bins and cuts provided for 2D ML application: binsN * cutDirPCMMl: " << binsN * static_cast(mlcuts.cutDirPCMMl->size()) << " bins vs. cutsPCMMlFlat: " << mlcuts.cutsPCMMlFlat->size() << " cuts"; + } + if (binsN != static_cast(mlcuts.onnxFileNames->size())) { + LOG(fatal) << "Mismatch in number of bins and ONNX files provided for 2D ML application: binsN " << binsN << " bins vs. onnxFileNames: " << mlcuts.onnxFileNames->size() << " ONNX files"; + } + if (binsN != static_cast(mlcuts.labelsBinsPCMMl->size())) { + LOG(fatal) << "Mismatch in number of bins and labels provided for 2D ML application: binsN:" << binsN << " bins vs. labelsBinsPCMMl: " << mlcuts.labelsBinsPCMMl->size() << " labels"; + } + if (static_cast(mlcuts.cutDirPCMMl->size()) != mlcuts.nClassesPCMMl) { + LOG(fatal) << "Mismatch in number of classes and cut directions provided for 2D ML application: nClassesPCMMl: " << mlcuts.nClassesPCMMl << " classes vs. cutDirPCMMl: " << mlcuts.cutDirPCMMl->size() << " cut directions"; + } + if (static_cast(mlcuts.labelsCutScoresPCMMl->size()) != mlcuts.nClassesPCMMl) { + LOG(fatal) << "Mismatch in number of labels for cut scores and number of classes provided for 2D ML application: nClassesPCMMl: " << mlcuts.nClassesPCMMl << " classes vs. labelsCutScoresPCMMl: " << mlcuts.labelsCutScoresPCMMl->size() << " labels"; + } + o2::framework::LabeledArray cutsPCMMl(mlcuts.cutsPCMMlFlat->data(), binsN, mlcuts.nClassesPCMMl, mlcuts.labelsBinsPCMMl, mlcuts.labelsCutScoresPCMMl); + emMlResponse.configure2D(mlcuts.binsPtPCMMl, mlcuts.binsCentPCMMl, cutsPCMMl, mlcuts.cutDirPCMMl, mlcuts.nClassesPCMMl); + } else { + int binsNPt = static_cast(mlcuts.binsPtPCMMl->size()) - 1; + if (binsNPt * static_cast(mlcuts.cutDirPCMMl->size()) != static_cast(mlcuts.cutsPCMMlFlat->size())) { + LOG(fatal) << "Mismatch in number of pT bins and cuts provided for ML application: binsNPt * cutDirPCMMl:" << binsNPt * mlcuts.cutDirPCMMl->size() << " bins vs. cutsPCMMlFlat: " << mlcuts.cutsPCMMlFlat->size() << " cuts"; + } + if (binsNPt != static_cast(mlcuts.onnxFileNames->size())) { + LOG(fatal) << "Mismatch in number of pT bins and ONNX files provided for ML application: binsNPt " << binsNPt << " bins vs. onnxFileNames: " << mlcuts.onnxFileNames->size() << " ONNX files"; + } + if (binsNPt != static_cast(mlcuts.labelsBinsPCMMl->size())) { + LOG(fatal) << "Mismatch in number of pT bins and labels provided for ML application: binsNPt:" << binsNPt << " bins vs. labelsBinsPCMMl: " << mlcuts.labelsBinsPCMMl->size() << " labels"; + } + if (mlcuts.nClassesPCMMl != static_cast(mlcuts.cutDirPCMMl->size())) { + LOG(fatal) << "Mismatch in number of classes and cut directions provided for ML application: nClassesPCMMl: " << mlcuts.nClassesPCMMl << " classes vs. cutDirPCMMl: " << mlcuts.cutDirPCMMl->size() << " cut directions"; + } + if (static_cast(mlcuts.labelsCutScoresPCMMl->size()) != mlcuts.nClassesPCMMl) { + LOG(fatal) << "Mismatch in number of labels for cut scores and number of classes provided for ML application: nClassesPCMMl:" << mlcuts.nClassesPCMMl << " classes vs. labelsCutScoresPCMMl: " << mlcuts.labelsCutScoresPCMMl->size() << " labels"; + } + o2::framework::LabeledArray cutsPCMMl(mlcuts.cutsPCMMlFlat->data(), binsNPt, mlcuts.nClassesPCMMl, mlcuts.labelsBinsPCMMl, mlcuts.labelsCutScoresPCMMl); + emMlResponse.configure(mlcuts.binsPtPCMMl, cutsPCMMl, mlcuts.cutDirPCMMl, mlcuts.nClassesPCMMl); + } + if (mlcuts.loadModelsFromCCDB) { + ccdbApi.init(ccdburl); + emMlResponse.setModelPathsCCDB(mlcuts.onnxFileNames, ccdbApi, mlcuts.modelPathsCCDB, mlcuts.timestampCCDB); + } else { + emMlResponse.setModelPathsLocal(mlcuts.onnxFileNames); + } + emMlResponse.cacheInputFeaturesIndices(mlcuts.namesInputFeatures); + emMlResponse.init(); + if (mlcuts.nClassesPCMMl == o2::analysis::NClassesML::kTwo) { + registry.add("V0/hBDTBackgroundScoreBeforeCutVsPt", "BDT background score before cut vs pT; pT (GeV/c); BDT background score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); + registry.add("V0/hBDTBackgroundScoreAfterCutVsPt", "BDT background score after cut vs pT; pT (GeV/c); BDT background score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); + registry.add("V0/hBDTSignalScoreBeforeCutVsPt", "BDT signal score before cut vs pT; pT (GeV/c); BDT signal score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); + registry.add("V0/hBDTSignalScoreAfterCutVsPt", "BDT signal score after cut vs pT; pT (GeV/c); BDT signal score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); + } else if (mlcuts.nClassesPCMMl == o2::analysis::NClassesML::kThree) { + registry.add("V0/hBDTBackgroundScoreBeforeCutVsPt", "BDT background score before cut vs pT; pT (GeV/c); BDT background score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); + registry.add("V0/hBDTBackgroundScoreAfterCutVsPt", "BDT background score after cut vs pT; pT (GeV/c); BDT background score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); + registry.add("V0/hBDTPrimaryPhotonScoreBeforeCutVsPt", "BDT primary photon score before cut vs pT; pT (GeV/c); BDT primary photon score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); + registry.add("V0/hBDTPrimaryPhotonScoreAfterCutVsPt", "BDT primary photon score after cut vs pT; pT (GeV/c); BDT primary photon score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); + registry.add("V0/hBDTSecondaryPhotonScoreBeforeCutVsPt", "BDT secondary photon score before cut vs pT; pT (GeV/c); BDT secondary photon score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); + registry.add("V0/hBDTSecondaryPhotonScoreAfterCutVsPt", "BDT secondary photon score after cut vs pT; pT (GeV/c); BDT secondary photon score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); + } else { + registry.add("V0/hBDTScoreBeforeCutVsPt", "BDT score before cut vs pT; pT (GeV/c); BDT score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); + registry.add("V0/hBDTScoreAfterCutVsPt", "BDT score after cut vs pT; pT (GeV/c); BDT score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); + } + } + + // Compare proper propagation and fast geometrical propagation + if (modeTrackPropagation == TrackPropMode::kBoth) { + registry.add("V0Leg/hDCAxyPropagationCompare", "Comparison of DCA_{xy} propagation;DCA_{xy} (cm) proper propagation; DCA_{xy} (cm) geom. propagation", {HistType::kTH2F, {{200, -10., 10.}, {200, -10., 10.}}}); + registry.add("V0Leg/hDCAzPropagationCompare", "Comparison of DCA_{z} propagation;DCA_{z} (cm) proper propagation; DCA_{z} (cm) geom. propagation", {HistType::kTH2F, {{200, -10., 10.}, {200, -10., 10.}}}); + registry.add("V0/hPhivPropagationCompare", "Comparison of #phi_{v};#phi_{v} proper propagation; #phi_{v} geom. propagation", {HistType::kTH2F, {{100, 0., 1.6}, {100, 0., 1.6}}}); + registry.add("V0/hPsiPairPropagationCompare", "Comparison of #Psi_{pair};#Psi_{pair} proper propagation; #Psi_{pair} geom. propagation", {HistType::kTH2F, {{100, 0., 1.6}, {100, 0., 1.6}}}); + } + + // Make sure the dedup.deduplicationScoreWeight is between 0 and 1 + if (dedup.deduplicationScoreWeight > 1.f) { // o2-linter: disable=magic-number (score has to be below unity) + LOG(warning) << "deduplicationScoreWeight is larger than unity which is not allowed"; + } + if (dedup.deduplicationScoreWeight < 0.f) { // o2-linter: disable=magic-number (score has to be above zero) + LOG(warning) << "deduplicationScoreWeight is smaller than zero which is not allowed"; + } + + if (doprocessMC) { + const AxisSpec axQ{60, 0.f, 0.3f, "q_{inv}^{true} (GeV/c)"}; + const AxisSpec axDEta{80, -1.6f, 1.6f, "#Delta#eta_{#gamma#gamma}^{true}"}; + const AxisSpec axClass{3, -0.5f, 2.5f, "0 = true, 1 = cross-leg fake, 2 = other fake"}; + // lostByPartnerFake is a subset of lostByCrossFake, and a pair can appear in several of them. + for (const auto& nm : {"before", "bothSurvive", "bothSurviveSameColl", "oneLost", "bothLost", + "lostByCrossFake", "lostByPartnerFake", "lostByOtherFake", "lostByTrue"}) { + const std::string dir = std::string("MCDedup/Pairs/") + nm + "/"; + registry.add((dir + "hQ").c_str(), "truth photon pairs with >=1 true V0 candidate before deduplication;q_{inv}^{true} (GeV/c);pairs", kTH1F, {axQ}, true); + registry.add((dir + "hDEta").c_str(), "truth photon pairs with >=1 true V0 candidate before deduplication;#Delta#eta^{true};pairs", kTH1F, {axDEta}, true); + } + registry.add("MCDedup/Candidates/hClassStage", "candidate composition;class;0 = before, 1 = stored", kTH2F, {axClass, {2, -0.5f, 1.5f}}, true); + + const AxisSpec axBlocker{4, -0.5f, 3.5f, "0 = true, 1 = cross-leg fake, 2 = other fake, 3 = no blocker (cut, not dedup)"}; + + registry.add("MCDedup/Photons/hFate", "fate of the true photons;0 = alive, 1 = lost (blocked), 2 = lost (no blocker);photons", kTH1F, {{3, -0.5f, 2.5f}}, true); + registry.add("MCDedup/Photons/hBlockerClass", "blockers of a killed true photon (ENTRIES = blockers, not photons);class of the blocker;blocker entries", kTH1F, {axBlocker}, true); + registry.add("MCDedup/Photons/hKillMargin", "mode 0/1 only;S_{victim} - S_{winner};kills", kTH1F, {{200, 0.f, 1.f}}, true); + registry.add("MCDedup/Photons/hVictimVsWinnerPCA", "PCA of the two;PCA_{victim} (cm);PCA_{winner} (cm)", kTH2F, {{60, 0.f, 3.f}, {60, 0.f, 3.f}}, true); + // Reason for photon building failure + // x = 0 -> an equally large solution containing it existed + // x > 0 -> keeping it would have cost x candidates + registry.add("MCDedup/Photons/hLostMargin", "why the true photon lost (mode 2);cardinality deficit;#Sigma S(best set with it) - #Sigma S(winner)", kTH2F, {{5, -0.5f, 4.5f}, {100, 0.f, 2.f}}, true); + if (dedup.cfgDedupTruthMaps) { + const AxisSpec axDEtaFine{640, -1.6f, 1.6f, "#Delta#eta^{true}"}; + const AxisSpec axDPhi{144, -o2::constants::math::PI, o2::constants::math::PI, "#Delta#varphi^{true} (rad)"}; + for (const auto& nm : {"before", "bothSurvive", "bothLost", "lostByPartnerFake"}) { + registry.add((std::string("MCDedup/Pairs/") + nm + "/hMap").c_str(), "truth photon pairs", kTHnSparseF, {axDEtaFine, axDPhi, axQ}, true); + } + } + } + } + + void initCCDB(MyBCs::iterator const& bc) + { + if (mRunNumber == bc.runNumber()) { + return; + } + mRunNumber = bc.runNumber(); + // In case override, don't proceed, please - no CCDB access required + if (d_bz_input > -990) { // o2-linter: disable=magic-number (override value) + d_bz = d_bz_input; + o2::parameters::GRPMagField grpmag; + if (std::fabs(d_bz) > 1e-5) { // o2-linter: disable=magic-number (override value) + grpmag.setL3Current(30000.f / (d_bz / 5.0f)); // o2-linter: disable=magic-number (override value) + } + o2::base::Propagator::initFieldFromGRP(&grpmag); + + return; + } + + auto run3grp_timestamp = bc.timestamp(); + o2::base::Propagator::initFieldFromGRP(&bc.grpMagField()); + // Fetch magnetic field from ccdb for current collision + d_bz = bc.grpMagField().getNominalL3Field(); + LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; + + if (useMatCorrType == 2) { // o2-linter: disable=magic-number (material budget correction) + // setMatLUT only after magfield has been initalized (setMatLUT has implicit and problematic init field call if not) + o2::base::Propagator::Instance()->setMatLUT(lut); + } + /// Set magnetic field for KF vertexing + const float magneticField = o2::base::Propagator::Instance()->getNominalBz(); + KFParticle::SetField(magneticField); + + mVDriftMgr.init(&ccdb->instance()); + } + + void updateCCDB(MyBCs::iterator const& bc) + { + auto timestamp = bc.timestamp(); + + mVDriftMgr.update(timestamp); + } + + std::pair> its_ib_Requirement = {0, {0, 1, 2}}; // no hit on 3 ITS ib layers. + template + bool checkV0leg(TTrack const& track) + { + using namespace o2::pwgem::photonmeson; + if constexpr (isMC) { + if (!track.has_mcParticle()) { + return false; + } + } + + if (trackcuts.disableITSonlyTracks && isITSonlyTrack(track)) { + return false; + } + + if (trackcuts.disableTPConlyTracks && isTPConlyTrack(track)) { + return false; + } + + if (trackcuts.requireITShit && !track.hasITS()) { + return false; + } + + if (track.x() > trackcuts.maxX) { + return false; + } + + if (!track.hasITS() && !track.hasTPC()) { + return false; + } + + if (track.hasITS() && !track.hasTPC() && (track.hasTRD() || track.hasTOF())) { // remove unrealistic track. this should not happen. + return false; + } + + if (track.hasTPC()) { + if (track.tpcNClsFound() < trackcuts.min_ncluster_tpc) { + return false; + } + if (track.tpcNClsCrossedRows() < trackcuts.mincrossedrows || track.tpcChi2NCl() > trackcuts.maxchi2tpc) { + return false; + } + if (track.tpcFractionSharedCls() > trackcuts.max_frac_shared_clusters_tpc) { + return false; + } + if (track.tpcNSigmaEl() < trackcuts.minTPCNsigmaEl || trackcuts.maxTPCNsigmaEl < track.tpcNSigmaEl()) { + return false; + } + } + + if (track.hasITS()) { + if (track.itsChi2NCl() > trackcuts.maxchi2its) { + return false; + } + + if (v0cuts.reject_v0_on_itsib) { + auto hits_ib = std::count_if(its_ib_Requirement.second.begin(), its_ib_Requirement.second.end(), [&](auto&& requiredLayer) { return track.itsClusterMap() & (1 << requiredLayer); }); + bool its_ob_only = hits_ib <= its_ib_Requirement.first; + if (!its_ob_only) { + return false; + } + } + } + + return true; + } + + float cospaXY_KF(const KFParticle& kfp, const KFParticle& PV) + { + float lx = kfp.GetX() - PV.GetX(); // flight length X + float ly = kfp.GetY() - PV.GetY(); // flight length Y + + float px = kfp.GetPx(); + float py = kfp.GetPy(); + float cospaXY = RecoDecay::dotProd(std::array{lx, ly}, std::array{px, py}) / (RecoDecay::sqrtSumOfSquares(lx, ly) * RecoDecay::sqrtSumOfSquares(px, py)); + if (cospaXY < -1.f) { + return -1.f; + } + if (cospaXY > 1.f) { + return 1.f; + } + return cospaXY; + } + + float cospaRZ_KF(const KFParticle& kfp, const KFParticle& PV) + { + float lx = kfp.GetX() - PV.GetX(); // flight length X + float ly = kfp.GetY() - PV.GetY(); // flight length Y + float lz = kfp.GetZ() - PV.GetZ(); // flight length Z + float lt = RecoDecay::sqrtSumOfSquares(lx, ly); // flight length R, i.e. transverse plane. + + float pt = RecoDecay::sqrtSumOfSquares(kfp.GetPx(), kfp.GetPy()); + float pz = kfp.GetPz(); + + float cospaRZ = RecoDecay::dotProd(std::array{lt, lz}, std::array{pt, pz}) / (RecoDecay::sqrtSumOfSquares(lt, lz) * RecoDecay::sqrtSumOfSquares(pt, pz)); + if (cospaRZ < -1.f) { + return -1.f; + } + if (cospaRZ > 1.f) { + return 1.f; + } + return cospaRZ; + } + + template + void fillTrackTable(TTrack const& track, TShiftedTrack const& shiftedtrack, const float dcaXY, const float dcaZ) + { + v0legs(track.collisionId(), track.globalIndex(), track.sign(), + shiftedtrack.GetPx(), shiftedtrack.GetPy(), shiftedtrack.GetPz(), dcaXY, dcaZ, + track.tpcNClsFindable(), track.tpcNClsFindableMinusFound(), track.tpcNClsFindableMinusCrossedRows(), track.tpcNClsShared(), + track.tpcChi2NCl(), track.tpcInnerParam(), track.tpcSignal(), + track.tpcNSigmaEl(), track.tpcNSigmaPi(), + track.itsClusterSizes(), track.itsChi2NCl(), track.detectorMap()); + + v0legsXYZ(shiftedtrack.GetX(), shiftedtrack.GetY(), shiftedtrack.GetZ()); + + if constexpr (isMC) { + v0legsDeDxMC(track.mcTunedTPCSignal()); + } + } + + template + void fillV0Table(TV0 const& v0, const bool filltable) + { + using namespace o2::pwgem::photonmeson; + // Get tracks + const auto& pos = v0.template posTrack_as(); + const auto& ele = v0.template negTrack_as(); + const auto& collision = v0.template collision_as(); // collision where this v0 belongs to. + // LOGF(info, "v0.collisionId() = %d, pos.collisionId() = %d, ele.collisionId() = %d", v0.collisionId(), pos.collisionId(), ele.collisionId()); + + if (pos.sign() * ele.sign() > 0) { // reject same sign pair + return; + } + + if (pos.pt() < trackcuts.min_pt_trackiu || ele.pt() < trackcuts.min_pt_trackiu) { + return; + } + + if (pos.globalIndex() == ele.globalIndex()) { + return; + } + + if (isITSonlyTrack(pos) && !ele.hasITS()) { + return; + } + + if (isITSonlyTrack(ele) && !pos.hasITS()) { + return; + } + + if (!checkV0leg(pos) || !checkV0leg(ele)) { + return; + } + + // LOGF(info, "v0.collisionId() = %d , v0.posTrackId() = %d , v0.negTrackId() = %d", v0.collisionId(), v0.posTrackId(), v0.negTrackId()); + + // if(isTPConlyTrack(ele)){ + // // LOGF(info, "TPConly: ele.globalIndex() = %d, ele.x() = %f, ele.y() = %f, ele.z() = %f, ele.tgl() = %f, ele.alpha() = %f, ele.snp() = %f, ele.signed1Pt() = %f", ele.globalIndex(), ele.x(), ele.y(), ele.z(), ele.tgl(), ele.alpha(), ele.snp(), ele.signed1Pt()); + // // LOGF(info, "TPConly: ele.globalIndex() = %d, ele.cYY() = %f, ele.cZY() = %f, ele.cZZ() = %f, ele.cSnpY() = %f, ele.cSnpZ() = %f, ele.cSnpSnp() = %f, ele.cTglY() = %f, ele.cTglZ() = %f, ele.cTglSnp() = %f, ele.cTglTgl() = %f, ele.c1PtY() = %f, ele.c1PtZ() = %f, ele.c1PtSnp() = %f, ele.c1PtTgl() = %f, ele.c1Pt21Pt2() = %f", ele.globalIndex(), ele.cYY(), ele.cZY(), ele.cZZ(), ele.cSnpY(), ele.cSnpZ(), ele.cSnpSnp(), ele.cTglY(), ele.cTglZ(), ele.cTglSnp(), ele.cTglTgl(), ele.c1PtY(), ele.c1PtZ(), ele.c1PtSnp(), ele.c1PtTgl(), ele.c1Pt21Pt2()); + // } + + // Calculate DCA with respect to the collision associated to the v0, not individual tracks + std::array dcaInfo{}; + + auto pTrack = getTrackParCov(pos); + if (trackcuts.moveTPCTracks && isTPConlyTrack(pos) && !mVDriftMgr.moveTPCTrack(collision, pos, pTrack)) { + LOGP(error, "failed correction for positive tpc track"); + return; + } + auto pTrackC = pTrack; + o2::math_utils::Point3D vtxPrim{ + collision.posX(), + collision.posY(), + collision.posZ()}; + if (modeTrackPropagation != TrackPropMode::kProper) { + dcaInfo = CalculateDCAFast(pTrackC, vtxPrim, d_bz); + } + pTrackC.setPID(o2::track::PID::Electron); + + std::array dcaInfoFast = dcaInfo; + if (modeTrackPropagation != TrackPropMode::kFast) { + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, pTrackC, 2.f, matCorr, &dcaInfo); + if (filltable && modeTrackPropagation == TrackPropMode::kBoth) { + registry.fill(HIST("V0Leg/hDCAxyPropagationCompare"), dcaInfo[0], dcaInfoFast[0]); + registry.fill(HIST("V0Leg/hDCAzPropagationCompare"), dcaInfo[1], dcaInfoFast[1]); + } + } + auto posdcaXY = dcaInfo[0]; + auto posdcaZ = dcaInfo[1]; + + auto nTrack = getTrackParCov(ele); + if (trackcuts.moveTPCTracks && isTPConlyTrack(ele) && !mVDriftMgr.moveTPCTrack(collision, ele, nTrack)) { + LOGP(error, "failed correction for negative tpc track"); + return; + } + auto nTrackC = nTrack; + if (modeTrackPropagation != TrackPropMode::kProper) { + dcaInfo = CalculateDCAFast(nTrackC, vtxPrim, d_bz); + } + nTrackC.setPID(o2::track::PID::Electron); + + dcaInfoFast = dcaInfo; + if (modeTrackPropagation != TrackPropMode::kFast) { + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, nTrackC, 2.f, matCorr, &dcaInfo); + if (filltable && modeTrackPropagation == TrackPropMode::kBoth) { + registry.fill(HIST("V0Leg/hDCAxyPropagationCompare"), dcaInfo[0], dcaInfoFast[0]); + registry.fill(HIST("V0Leg/hDCAzPropagationCompare"), dcaInfo[1], dcaInfoFast[1]); + } + } + auto eledcaXY = dcaInfo[0]; + auto eledcaZ = dcaInfo[1]; + + if (std::fabs(posdcaXY) < trackcuts.dcapostopv || std::fabs(eledcaXY) < trackcuts.dcanegtopv) { + return; + } + + std::array xyz = {0.f, 0.f, 0.f}; + Vtx_recalculationParCov(o2::base::Propagator::Instance(), pTrack, nTrack, xyz, matCorr); + float rxy_tmp = RecoDecay::sqrtSumOfSquares(xyz[0], xyz[1]); + if (rxy_tmp > trackcuts.maxX + v0cuts.margin_r_tpc) { + return; + } + if (rxy_tmp < std::fabs(xyz[2]) * std::tan(2 * std::atan(std::exp(-v0cuts.max_eta_v0))) - v0cuts.margin_z) { + return; // RZ line cut + } + + float phiv = 999.f; + float psipair = 999.f; + float phivFast = 999.f; + float psipairFast = 999.f; + float baseR = std::hypot(xyz[0], xyz[1]); + // This method uses the track Helix instead of the full propagation. + // Hence, it is only an approximation but much faster + if (modeTrackPropagation != TrackPropMode::kProper) { + + o2::track::TrackAuxPar helixPosEle(nTrack, d_bz); + o2::track::TrackAuxPar helixPosPos(pTrack, d_bz); + + float diffX = helixPosEle.xC - helixPosPos.xC; + float diffY = helixPosEle.yC - helixPosPos.yC; + auto phiHelix = RecoDecay::constrainAngle(std::atan2(diffY, diffX) - o2::constants::math::PI / 2.); + + // Electron + float arcLenghtEle = helixPosEle.rC * 0.9 > propV0LegsRadius ? std::asin(propV0LegsRadius / helixPosEle.rC) * helixPosEle.rC : o2::constants::math::PI / 2.2 * helixPosEle.rC; // This assumes that the photon momentum vector is a tangent of the circle // o2-linter: disable=magic-number (geometrical assumption for propagation) + auto propTrackEle = getPropMomentumFromTrackHelix(arcLenghtEle, ele, helixPosEle, d_bz / 10., phiHelix - ele.phi()); + // Positron + float arcLenghtPos = helixPosPos.rC * 0.9 > propV0LegsRadius ? std::asin(propV0LegsRadius / helixPosPos.rC) * helixPosPos.rC : o2::constants::math::PI / 2.2 * helixPosPos.rC; // This assumes that the photon momentum vector is a tangent of the circle // o2-linter: disable=magic-number (geometrical assumption for propagation) + auto propTrackPos = getPropMomentumFromTrackHelix(arcLenghtPos, pos, helixPosPos, d_bz / 10., phiHelix - pos.phi()); + + phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(propTrackPos[0], propTrackPos[1], propTrackPos[2], propTrackEle[0], propTrackEle[1], propTrackEle[2], pos.sign(), ele.sign(), d_bz); + psipair = o2::aod::pwgem::dilepton::utils::pairutil::getPsiPair(propTrackPos[0], propTrackPos[1], propTrackPos[2], propTrackEle[0], propTrackEle[1], propTrackEle[2]); + + // Store values for later comparison + phivFast = phiv; + psipairFast = psipair; + } + // This uses the full propagation including material effects + if (modeTrackPropagation != TrackPropMode::kFast) { + std::array offsetsR = {propV0LegsRadius, 30.f, 10.f}; + bool pPropagatedSuccess = false; + bool nPropagatedSuccess = false; + auto pTrackProp = pTrack; + auto nTrackProp = nTrack; + for (const auto& offsetR : offsetsR) { + pTrackProp = pTrack; + pTrackProp.setPID(o2::track::PID::Electron); + nTrackProp = nTrack; + nTrackProp.setPID(o2::track::PID::Electron); + + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, pTrackProp, 2.f, matCorr, &dcaInfo); + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, nTrackProp, 2.f, matCorr, &dcaInfo); + + pPropagatedSuccess = o2::base::Propagator::Instance()->propagateToR(pTrackProp, baseR + offsetR); + nPropagatedSuccess = o2::base::Propagator::Instance()->propagateToR(nTrackProp, baseR + offsetR); + + if (pPropagatedSuccess && nPropagatedSuccess) { + KFPTrack kfp_track_posProp = createKFPTrackFromTrackParCov(pTrackProp, pos.sign(), pos.tpcNClsFound(), pos.tpcChi2NCl()); + KFPTrack kfp_track_eleProp = createKFPTrackFromTrackParCov(nTrackProp, ele.sign(), ele.tpcNClsFound(), ele.tpcChi2NCl()); + phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(kfp_track_posProp.GetPx(), kfp_track_posProp.GetPy(), kfp_track_posProp.GetPz(), kfp_track_eleProp.GetPx(), kfp_track_eleProp.GetPy(), kfp_track_eleProp.GetPz(), pos.sign(), ele.sign(), d_bz); + psipair = o2::aod::pwgem::dilepton::utils::pairutil::getPsiPair(kfp_track_posProp.GetPx(), kfp_track_posProp.GetPy(), kfp_track_posProp.GetPz(), kfp_track_eleProp.GetPx(), kfp_track_eleProp.GetPy(), kfp_track_eleProp.GetPz()); + break; + } + LOG(debug) << "Propagation to offset" << offsetR << " cm failed for " << (pPropagatedSuccess ? "negative" : "positive") << " track. Trying smaller offset."; + } + if (filltable && modeTrackPropagation == TrackPropMode::kBoth) { + registry.fill(HIST("V0/hPhivPropagationCompare"), phiv, phivFast); + registry.fill(HIST("V0/hPsiPairPropagationCompare"), psipair, psipairFast); + } + } + + if (phiv == 999.f || psipair == 999.f) { // o2-linter: disable=magic-number (nonsensical default value) + LOG(debug) << "Propagation failed for all radii (" << propV0LegsRadius << ", 30, 10 cm). Using default values for phiv and psipair (999.f)."; + } + + KFPTrack kfp_track_pos = createKFPTrackFromTrackParCov(pTrack, pos.sign(), pos.tpcNClsFound(), pos.tpcChi2NCl()); + KFPTrack kfp_track_ele = createKFPTrackFromTrackParCov(nTrack, ele.sign(), ele.tpcNClsFound(), ele.tpcChi2NCl()); + KFParticle kfp_pos(kfp_track_pos, kPositron); + KFParticle kfp_ele(kfp_track_ele, kElectron); + std::array GammaDaughters{&kfp_pos, &kfp_ele}; + + KFParticle gammaKF; + gammaKF.SetConstructMethod(2); + gammaKF.Construct(GammaDaughters.data(), 2); + if (v0cuts.kfMassConstrain > -0.1) { // o2-linter: disable=magic-number (nonsensical default value) + gammaKF.SetNonlinearMassConstraint(v0cuts.kfMassConstrain); + } + KFPVertex kfpVertex = createKFPVertexFromCollision(collision); + KFParticle KFPV(kfpVertex); + + // Transport the gamma to the recalculated decay vertex + KFParticle gammaKF_DecayVtx = gammaKF; // with respect to (0,0,0) + gammaKF_DecayVtx.TransportToPoint(xyz.data()); + + float cospa_kf = cpaFromKF(gammaKF_DecayVtx, KFPV); + if (!ele.hasITS() && !pos.hasITS()) { + if (cospa_kf < v0cuts.min_v0cospa_tpconly) { + return; + } + } else { + if (cospa_kf < v0cuts.min_v0cospa_its) { + return; + } + } + + float rxy = RecoDecay::sqrtSumOfSquares(gammaKF_DecayVtx.GetX(), gammaKF_DecayVtx.GetY()); + if (rxy > trackcuts.maxX + v0cuts.margin_r_tpc) { + return; + } + if (rxy < std::fabs(gammaKF_DecayVtx.GetZ()) * std::tan(2 * std::atan(std::exp(-v0cuts.max_eta_v0))) - v0cuts.margin_z) { + return; // RZ line cut + } + if (rxy < v0cuts.min_v0radius || v0cuts.max_v0radius < rxy) { + return; + } + + if (!filltable) { + if (isITSTPCTrack(pos) && isITSTPCTrack(ele)) { + registry.fill(HIST("V0/hRxy_minX_ITSTPC_ITSTPC"), std::min(pTrack.getX(), nTrack.getX()), std::min(pTrack.getX(), nTrack.getX()) - rxy); // trackiu.x() - rxy should be positive + } else if (isITSonlyTrack(pos) && isITSonlyTrack(ele)) { + registry.fill(HIST("V0/hRxy_minX_ITSonly_ITSonly"), std::min(pTrack.getX(), nTrack.getX()), std::min(pTrack.getX(), nTrack.getX()) - rxy); // trackiu.x() - rxy should be positive + } else if ((isITSTPCTrack(pos) && isITSonlyTrack(ele)) || (isITSTPCTrack(ele) && isITSonlyTrack(pos))) { + registry.fill(HIST("V0/hRxy_minX_ITSTPC_ITSonly"), std::min(pTrack.getX(), nTrack.getX()), std::min(pTrack.getX(), nTrack.getX()) - rxy); // trackiu.x() - rxy should be positive + } else if (isITSTPCTrack(pos) && !ele.hasITS()) { + registry.fill(HIST("V0/hRxy_minX_ITSTPC_TPC"), std::min(pTrack.getX(), 83.f), std::min(pTrack.getX(), 83.f) - rxy); // trackiu.x() - rxy should be positive + } else if (isITSTPCTrack(ele) && !pos.hasITS()) { + registry.fill(HIST("V0/hRxy_minX_ITSTPC_TPC"), std::min(nTrack.getX(), 83.f), std::min(nTrack.getX(), 83.f) - rxy); // trackiu.x() - rxy should be positive + } else { + registry.fill(HIST("V0/hRxy_minX_TPC_TPC"), std::min(83.f, 83.f), std::min(83.f, 83.f) - rxy); // trackiu.x() - rxy should be positive + } + } + + if (pos.hasITS() && ele.hasITS()) { // ITSonly-ITSonly, ITSTPC-ITSTPC, ITSTPC-ITSonly + if (rxy > std::min(pTrack.getX(), nTrack.getX()) + v0cuts.margin_r_its) { + return; + } + } else if (!pos.hasITS() && ele.hasITS()) { // ITSTPC-TPC + if (rxy > std::min(83.f, nTrack.getX()) + v0cuts.margin_r_itstpc_tpc) { + return; + } + } else if (pos.hasITS() && !ele.hasITS()) { // ITSTPC-TPC + if (rxy > std::min(pTrack.getX(), 83.f) + v0cuts.margin_r_itstpc_tpc) { + return; + } + } else if (!pos.hasITS() && !ele.hasITS()) { // TPC-TPC + if (rxy > std::min(83.f, 83.f) + v0cuts.margin_r_tpc) { + return; + } + } + + if ((!pos.hasITS() || !ele.hasITS()) && rxy < v0cuts.max_r_req_its) { // conversion points smaller than v0cuts.max_r_req_its have to be detected with ITS hits. + return; + } + + if ((!pos.hasITS() && !ele.hasITS()) && rxy < v0cuts.min_r_tpconly) { // TPConly tracks can detect conversion points larger than v0cuts.min_r_tpconly. + return; + } + + // Apply a topological constraint of the gamma to the PV. Parameters will be given at the primary vertex. + KFParticle gammaKF_PV = gammaKF; + gammaKF_PV.SetProductionVertex(KFPV); + float v0pt = RecoDecay::sqrtSumOfSquares(gammaKF_PV.GetPx(), gammaKF_PV.GetPy()); + float v0eta = RecoDecay::eta(std::array{gammaKF_PV.GetPx(), gammaKF_PV.GetPy(), gammaKF_PV.GetPz()}); + float v0phi = RecoDecay::constrainAngle(RecoDecay::phi(gammaKF_PV.GetPx(), gammaKF_PV.GetPy())); + + // KFParticle gammaKF_DecayVtx2 = gammaKF; + // gammaKF_DecayVtx2.SetProductionVertex(KFPV); + // gammaKF_DecayVtx2.TransportToPoint(xyz); + // LOGF(info, "gammaKF_PV.GetPx() = %f, gammaKF_DecayVtx.GetPx() = %f, gammaKF_DecayVtx2.GetPx() = %f", gammaKF_PV.GetPx(), gammaKF_DecayVtx.GetPx(), gammaKF_DecayVtx2.GetPx()); + // LOGF(info, "gammaKF_PV.GetPy() = %f, gammaKF_DecayVtx.GetPy() = %f, gammaKF_DecayVtx2.GetPy() = %f", gammaKF_PV.GetPy(), gammaKF_DecayVtx.GetPy(), gammaKF_DecayVtx2.GetPy()); + // LOGF(info, "gammaKF_PV.GetPz() = %f, gammaKF_DecayVtx.GetPz() = %f, gammaKF_DecayVtx2.GetPz() = %f", gammaKF_PV.GetPz(), gammaKF_DecayVtx.GetPz(), gammaKF_DecayVtx2.GetPz()); + + if (std::fabs(v0eta) > v0cuts.max_eta_v0 || v0pt < v0cuts.min_pt_v0) { + return; + } + + if (isITSonlyTrack(ele) && isITSonlyTrack(pos) && v0pt > v0cuts.max_pt_v0_itsonly) { + return; + } + + KFParticle kfp_pos_DecayVtx = kfp_pos; // Don't set Primary Vertex + KFParticle kfp_ele_DecayVtx = kfp_ele; // Don't set Primary Vertex + kfp_pos_DecayVtx.TransportToPoint(xyz.data()); // Don't set Primary Vertex + kfp_ele_DecayVtx.TransportToPoint(xyz.data()); // Don't set Primary Vertex + + float cospaXYKF = cospaXY_KF(gammaKF_DecayVtx, KFPV); + float cospaRZKF = cospaRZ_KF(gammaKF_DecayVtx, KFPV); + auto centType = static_cast(mlcuts.centTypePCMMl.value); + v0photoncandidate.setPhotonCandidate(gammaKF_DecayVtx, gammaKF_PV, pos, kfp_pos_DecayVtx, ele, kfp_ele_DecayVtx, collision, cospaXYKF, cospaRZKF, cospaXYKF, psipair, phiv, centType, posdcaXY, eledcaXY, posdcaZ, eledcaZ); + + if (!ele.hasITS() && !pos.hasITS()) { // V0s with TPConly-TPConly + if (v0cuts.max_r_itsmft_ss < rxy && rxy < trackcuts.maxX + v0cuts.margin_r_tpc) { + if (v0photoncandidate.getPCA() > v0cuts.max_dcav0dau_tpc_inner_fc) { + return; + } + } else { + if (v0photoncandidate.getPCA() > v0cuts.max_dcav0dau_tpconly) { + return; + } + } + } else { // V0s with ITS hits + if (rxy < v0cuts.max_r_req_its) { + if (v0photoncandidate.getPCA() > v0cuts.max_dcav0dau_itsibss) { + return; + } + } else { + if (v0photoncandidate.getPCA() > v0cuts.max_dcav0dau_its) { + return; + } + } + } + + if (isITSonlyTrack(pos) && v0photoncandidate.getPosPt() > trackcuts.maxpt_itsonly) { + return; + } + + if (isITSonlyTrack(ele) && v0photoncandidate.getElePt() > trackcuts.maxpt_itsonly) { + return; + } + + if (v0photoncandidate.getChi2NDF() > 6e+3) { // protection for uint16. // o2-linter: disable=magic-number (protection for uint16) + return; + } + + if (std::fabs(v0photoncandidate.getDcaXYToPV()) > v0cuts.max_dcatopv_xy_v0 || std::fabs(v0photoncandidate.getDcaZToPV()) > v0cuts.max_dcatopv_z_v0) { + return; + } + + if (!checkAP(v0photoncandidate.getAlpha(), v0photoncandidate.getQt(), v0cuts.max_alpha_ap, v0cuts.max_qt_ap)) { // store only photon conversions + return; + } + pca_map[std::make_tuple(v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex())] = v0photoncandidate.getPCA(); + cospa_map[std::make_tuple(v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex())] = v0photoncandidate.getCosPA(); + + ROOT::Math::PxPyPzMVector vposDedup(kfp_pos_DecayVtx.GetPx(), kfp_pos_DecayVtx.GetPy(), kfp_pos_DecayVtx.GetPz(), o2::constants::physics::MassElectron); + ROOT::Math::PxPyPzMVector veleDedup(kfp_ele_DecayVtx.GetPx(), kfp_ele_DecayVtx.GetPy(), kfp_ele_DecayVtx.GetPz(), o2::constants::physics::MassElectron); + const auto meeSV = static_cast((vposDedup + veleDedup).M()); + + const auto score = getScoreV0(v0photoncandidate.getCosPA(), v0photoncandidate.getPCA(), dedup.deduplicationScoreWeight); + V0CandidateHelper v0Helper(v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex(), + v0photoncandidate.getCosPA(), v0photoncandidate.getPCA(), score, meeSV); + v0Helper.pxAtPV = gammaKF_PV.GetPx(); + v0Helper.pyAtPV = gammaKF_PV.GetPy(); + v0Helper.pzAtPV = gammaKF_PV.GetPz(); + vecV0Dedup.emplace_back(v0Helper); + + if (mlcuts.applyPCMMl) { + bool isSelectedML = false; + std::vector mlInputFeatures = emMlResponse.getInputFeatures(v0photoncandidate, pos, ele); + if (mlcuts.use2DBinning) { + isSelectedML = emMlResponse.isSelectedMl(mlInputFeatures, v0photoncandidate.getPt(), v0photoncandidate.getCent(), outputML); + } else { + isSelectedML = emMlResponse.isSelectedMl(mlInputFeatures, v0photoncandidate.getPt(), outputML); + } + if (filltable) { + if (mlcuts.nClassesPCMMl == o2::analysis::NClassesML::kTwo) { + registry.fill(HIST("V0/hBDTBackgroundScoreBeforeCutVsPt"), v0photoncandidate.getPt(), outputML[0]); + registry.fill(HIST("V0/hBDTSignalScoreBeforeCutVsPt"), v0photoncandidate.getPt(), outputML[1]); + } else if (mlcuts.nClassesPCMMl == o2::analysis::NClassesML::kThree) { + registry.fill(HIST("V0/hBDTPrimaryPhotonScoreBeforeCutVsPt"), v0photoncandidate.getPt(), outputML[0]); + registry.fill(HIST("V0/hBDTSecondaryPhotonScoreBeforeCutVsPt"), v0photoncandidate.getPt(), outputML[1]); + registry.fill(HIST("V0/hBDTBackgroundScoreBeforeCutVsPt"), v0photoncandidate.getPt(), outputML[2]); + } else { + registry.fill(HIST("V0/hBDTScoreBeforeCutVsPt"), v0photoncandidate.getPt(), outputML[0]); + } + } + if (!isSelectedML) { + return; + } + if (filltable) { + if (mlcuts.nClassesPCMMl == o2::analysis::NClassesML::kTwo) { + registry.fill(HIST("V0/hBDTBackgroundScoreAfterCutVsPt"), v0photoncandidate.getPt(), outputML[0]); + registry.fill(HIST("V0/hBDTSignalScoreAfterCutVsPt"), v0photoncandidate.getPt(), outputML[1]); + } else if (mlcuts.nClassesPCMMl == o2::analysis::NClassesML::kThree) { + registry.fill(HIST("V0/hBDTPrimaryPhotonScoreAfterCutVsPt"), v0photoncandidate.getPt(), outputML[0]); + registry.fill(HIST("V0/hBDTSecondaryPhotonScoreAfterCutVsPt"), v0photoncandidate.getPt(), outputML[1]); + registry.fill(HIST("V0/hBDTBackgroundScoreAfterCutVsPt"), v0photoncandidate.getPt(), outputML[2]); + } else { + registry.fill(HIST("V0/hBDTScoreAfterCutVsPt"), v0photoncandidate.getPt(), outputML[0]); + } + } + } + + if (filltable) { + registry.fill(HIST("V0/hAP"), v0photoncandidate.getAlpha(), v0photoncandidate.getQt()); + registry.fill(HIST("V0/hConversionPointXY"), gammaKF_DecayVtx.GetX(), gammaKF_DecayVtx.GetY()); + registry.fill(HIST("V0/hConversionPointRZ"), gammaKF_DecayVtx.GetZ(), rxy); + registry.fill(HIST("V0/hPt"), v0photoncandidate.getPt()); + registry.fill(HIST("V0/hEtaPhi"), v0phi, v0eta); + registry.fill(HIST("V0/hCosPA"), v0photoncandidate.getCosPA()); + registry.fill(HIST("V0/hCosPA_Rxy"), rxy, v0photoncandidate.getCosPA()); + registry.fill(HIST("V0/hPCA"), v0photoncandidate.getPCA()); + registry.fill(HIST("V0/hPCA_CosPA"), v0photoncandidate.getCosPA(), v0photoncandidate.getPCA()); + registry.fill(HIST("V0/hPCA_Rxy"), rxy, v0photoncandidate.getPCA()); + registry.fill(HIST("V0/hDCAxyz"), v0photoncandidate.getDcaXYToPV(), v0photoncandidate.getDcaZToPV()); + registry.fill(HIST("V0/hPCA_diffX"), v0photoncandidate.getPCA(), std::min(pTrack.getX(), nTrack.getX()) - rxy); // trackiu.x() - rxy should be positive + registry.fill(HIST("V0/hPhiVPsiPair"), v0photoncandidate.getPsiPair(), v0photoncandidate.getPhiV()); + + // LOGF(info, "cospa_kf = %f, cospaXY_kf = %f, cospaRZ_kf = %f", cospa_kf, cospaXY_kf, cospaRZ_kf); + registry.fill(HIST("V0/hCosPAXY_Rxy"), rxy, cospaXYKF); + registry.fill(HIST("V0/hCosPARZ_Rxy"), rxy, cospaRZKF); + + for (const auto& leg : {kfp_pos_DecayVtx, kfp_ele_DecayVtx}) { + float legpt = RecoDecay::sqrtSumOfSquares(leg.GetPx(), leg.GetPy()); + float legeta = RecoDecay::eta(std::array{leg.GetPx(), leg.GetPy(), leg.GetPz()}); + float legphi = RecoDecay::constrainAngle(RecoDecay::phi(leg.GetPx(), leg.GetPy())); + registry.fill(HIST("V0Leg/hPt"), legpt); + registry.fill(HIST("V0Leg/hEtaPhi"), legphi, legeta); + } // end of leg loop + for (const auto& leg : {pos, ele}) { + registry.fill(HIST("V0Leg/hdEdx_Pin"), leg.tpcInnerParam(), leg.tpcSignal()); + registry.fill(HIST("V0Leg/hTPCNsigmaEl"), leg.tpcInnerParam(), leg.tpcNSigmaEl()); + } // end of leg loop + for (const auto& leg : {pTrack, nTrack}) { + registry.fill(HIST("V0Leg/hXZ"), leg.getZ(), leg.getX()); + registry.fill(HIST("V0Leg/hRelDeltaPt"), leg.getPt(), leg.getPt() * std::sqrt(leg.getSigma1Pt2())); + } // end of leg loop + registry.fill(HIST("V0Leg/hDCAxyz"), posdcaXY, posdcaZ); + registry.fill(HIST("V0Leg/hDCAxyz"), eledcaXY, eledcaZ); + + ROOT::Math::PxPyPzMVector vpos_sv(kfp_pos_DecayVtx.GetPx(), kfp_pos_DecayVtx.GetPy(), kfp_pos_DecayVtx.GetPz(), o2::constants::physics::MassElectron); + ROOT::Math::PxPyPzMVector vele_sv(kfp_ele_DecayVtx.GetPx(), kfp_ele_DecayVtx.GetPy(), kfp_ele_DecayVtx.GetPz(), o2::constants::physics::MassElectron); + ROOT::Math::PxPyPzMVector v0_sv = vpos_sv + vele_sv; + registry.fill(HIST("V0/hMeeSV_Rxy"), rxy, v0_sv.M()); + + v0photonskf(collision.globalIndex(), v0.globalIndex(), v0legs.lastIndex() + 1, v0legs.lastIndex() + 2, + gammaKF_DecayVtx.GetX(), gammaKF_DecayVtx.GetY(), gammaKF_DecayVtx.GetZ(), + gammaKF_PV.GetPx(), gammaKF_PV.GetPy(), gammaKF_PV.GetPz(), + v0_sv.M(), v0photoncandidate.getDcaXYToPV(), v0photoncandidate.getDcaZToPV(), + cospa_kf, cospaXYKF, cospaRZKF, + v0photoncandidate.getPCA(), v0photoncandidate.getAlpha(), v0photoncandidate.getQt(), v0photoncandidate.getChi2NDF()); + v0photonsphivpsi(v0photoncandidate.getPhiV(), v0photoncandidate.getPsiPair()); + + // v0photonskfcov(gammaKF_PV.GetCovariance(9), gammaKF_PV.GetCovariance(14), gammaKF_PV.GetCovariance(20), gammaKF_PV.GetCovariance(13), gammaKF_PV.GetCovariance(19), gammaKF_PV.GetCovariance(18)); + + fillTrackTable(pos, kfp_pos_DecayVtx, posdcaXY, posdcaZ); // positive leg first + fillTrackTable(ele, kfp_ele_DecayVtx, eledcaXY, eledcaZ); // negative leg second + } // end of fill table + } + + Preslice perCollision = o2::aod::v0::collisionId; + std::map, float> pca_map; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) -> pca + std::map, float> cospa_map; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) -> cospa + std::vector vecV0Dedup; // vector with all V0Candidates that is used to sort them by score (see struct V0CandidateHelper for more details of content) + std::vector> stored_v0Ids; // (pos.globalIndex(), ele.globalIndex()) + std::vector> stored_fullv0Ids; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) + std::unordered_map nv0_map; // map collisionId -> nv0 + + template + void build(TCollisions const& collisions, TV0s const& v0s, TTracks const& tracks, TBCs const&, DedupDiag* diag = nullptr) + { + for (const auto& collision : collisions) { + if constexpr (isMC) { + if (!collision.has_mcCollision()) { + continue; + } + } + + if (!collision.isSelected()) { + continue; + } + + // if constexpr (isTriggerAnalysis) { + // if (collision.triggerMask_raw() == 0) { + // continue; + // } + // } + + nv0_map[collision.globalIndex()] = 0; + + const auto& bc = collision.template foundBC_as(); + initCCDB(bc); + registry.fill(HIST("hCollisionCounter"), 1); + + updateCCDB(bc); // delay update until is needed + + vecV0Dedup.reserve(30000); // rough estimate for number of V0s per DF + const auto& v0s_per_coll = v0s.sliceBy(perCollision, collision.globalIndex()); + // LOGF(info, "n v0 = %d", v0s_per_coll.size()); + for (const auto& v0 : v0s_per_coll) { + // LOGF(info, "collision.globalIndex() = %d, v0.globalIndex() = %d, v0.posTrackId() = %d, v0.negTrackId() = %d", collision.globalIndex(), v0.globalIndex(), v0.posTrackId() , v0.negTrackId()); + fillV0Table(v0, false); + } // end of v0 loop + } // end of collision loop + + stored_v0Ids.reserve(pca_map.size()); // number of photon candidates per DF + stored_fullv0Ids.reserve(pca_map.size()); // number of photon candidates per DF + + // find minimal pca + if (dedup.deduplicationMode == V0DeduplicationMode::Pairwise) { + for (const auto& [key, value] : pca_map) { + auto v0Id = std::get<0>(key); + auto collisionId = std::get<1>(key); + auto posId = std::get<2>(key); + auto eleId = std::get<3>(key); + float v0pca = value; + float cospa = cospa_map[key]; + bool is_closest_v0 = true; + bool is_most_aligned_v0 = true; + + for (const auto& [key_tmp, value_tmp] : pca_map) { + auto v0Id_tmp = std::get<0>(key_tmp); + auto collisionId_tmp = std::get<1>(key_tmp); + auto posId_tmp = std::get<2>(key_tmp); + auto eleId_tmp = std::get<3>(key_tmp); + float v0pca_tmp = value_tmp; + float cospa_tmp = cospa_map[key_tmp]; + + if (v0Id == v0Id_tmp) { // skip exactly the same v0 + continue; + } + + if (collisionId != collisionId_tmp && eleId == eleId_tmp && posId == posId_tmp && cospa < cospa_tmp) { + if (diag != nullptr) { + diag->blockersByKey[key].push_back(key_tmp); + } + is_most_aligned_v0 = false; + break; + } + if ((eleId == eleId_tmp || posId == posId_tmp) && v0pca > v0pca_tmp) { + if (diag != nullptr) { + diag->blockersByKey[key].push_back(key_tmp); + } + is_closest_v0 = false; + break; + } + } // end of pca_map tmp loop + + bool is_stored = std::find(stored_v0Ids.begin(), stored_v0Ids.end(), std::make_pair(posId, eleId)) != stored_v0Ids.end(); + if (is_closest_v0 && is_most_aligned_v0 && !is_stored) { + // auto v0 = v0s.rawIteratorAt(v0Id); + // auto collision = collisions.rawIteratorAt(collisionId); + // auto pos = tracks.rawIteratorAt(posId); + // auto ele = tracks.rawIteratorAt(eleId); + // LOGF(info, "!accept! | collision id = %d | v0id1 = %d , posid1 = %d , eleid1 = %d , pca1 = %f , cospa = %f", collisionId, v0Id, posId, eleId, v0pca, cospa); + + // fillV0Table(v0, true); + stored_v0Ids.emplace_back(std::make_pair(posId, eleId)); + stored_fullv0Ids.emplace_back(std::make_tuple(v0Id, collisionId, posId, eleId)); + nv0_map[collisionId]++; + } + } // end of pca_map loop + // LOGF(info, "pca_map.size() = %d", pca_map.size()); + + } else if (dedup.deduplicationMode == V0DeduplicationMode::KeepAll) { + // Keep-all: Every candidate that survived the quality cuts is stored, including all duplicates + stored_v0Ids.clear(); + stored_fullv0Ids.clear(); + nv0_map.clear(); + for (const auto& v0Cand : vecV0Dedup) { + stored_v0Ids.emplace_back(v0Cand.posID, v0Cand.eleID); + stored_fullv0Ids.emplace_back(v0Cand.v0ID, v0Cand.colID, v0Cand.posID, v0Cand.eleID); + nv0_map[v0Cand.colID]++; + } + std::sort(stored_fullv0Ids.begin(), stored_fullv0Ids.end(), + [](const auto& a, const auto& b) { + return std::get<1>(a) < std::get<1>(b); + }); + } else if (dedup.deduplicationMode == V0DeduplicationMode::GroupMatching) { + const int maxEnum = std::min(dedup.dedupMaxGroupSize.value, 16); // o2-linter: disable=magic-number (subset check of photons) + const int nCand = static_cast(vecV0Dedup.size()); + stored_v0Ids.clear(); + stored_fullv0Ids.clear(); + nv0_map.clear(); + int nGreedyFallback = 0; + + // ---- conflict groups: candidates connected through a shared leg ----- + std::vector parent(nCand); + for (int i = 0; i < nCand; ++i) { + parent[i] = i; + } + auto findRoot = [&parent](int x) { + while (parent[x] != x) { + parent[x] = parent[parent[x]]; // path halving + x = parent[x]; + } + return x; + }; + std::unordered_map firstCandOfTrack; + for (int i = 0; i < nCand; ++i) { + for (const int& trackId : {vecV0Dedup[i].posID, vecV0Dedup[i].eleID}) { + auto [it, inserted] = firstCandOfTrack.try_emplace(trackId, i); + if (!inserted) { + parent[findRoot(i)] = findRoot(it->second); + } + } + } + std::map> groups; + for (int i = 0; i < nCand; ++i) { + groups[findRoot(i)].push_back(i); + } + + // ---- decide each group on its own ---------------------------------- + for (auto& [root, members] : groups) { // o2-linter: disable=const-ref-in-for-loop (members is sorted in place below) + const int nMembers = static_cast(members.size()); + std::vector selected(nMembers, 0); + + // Fix the order inside the group: best score first + std::sort(members.begin(), members.end(), [this](int a, int b) { + if (vecV0Dedup[a].score != vecV0Dedup[b].score) { + return vecV0Dedup[a].score < vecV0Dedup[b].score; + } + return vecV0Dedup[a].v0ID < vecV0Dedup[b].v0ID; + }); + + if (nMembers == 1) { + selected[0] = 1; + } else if (nMembers <= maxEnum) { + + std::unordered_map legBit; + std::vector legMask(nMembers, 0); + for (int k = 0; k < nMembers; ++k) { + for (const int& trackId : {vecV0Dedup[members[k]].posID, vecV0Dedup[members[k]].eleID}) { + const int bit = legBit.try_emplace(trackId, static_cast(legBit.size())).first->second; + legMask[k] |= (1ull << bit); // at most 2 * 16 = 32 legs, fits into 64 bit + } + } + uint32_t bestMask = 0; + int bestCount = 0; + float bestScore = 0.f; + + std::vector bestWithCount(nMembers, -1); + std::vector bestWithScore(nMembers, 0.f); + for (uint32_t mask = 1; mask < (1u << nMembers); ++mask) { + uint64_t usedLegsLocal = 0; + float scoreSum = 0.f; + int count = 0; + bool disjoint = true; + for (int k = 0; k < nMembers; ++k) { + if ((mask & (1u << k)) == 0) { + continue; + } + if ((usedLegsLocal & legMask[k]) != 0) { + disjoint = false; + break; + } + usedLegsLocal |= legMask[k]; + scoreSum += vecV0Dedup[members[k]].score; + ++count; + } + if (!disjoint) { + continue; + } + + if (count > bestCount || (count == bestCount && scoreSum < bestScore)) { + bestCount = count; + bestScore = scoreSum; + bestMask = mask; + } + if (diag != nullptr) { + for (int k = 0; k < nMembers; ++k) { + if ((mask & (1u << k)) == 0) { + continue; + } + if (count > bestWithCount[k] || (count == bestWithCount[k] && scoreSum < bestWithScore[k])) { + bestWithCount[k] = count; + bestWithScore[k] = scoreSum; + } + } + } + } + for (int k = 0; k < nMembers; ++k) { + selected[k] = ((bestMask & (1u << k)) != 0) ? 1 : 0; + if (diag != nullptr && selected[k] == 0 && bestWithCount[k] >= 0) { + const auto& cand = vecV0Dedup[members[k]]; + diag->lossMargin[std::make_tuple(static_cast(cand.v0ID), static_cast(cand.colID), + static_cast(cand.posID), static_cast(cand.eleID))] = + std::make_pair(bestCount - bestWithCount[k], bestWithScore[k] - bestScore); + } + } + } else { + // Too large to enumerate: greedy by score (members is already sorted that way) + ++nGreedyFallback; + std::vector usedTracks; + usedTracks.reserve(2 * nMembers); + for (int k = 0; k < nMembers; ++k) { + const auto& cand = vecV0Dedup[members[k]]; + if (std::find(usedTracks.begin(), usedTracks.end(), cand.posID) != usedTracks.end() || + std::find(usedTracks.begin(), usedTracks.end(), cand.eleID) != usedTracks.end()) { + continue; + } + usedTracks.push_back(cand.posID); + usedTracks.push_back(cand.eleID); + selected[k] = 1; + } + } + + // ---- store the winners, book the blockers of the losers ---------- + for (int k = 0; k < nMembers; ++k) { + const auto& cand = vecV0Dedup[members[k]]; + if (selected[k] != 0) { + stored_v0Ids.emplace_back(cand.posID, cand.eleID); + stored_fullv0Ids.emplace_back(cand.v0ID, cand.colID, cand.posID, cand.eleID); + nv0_map[cand.colID]++; + continue; + } + if (diag == nullptr) { + continue; + } + // all winners that took one leg + const CandKey victimKey = std::make_tuple(static_cast(cand.v0ID), static_cast(cand.colID), + static_cast(cand.posID), static_cast(cand.eleID)); + for (int j = 0; j < nMembers; ++j) { + if (selected[j] == 0) { + continue; + } + const auto& winner = vecV0Dedup[members[j]]; + if (winner.posID == cand.posID || winner.eleID == cand.eleID) { + diag->blockersByKey[victimKey].emplace_back(static_cast(winner.v0ID), static_cast(winner.colID), + static_cast(winner.posID), static_cast(winner.eleID)); + } + } + } + } + + if (nGreedyFallback > 0) { + LOGF(warning, "group matching: %d conflict group(s) larger than dedupMaxGroupSize = %d were solved greedily", nGreedyFallback, maxEnum); + } + + // the table expects candidates ordered by collision + std::sort(stored_fullv0Ids.begin(), stored_fullv0Ids.end(), + [](const auto& a, const auto& b) { + return std::get<1>(a) < std::get<1>(b); + }); + + } else { + // Sort best candidates first, depending on score + std::sort(vecV0Dedup.begin(), vecV0Dedup.end()); + // container to keep track of which electron and positron tracks have already been used + std::vector usedLegs(tracks.size(), false); + std::unordered_map ownerOfLeg; // diagnostics only + stored_v0Ids.clear(); + stored_fullv0Ids.clear(); + nv0_map.clear(); + for (const auto& v0Cand : vecV0Dedup) { + const CandKey candKey = std::make_tuple(static_cast(v0Cand.v0ID), static_cast(v0Cand.colID), + static_cast(v0Cand.posID), static_cast(v0Cand.eleID)); + if (usedLegs[v0Cand.posID] || usedLegs[v0Cand.eleID]) { + if (diag != nullptr) { + // both legs, not just the first one found: a cross-leg fake is usually blocked by + // two different winners, and that is exactly the case being measured here + auto& blockers = diag->blockersByKey[candKey]; + for (const int& legId : {v0Cand.posID, v0Cand.eleID}) { + const auto itOwner = ownerOfLeg.find(legId); + if (itOwner == ownerOfLeg.end()) { + continue; + } + if (std::find(blockers.begin(), blockers.end(), itOwner->second) == blockers.end()) { + blockers.push_back(itOwner->second); // the same winner may hold both legs + } + } + } + continue; + } + usedLegs[v0Cand.posID] = true; + usedLegs[v0Cand.eleID] = true; + if (diag != nullptr) { + ownerOfLeg[v0Cand.posID] = candKey; + ownerOfLeg[v0Cand.eleID] = candKey; + } + stored_v0Ids.emplace_back(v0Cand.posID, v0Cand.eleID); + stored_fullv0Ids.emplace_back(v0Cand.v0ID, v0Cand.colID, v0Cand.posID, v0Cand.eleID); + nv0_map[v0Cand.colID]++; + } + + // sort accepted candidates by collision ID to be compatible with table + std::sort(stored_fullv0Ids.begin(), stored_fullv0Ids.end(), + [](const auto& a, const auto& b) { + return std::get<1>(a) < std::get<1>(b); + }); + + // End of deduplication + } + + if (diag != nullptr) { + diag->snapshot = vecV0Dedup; + diag->stored = stored_fullv0Ids; + } + + std::set skimRejected; + if (skim.minNv0PerCollision > 0 || skim.maxMinvPairPerCollision > 0.f) { + const std::set storedSetSkim(stored_fullv0Ids.begin(), stored_fullv0Ids.end()); + std::unordered_map> candsPerColl; + for (const auto& c : vecV0Dedup) { + if (storedSetSkim.contains(std::make_tuple(static_cast(c.v0ID), static_cast(c.colID), + static_cast(c.posID), static_cast(c.eleID)))) { + candsPerColl[c.colID].push_back(&c); + } + } + for (const auto& [colId, cands] : candsPerColl) { + if (skim.minNv0PerCollision > 0 && static_cast(cands.size()) < skim.minNv0PerCollision) { + skimRejected.insert(colId); + continue; + } + if (skim.maxMinvPairPerCollision > 0.f) { + bool hasLowMPair = false; + for (size_t i = 0; i < cands.size() && !hasLowMPair; ++i) { + for (size_t j = i + 1; j < cands.size(); ++j) { + ROOT::Math::PxPyPzMVector v1(cands[i]->pxAtPV, cands[i]->pyAtPV, cands[i]->pzAtPV, 0.f); + ROOT::Math::PxPyPzMVector v2(cands[j]->pxAtPV, cands[j]->pyAtPV, cands[j]->pzAtPV, 0.f); + if ((v1 + v2).M() < skim.maxMinvPairPerCollision) { + hasLowMPair = true; + break; + } + } + } + if (!hasLowMPair) { + skimRejected.insert(colId); + } + } + } + } + + for (const auto& fullv0Id : stored_fullv0Ids) { + auto v0Id = std::get<0>(fullv0Id); + const auto collisionId = std::get<1>(fullv0Id); + if (skimRejected.contains(collisionId)) { + continue; // collision failed the skim - do not store its photons/legs + } + // auto posId = std::get<2>(fullv0Id); + // auto eleId = std::get<3>(fullv0Id); + // LOGF(info, "!accept! | collision id = %d | v0id = %d , posid = %d , eleid = %d", collisionId, v0Id, posId, eleId); + + auto v0 = v0s.rawIteratorAt(v0Id); + if constexpr (enableFilter) { + auto collision_tmp = v0.template collision_as(); // collision where this v0 belongs. + if (!(collision_tmp.neeuls() >= 1 || collision_tmp.neeuls() + nv0_map[collision_tmp.globalIndex()] >= 2)) { // o2-linter: disable=magic-number (nonsensical default value) + continue; + } + // LOGF(info, "collision_tmp.globalIndex() = %d, collision_tmp.neeuls() = %d, nv0_map = %d", collision_tmp.globalIndex(), collision_tmp.neeuls(), nv0_map[collision_tmp.globalIndex()]); + } + + fillV0Table(v0, true); + } // end of fullv0Id loop + + // for (const auto& collision : collisions) { + // if constexpr (isMC) { + // if (!collision.has_mcCollision()) { + // continue; + // } + // } + // // events_ngpcm(nv0_map[collision.globalIndex()]); + // } // end of collision loop + + pca_map.clear(); + cospa_map.clear(); + nv0_map.clear(); + stored_v0Ids.clear(); + stored_v0Ids.shrink_to_fit(); + stored_fullv0Ids.clear(); + stored_fullv0Ids.shrink_to_fit(); + vecV0Dedup.clear(); + } // end of build + + o2::framework::expressions::Filter v0Filter = o2::aod::v0::v0Type > (uint8_t)0; + using FilteredV0s = o2::soa::Filtered; + + enum PairFate { + kBefore = 0, + kBothSurvive, + kOneLost, + kBothLost, + kLostByCrossFake, + kLostByPartnerFake, + kLostByOtherFake, + kLostByTrue + }; + + void fillTruthPair(int fate, float q, float dEta) + { + switch (fate) { + case kBefore: + registry.fill(HIST("MCDedup/Pairs/before/hQ"), q); + registry.fill(HIST("MCDedup/Pairs/before/hDEta"), dEta); + break; + case kBothSurvive: + registry.fill(HIST("MCDedup/Pairs/bothSurvive/hQ"), q); + registry.fill(HIST("MCDedup/Pairs/bothSurvive/hDEta"), dEta); + break; + case kOneLost: + registry.fill(HIST("MCDedup/Pairs/oneLost/hQ"), q); + registry.fill(HIST("MCDedup/Pairs/oneLost/hDEta"), dEta); + break; + case kBothLost: + registry.fill(HIST("MCDedup/Pairs/bothLost/hQ"), q); + registry.fill(HIST("MCDedup/Pairs/bothLost/hDEta"), dEta); + break; + case kLostByCrossFake: + registry.fill(HIST("MCDedup/Pairs/lostByCrossFake/hQ"), q); + registry.fill(HIST("MCDedup/Pairs/lostByCrossFake/hDEta"), dEta); + break; + case kLostByPartnerFake: + registry.fill(HIST("MCDedup/Pairs/lostByPartnerFake/hQ"), q); + registry.fill(HIST("MCDedup/Pairs/lostByPartnerFake/hDEta"), dEta); + break; + case kLostByOtherFake: + registry.fill(HIST("MCDedup/Pairs/lostByOtherFake/hQ"), q); + registry.fill(HIST("MCDedup/Pairs/lostByOtherFake/hDEta"), dEta); + break; + case kLostByTrue: + registry.fill(HIST("MCDedup/Pairs/lostByTrue/hQ"), q); + registry.fill(HIST("MCDedup/Pairs/lostByTrue/hDEta"), dEta); + break; + default: + break; + } + } + + void fillTruthPairMap(int fate, float dEta, float dPhi, float q) + { + if (!dedup.cfgDedupTruthMaps) { + return; + } + switch (fate) { + case kBefore: + registry.fill(HIST("MCDedup/Pairs/before/hMap"), dEta, dPhi, q); + break; + case kBothSurvive: + registry.fill(HIST("MCDedup/Pairs/bothSurvive/hMap"), dEta, dPhi, q); + break; + case kBothLost: + registry.fill(HIST("MCDedup/Pairs/bothLost/hMap"), dEta, dPhi, q); + break; + case kLostByPartnerFake: + registry.fill(HIST("MCDedup/Pairs/lostByPartnerFake/hMap"), dEta, dPhi, q); + break; + default: + break; + } + } + + template + void fillDedupTruthDiagnostics(TTracks const& tracks, TMCParticles const& mcparticles, DedupDiag const& diag) + { + const int nCand = static_cast(diag.snapshot.size()); + if (nCand == 0) { + return; + } + const std::set storedSet(diag.stored.begin(), diag.stored.end()); + + std::vector key(nCand); + std::vector cls(nCand, static_cast(kV0OtherFake)); + std::vector motherPos(nCand, -1), motherEle(nCand, -1); + std::map indexOfKey; + for (int i = 0; i < nCand; ++i) { + const auto& c = diag.snapshot[i]; + key[i] = std::make_tuple(static_cast(c.v0ID), static_cast(c.colID), + static_cast(c.posID), static_cast(c.eleID)); + indexOfKey[key[i]] = i; + cls[i] = static_cast(classifyV0Truth(tracks.rawIteratorAt(c.posID), tracks.rawIteratorAt(c.eleID), + mcparticles, motherPos[i], motherEle[i])); + registry.fill(HIST("MCDedup/Candidates/hClassStage"), static_cast(cls[i]), 0.f); + if (storedSet.contains(key[i]) > 0) { + registry.fill(HIST("MCDedup/Candidates/hClassStage"), static_cast(cls[i]), 1.f); + } + } + + struct PhotonFate { + bool alive = false; + int best = -1; // best-score candidate, only used for the margin plots + std::vector cands; // all true candidates of this photon + std::set aliveColls; // collisions with a surviving candidate + }; + std::map fate; + for (int i = 0; i < nCand; ++i) { + if (cls[i] != kV0True) { + continue; + } + auto& f = fate[motherPos[i]]; + f.cands.push_back(i); + if (storedSet.contains(key[i]) > 0) { + f.alive = true; + f.aliveColls.insert(std::get<1>(key[i])); + } + if (f.best < 0 || diag.snapshot[i].score < diag.snapshot[f.best].score) { + f.best = i; + } + } + + std::map> blockersOfPhoton; + for (const auto& [mid, f] : fate) { + if (f.alive) { + registry.fill(HIST("MCDedup/Photons/hFate"), 0.f); + continue; + } + auto& blockers = blockersOfPhoton[mid]; + for (const int& ci : f.cands) { + const auto itBlocker = diag.blockersByKey.find(key[ci]); + if (itBlocker == diag.blockersByKey.end()) { + continue; + } + for (const auto& bKey : itBlocker->second) { + const auto itIndex = indexOfKey.find(bKey); + if (itIndex == indexOfKey.end()) { + continue; + } + if (std::find(blockers.begin(), blockers.end(), itIndex->second) == blockers.end()) { + blockers.push_back(itIndex->second); + } + } + } + // Why was it not in the winning set? Take the most favourable of its candidates: the one + // that could have been kept at the smallest cost in cardinality. + int bestDeficit = -1; + float bestExcess = 0.f; + for (const int& ci : f.cands) { + const auto itMargin = diag.lossMargin.find(key[ci]); + if (itMargin == diag.lossMargin.end()) { + continue; + } + if (bestDeficit < 0 || itMargin->second.first < bestDeficit || + (itMargin->second.first == bestDeficit && itMargin->second.second < bestExcess)) { + bestDeficit = itMargin->second.first; + bestExcess = itMargin->second.second; + } + } + if (bestDeficit >= 0) { + registry.fill(HIST("MCDedup/Photons/hLostMargin"), static_cast(bestDeficit), bestExcess); + } + // hVictimBlockerClass below counts one entry per blocker and a victim can have two. + registry.fill(HIST("MCDedup/Photons/hFate"), blockers.empty() ? 2.f : 1.f); + + if (blockers.empty()) { + registry.fill(HIST("MCDedup/Photons/hBlockerClass"), 3.f); // o2-linter: disable=magic-number (bin 3 = no blocker) + continue; + } + for (const int& w : blockers) { + registry.fill(HIST("MCDedup/Photons/hBlockerClass"), static_cast(cls[w])); + } + // margin and PCA comparison relation the best candidate to its blocker + + if (f.best >= 0 && dedup.deduplicationMode.value < static_cast(V0DeduplicationMode::GroupMatching)) { + const int w = blockers.front(); + registry.fill(HIST("MCDedup/Photons/hKillMargin"), diag.snapshot[f.best].score - diag.snapshot[w].score); + registry.fill(HIST("MCDedup/Photons/hVictimVsWinnerPCA"), diag.snapshot[f.best].pca, diag.snapshot[w].pca); + } + } + + // ---- truth pairs, grouped by MC collision ---------------------------- + std::map> mothersByMcColl; + for (const auto& [mid, f] : fate) { + mothersByMcColl[mcparticles.iteratorAt(mid).mcCollisionId()].push_back(mid); + } + constexpr float MaxQTruth = 0.3f; + for (const auto& [mcCol, mids] : mothersByMcColl) { + for (size_t a = 0; a < mids.size(); ++a) { + for (size_t b = a + 1; b < mids.size(); ++b) { + const auto g1 = mcparticles.iteratorAt(mids[a]); + const auto g2 = mcparticles.iteratorAt(mids[b]); + const float e1 = std::hypot(g1.px(), g1.py(), g1.pz()); + const float e2 = std::hypot(g2.px(), g2.py(), g2.pz()); + const float qTrue = std::sqrt(std::max(0.f, 2.f * (e1 * e2 - g1.px() * g2.px() - g1.py() * g2.py() - g1.pz() * g2.pz()))); + if (qTrue > MaxQTruth) { + continue; + } + const float dEta = g1.eta() - g2.eta(); + const float dPhi = RecoDecay::constrainAngle(static_cast(g1.phi() - g2.phi()), -o2::constants::math::PI); + fillTruthPair(kBefore, qTrue, dEta); + fillTruthPairMap(kBefore, dEta, dPhi, qTrue); + + const auto& f1 = fate.at(mids[a]); + const auto& f2 = fate.at(mids[b]); + const int nLost = (f1.alive ? 0 : 1) + (f2.alive ? 0 : 1); + if (nLost == 0) { + fillTruthPair(kBothSurvive, qTrue, dEta); + fillTruthPairMap(kBothSurvive, dEta, dPhi, qTrue); + bool sameColl = false; + for (const auto& c1 : f1.aliveColls) { + sameColl = sameColl || (f2.aliveColls.count(c1) > 0); + } + if (sameColl) { + registry.fill(HIST("MCDedup/Pairs/bothSurviveSameColl/hQ"), qTrue); + } + continue; + } + if (nLost == 1) { + fillTruthPair(kOneLost, qTrue, dEta); + } else { + fillTruthPair(kBothLost, qTrue, dEta); + fillTruthPairMap(kBothLost, dEta, dPhi, qTrue); + } + + // Reason for photon building failure + bool byCross = false, byPartner = false, byOther = false, byTrue = false; + auto attribute = [&](int64_t victim, int64_t partner) { + if (fate.at(victim).alive) { + return; + } + const auto itB = blockersOfPhoton.find(victim); + if (itB == blockersOfPhoton.end()) { + return; + } + for (const int& w : itB->second) { + if (cls[w] == kV0CrossLegFake) { + byCross = true; + // partner fake = a cross-leg fake using a leg of the other photon + if (motherPos[w] == partner || motherEle[w] == partner) { + byPartner = true; + } + } else if (cls[w] == kV0OtherFake) { + byOther = true; + } else if (cls[w] == kV0True) { + byTrue = true; // real photon against real photon + } + } + }; + attribute(mids[a], mids[b]); + attribute(mids[b], mids[a]); + if (byCross) { + fillTruthPair(kLostByCrossFake, qTrue, dEta); + } + if (byPartner) { + fillTruthPair(kLostByPartnerFake, qTrue, dEta); + fillTruthPairMap(kLostByPartnerFake, dEta, dPhi, qTrue); + } + if (byOther) { + fillTruthPair(kLostByOtherFake, qTrue, dEta); + } + if (byTrue) { + fillTruthPair(kLostByTrue, qTrue, dEta); + } + } + } + } + } + + void processRec(MyCollisions const& collisions, FilteredV0s const& v0s, MyTracksIU const& tracks, MyBCs const& bcs) + { + build(collisions, v0s, tracks, bcs); + } + PROCESS_SWITCH(PhotonConversionBuilder, processRec, "process reconstructed info for data", true); + + // void processRec_SWT(MyCollisionsWithSWT const& collisions, FilteredV0s const& v0s, MyTracksIU const& tracks, MyBCs const& bcs) + // { + // build(collisions, v0s, tracks, bcs); + // } + // PROCESS_SWITCH(PhotonConversionBuilder, processRec_SWT, "process reconstructed info for data", false); + + void processMC(MyCollisionsMC const& collisions, FilteredV0s const& v0s, MyTracksIUMC const& tracks, + MyBCs const& bcs, o2::aod::McParticles const& mcparticles) + { + DedupDiag diag; + build(collisions, v0s, tracks, bcs, &diag); + fillDedupTruthDiagnostics(tracks, mcparticles, diag); + } + PROCESS_SWITCH(PhotonConversionBuilder, processMC, "process reconstructed info for MC", false); + + void processRec_OnlyIfDielectron(o2::soa::Join const& collisions, FilteredV0s const& v0s, MyTracksIU const& tracks, MyBCs const& bcs) + { + build(collisions, v0s, tracks, bcs); + } + PROCESS_SWITCH(PhotonConversionBuilder, processRec_OnlyIfDielectron, "process reconstructed info for data", false); + + // void processRec_SWT_OnlyIfDielectron(soa::Join const& collisions, FilteredV0s const& v0s, MyTracksIU const& tracks, MyBCs const& bcs) + // { + // build(collisions, v0s, tracks, bcs); + // } + // PROCESS_SWITCH(PhotonConversionBuilder, processRec_SWT_OnlyIfDielectron, "process reconstructed info for data", false); +}; diff --git a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilderTmpTable.cxx b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilderTmpTable.cxx new file mode 100644 index 00000000000..6b7927b6340 --- /dev/null +++ b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilderTmpTable.cxx @@ -0,0 +1,23 @@ +// 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 photonconversionbuilder.ch +/// \brief this task produces photon data table with KFParticle. +/// \author Daiki Sekihata , Tokyo + +#include "PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h" + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + return o2::framework::WorkflowSpec{ + adaptAnalysisTask>(context, o2::framework::TaskName{"photon-conversion-builder-tmptable"})}; +} diff --git a/PWGEM/PhotonMeson/TableProducer/skimmerDalitzEvents.cxx b/PWGEM/PhotonMeson/TableProducer/skimmerDalitzEvents.cxx new file mode 100644 index 00000000000..79414492f42 --- /dev/null +++ b/PWGEM/PhotonMeson/TableProducer/skimmerDalitzEvents.cxx @@ -0,0 +1,147 @@ +// Copyright 2019-2020 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 skimmerDalitzEvents.cxx +/// \brief write tables for photons and electrons for dalitz decay +/// \author josuha.konig@cern.ch + +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; +using namespace o2::common::core; + +using MyCollisions = soa::Join; +using MyElectrons = o2::aod::EMPrimaryElectronsFromDalitzTmp; +using MyV0Photons = o2::soa::Join; +using MyV0PhotonsLegs = o2::soa::Join; + +using MyElectronsMC = o2::soa::Join; +using MyV0PhotonsLegsMC = o2::soa::Join; + +struct skimmerDalitzEvents { + + Produces v0photonskf; + Produces v0legs; + Produces v0legsXYZ; + Produces v0legsDeDxMC; + Produces v0photonsphivpsi; + + Produces emprimaryelectrons; + Produces emprimaryelectronsDeDxMC; + + Configurable minNElecCand{"minNElecCand", 2, "minimum number of electrons/positron candidates in one event"}; + Configurable minNGammaCand{"minNGammaCand", 1, "minimum number of V0 photon candidates in one event"}; + + // ---------- for data ---------- + template + void process(MyCollisions const& collisions, TElectrons const& emPrimaryElecTmp, TV0Photons const& v0photonskfTmp, TV0Legs const& v0LegsTmp) + { + PresliceUnsorted perCollisionEl = aod::emprimaryelectron::collisionId; + Preslice perCol_pcm = o2::aod::v0photonkf::collisionId; + Preslice perCol_legs = o2::aod::v0leg::collisionId; + for (const auto& collision : collisions) { + auto tracks = emPrimaryElecTmp.sliceBy(perCollisionEl, collision.globalIndex()); // o2::aod::track::collisionId // o2::aod::emprimaryelectronda::pmeventId + + if (tracks.size() < minNElecCand) { + continue; + } + + auto photonskf = v0photonskfTmp.sliceBy(perCol_pcm, collision.globalIndex()); + + if (photonskf.size() < minNGammaCand) { + continue; + } + + // Write electron tracks + for (const auto& track : tracks) { + emprimaryelectrons(track.collisionId(), track.trackId(), track.sign(), + track.pt(), track.eta(), track.phi(), + track.dcaXY(), track.dcaZ(), track.cYY(), track.cZY(), track.cZZ(), + track.tpcNClsFindable(), track.tpcNClsFindableMinusFound(), track.tpcNClsFindableMinusCrossedRows(), track.tpcNClsShared(), + track.tpcChi2NCl(), track.tpcInnerParam(), + track.tpcSignal(), track.tpcNSigmaEl(), track.tpcNSigmaPi(), + track.beta(), track.tofNSigmaEl(), + track.itsClusterSizes(), track.itsChi2NCl(), track.tofChi2(), track.detectorMap()); + + if constexpr (isMC) { + emprimaryelectronsDeDxMC( + track.mcTunedTPCSignal()); + } + } + + for (const auto& v0 : photonskf) { + v0photonskf(v0.collisionId(), v0.v0Id(), v0.posTrack(), v0.negTrack(), + v0.vx(), v0.vy(), v0.vz(), + v0.px(), v0.py(), v0.pz(), + v0.mGamma(), + v0.dcaXYtopv(), v0.dcaZtopv(), + v0.cospa(), v0.cospaXY(), v0.cospaRZ(), v0.pca(), + v0.alpha(), v0.qtarm(), + v0.chiSquareNDF()); + + v0photonsphivpsi( + v0.phiv(), v0.psipair()); + } + + auto v0LegsTmpPerColl = v0LegsTmp.sliceBy(perCol_legs, collision.globalIndex()); + for (const auto& leg : v0LegsTmp) { + v0legs( + leg.collisionId(), leg.trackId(), leg.sign(), + leg.px(), leg.py(), leg.pz(), + leg.dcaXY(), leg.dcaZ(), + leg.tpcNClsFindable(), leg.tpcNClsFindableMinusFound(), leg.tpcNClsFindableMinusCrossedRows(), leg.tpcNClsShared(), + leg.tpcChi2NCl(), leg.tpcInnerParam(), + leg.tpcSignal(), leg.tpcNSigmaEl(), leg.tpcNSigmaPi(), + leg.itsClusterSizes(), leg.itsChi2NCl(), leg.detectorMap()); + + v0legsXYZ( + leg.x(), leg.y(), leg.z()); + + if constexpr (isMC) { + v0legsDeDxMC( + leg.mcTunedTPCSignal()); + } + } + } + } + + void processRec(MyCollisions const& collisions, MyElectrons const& emPrimaryElecTmp, MyV0Photons const& v0photonskfTmp, MyV0PhotonsLegs const& v0LegsTmp) + { + process(collisions, emPrimaryElecTmp, v0photonskfTmp, v0LegsTmp); + } + + void processMC(MyCollisions const& collisions, MyElectronsMC const& emPrimaryElecTmp, MyV0Photons const& v0photonskfTmp, MyV0PhotonsLegsMC const& v0LegsTmp) + { + process(collisions, emPrimaryElecTmp, v0photonskfTmp, v0LegsTmp); + } + + PROCESS_SWITCH(skimmerDalitzEvents, processRec, "process reconstructed info only", false); // data + PROCESS_SWITCH(skimmerDalitzEvents, processMC, "process reconstructed and MC info", false); // MC +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{ + adaptAnalysisTask(context, TaskName{"skimmer-dalitz-events"})}; +} diff --git a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.cxx b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.cxx index fd55f41b1a0..97fe86ece34 100644 --- a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.cxx +++ b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.cxx @@ -10,1005 +10,19 @@ // or submit itself to any jurisdiction. /// \file skimmerPrimaryElectronFromDalitzEE.cxx -/// \brief write relevant information about primary electrons. -/// \author daiki.sekihata@cern.ch +/// \brief write electrons for Dalitz into new table +/// \author lgansbartl@stud.uni-frankfurt.de -#include "PWGEM/Dilepton/Utils/PairUtilities.h" -#include "PWGEM/PhotonMeson/DataModel/EventTables.h" -#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" +#include "PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h" -#include "Common/Core/PID/PIDTOFParamService.h" -#include "Common/DataModel/CollisionAssociationTables.h" -#include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/PIDResponseTOF.h" -#include "Common/DataModel/PIDResponseTPC.h" -#include "Common/DataModel/TrackSelectionTables.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace o2; -using namespace o2::soa; using namespace o2::framework; +using namespace o2::aod; using namespace o2::framework::expressions; -using namespace o2::constants::physics; - -using MyCollisions = soa::Join; -using MyCollisionsWithSWT = soa::Join; -using MyBCs = soa::Join; - -using MyCollisionsMC = soa::Join; -using MyTracks = soa::Join; -using MyTrack = MyTracks::iterator; -using MyTracksMC = soa::Join; -using MyTrackMC = MyTracksMC::iterator; - -namespace o2::aod -{ -namespace pwgem::pm::recalculatedtofpid -{ -DECLARE_SOA_COLUMN(BetaRecalculated, betaRecalculated, float); -DECLARE_SOA_COLUMN(TOFNSigmaElRecalculated, tofNSigmaElRecalculated, float); -} // namespace pwgem::pm::recalculatedtofpid - -DECLARE_SOA_TABLE(EMTOFNSigmas, "AOD", "EMTOFNSIGMA", // make std::map in your tasks later. // Don't store this table in the derived data. - o2::aod::emprimaryelectron::CollisionId, o2::aod::emprimaryelectron::TrackId, - o2::aod::pwgem::pm::recalculatedtofpid::BetaRecalculated, o2::aod::pwgem::pm::recalculatedtofpid::TOFNSigmaElRecalculated); - -using EMTOFNSigma = EMTOFNSigmas::iterator; -} // namespace o2::aod - -struct skimmerPrimaryElectronFromDalitzEE { - SliceCache cache; - Preslice perCol = o2::aod::track::collisionId; - PresliceOptional perTracksCollision = aod::track::collisionId; - Preslice perCol_pcm = o2::aod::v0photonkf::collisionId; - Preslice trackIndicesPerCollision = aod::track_association::collisionId; - Produces emprimaryelectrons; - Produces emprimaryelectronsDeDxMC; - Service mTOFResponse{}; - - Produces emtofs; - - enum class enumFillingMode { - SingleTrack = 1, - EpEmPairs = 2, - EpEmPairsAndPhoton = 3 - }; - - // Configurables - Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; - Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable skipGRPOquery{"skipGRPOquery", true, "skip grpo query"}; - - // Operation and minimisation criteria - Configurable dBzInput{"dBzInput", -999, "bz field in kG, -999 is automatic"}; - Configurable min_ncluster_tpc{"min_ncluster_tpc", 0, "min ncluster tpc"}; // o2-linter: disable=name/function-variable (renaming configs would mess up hyperloop) - Configurable mincrossedrows{"mincrossedrows", 70, "min. crossed rows"}; - Configurable min_tpc_cr_findable_ratio{"min_tpc_cr_findable_ratio", 0.8, "min. TPC Ncr/Nf ratio"}; - Configurable max_frac_shared_clusters_tpc{"max_frac_shared_clusters_tpc", 999.f, "max fraction of shared clusters in TPC"}; - Configurable min_ncluster_its{"min_ncluster_its", 4, "min ncluster its"}; - Configurable min_ncluster_itsib{"min_ncluster_itsib", 1, "min ncluster itsib"}; - Configurable minchi2tpc{"minchi2tpc", 0.0, "min. chi2/NclsTPC"}; - Configurable maxchi2tpc{"maxchi2tpc", 5.0, "max. chi2/NclsTPC"}; - Configurable minchi2its{"minchi2its", 0.0, "min. chi2/NclsITS"}; - Configurable maxchi2its{"maxchi2its", 36.0, "max. chi2/NclsITS"}; - Configurable minpt{"minpt", 0.05, "min pt for ITS-TPC track"}; - Configurable maxeta{"maxeta", 2.0, "max eta acceptance"}; - Configurable dca_xy_max{"dca_xy_max", 1, "max DCAxy in cm"}; // o2-linter: disable=name/function-variable (renaming configs would mess up hyperloop) - Configurable dca_z_max{"dca_z_max", 1, "max DCAz in cm"}; // o2-linter: disable=name/function-variable (renaming configs would mess up hyperloop) - Configurable dca_3d_sigma_max{"dca_3d_sigma_max", 2, "max DCA 3D in sigma"}; // o2-linter: disable=name/function-variable (renaming configs would mess up hyperloop) - Configurable minTPCNsigmaEl{"minTPCNsigmaEl", -2.5, "min. TPC n sigma for electron inclusion"}; - Configurable maxTPCNsigmaEl{"maxTPCNsigmaEl", +3.5, "max. TPC n sigma for electron inclusion"}; - Configurable maxTPCNsigmaPi{"maxTPCNsigmaPi", 0.0, "max. TPC n sigma for pion exclusion"}; - Configurable minTPCNsigmaPi{"minTPCNsigmaPi", 0.0, "min. TPC n sigma for pion exclusion"}; - Configurable minTOFNsigmaEl{"minTOFNsigmaEl", -3.5, "min. TOF n sigma for electron inclusion"}; - Configurable maxTOFNsigmaEl{"maxTOFNsigmaEl", +3.5, "max. TOF n sigma for electron inclusion"}; - Configurable minTPCNsigmaKa{"minTPCNsigmaKa", -2.5, "min. TPC n sigma for kaon exclusion"}; - Configurable maxTPCNsigmaKa{"maxTPCNsigmaKa", 2.5, "max. TPC n sigma for kaon exclusion"}; - Configurable minTPCNsigmaPr{"minTPCNsigmaPr", -2.5, "min. TPC n sigma for proton exclusion"}; - Configurable maxTPCNsigmaPr{"maxTPCNsigmaPr", 2.5, "max. TPC n sigma for proton exclusion"}; - Configurable requireTOF{"requireTOF", false, "require TOF hit"}; - Configurable min_pin_for_pion_rejection{"min_pin_for_pion_rejection", 0.0, "pion rejection is applied above this pin"}; // this is used only in TOFreq - Configurable max_pin_for_pion_rejection{"max_pin_for_pion_rejection", 0.5, "pion rejection is applied below this pin"}; - Configurable minMee{"minMee", 0., "min. mee to store dalitz ee pairs"}; - Configurable maxMee{"maxMee", 0.5, "max. mee to store dalitz ee pairs"}; - Configurable minMeegamma{"minMeegamma", 0.3, "min. mee to store eegamma candidates"}; - Configurable maxMeegamma{"maxMeegamma", 0.8, "max. mee to store eegamma candidates"}; - Configurable fillLS{"fillLS", true, "flag to fill LS histograms for QA"}; - Configurable fillingMode{"fillingMode", 1, "Filling mode| 1: fill tracks without pair selection, 2: fill tracks from selected pairs, 3: fill tracks from selected pairs that can be combined with a photon"}; - Configurable fillWithEtaMassCut{"fillWithEtaMassCut", true, "only valid for fillingmode 3; true: filling tabled based on eta candidate selection with minMeegamma < M < maxMeegamma, false: fill identical to fillingmode 2"}; - Configurable includeITSsa{"includeITSsa", false, "Flag to include ITSsa tracks"}; - Configurable maxpt_itssa{"maxpt_itssa", 0.15, "max pt for ITSsa track"}; // o2-linter: disable=name/function-variable (renaming configs would mess up hyperloop) - Configurable maxMeanITSClusterSize{"maxMeanITSClusterSize", 16, "max x cos(lambda)"}; - Configurable slope{"slope", 0.0185, "slope for m vs. phiv"}; - Configurable intercept{"intercept", -0.0380, "intercept for m vs. phiv"}; - Configurable useTOFNSigmaDeltaBC{"useTOFNSigmaDeltaBC", false, "Flag to shift delta BC for TOF n sigma (only with TTCA)"}; - Configurable storeOnlyTrueElectronMC{"storeOnlyTrueElectronMC", false, "Flag to store only true electron in MC"}; - - HistogramRegistry fRegistry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; - static constexpr std::array DileptonSigns = {"uls/", "lspp/", "lsmm/"}; - - int mRunNumber = 0; - float dBz = 0.; - Service ccdb{}; - o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; - o2::dataformats::VertexBase mVtx; - - void init(InitContext& initContext) - { - // if (doprocessRec && doprocessRec_SWT) { - // LOGF(fatal, "Cannot enable doprocessRec and doprocessRec_SWT at the same time. Please choose one."); - // } - - mRunNumber = 0; - dBz = 0; - - ccdb->setURL(ccdburl); - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); - ccdb->setFatalWhenNull(false); - - LOGF(info, "before TOF initSetup"); - mTOFResponse->initSetup(ccdb, initContext); - LOGF(info, "after TOF initSetup"); - - fRegistry.add("Track/hPt", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); - fRegistry.add("Track/hEtaPhi", "#eta vs. #varphi;#varphi (rad.);#eta", kTH2F, {{180, 0, o2::constants::math::TwoPI}, {400, -2.0f, 2.0f}}, false); - fRegistry.add("Track/hQoverPt", "q/pT;q/p_{T} (GeV/c)^{-1}", kTH1F, {{400, -20, 20}}, false); - fRegistry.add("Track/hRelDeltaPt", "pT resolution;p_{T} (GeV/c);#Deltap_{T}/p_{T}", kTH2F, {{1000, 0, 10}, {100, 0, 0.1}}, false); - fRegistry.add("Track/hDCAxyz", "DCA xy vs. z;DCA_{xy} (cm);DCA_{z} (cm)", kTH2F, {{200, -1.0f, 1.0f}, {200, -1.0f, 1.0f}}, false); - fRegistry.add("Track/hDCAxy_Pt", "DCA_{xy} vs. pT;p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {{200, 0, 10}, {200, -1, 1}}, false); - fRegistry.add("Track/hDCAz_Pt", "DCA_{z} vs. pT;p_{T} (GeV/c);DCA_{z} (cm)", kTH2F, {{200, 0, 10}, {200, -1, 1}}, false); - fRegistry.add("Track/hDCAxyzSigma", "DCA xy vs. z;DCA_{xy} (#sigma);DCA_{z} (#sigma)", kTH2F, {{200, -10.0f, 10.0f}, {200, -10.0f, 10.0f}}, false); - fRegistry.add("Track/hDCAxyRes_Pt", "DCA_{xy} resolution vs. pT;p_{T} (GeV/c);DCA_{xy} resolution (#mum)", kTH2F, {{200, 0, 10}, {500, 0., 500}}, false); - fRegistry.add("Track/hDCAzRes_Pt", "DCA_{z} resolution vs. pT;p_{T} (GeV/c);DCA_{z} resolution (#mum)", kTH2F, {{200, 0, 10}, {500, 0., 500}}, false); - - // TPC - fRegistry.add("Track/hNclsTPC", "number of TPC clusters", kTH1F, {{161, -0.5, 160.5}}, false); - fRegistry.add("Track/hNcrTPC", "number of TPC crossed rows", kTH1F, {{161, -0.5, 160.5}}, false); - fRegistry.add("Track/hChi2TPC", "chi2/number of TPC clusters", kTH1F, {{100, 0, 10}}, false); - fRegistry.add("Track/hTPCNcr2Nf", "TPC Ncr/Nfindable", kTH1F, {{200, 0, 2}}, false); - fRegistry.add("Track/hTPCNcls2Nf", "TPC Ncls/Nfindable", kTH1F, {{200, 0, 2}}, false); - fRegistry.add("Track/hTPCNclsShared", "TPC Ncls shared/Ncls;p_{T} (GeV/c);N_{cls}^{shared}/N_{cls} in TPC", kTH2F, {{1000, 0, 10}, {100, 0, 1}}, false); - fRegistry.add("Track/hTPCdEdx", "TPC dE/dx;p_{in} (GeV/c);TPC dE/dx (a.u.)", kTH2F, {{1000, 0, 10}, {200, 0, 200}}, false); - fRegistry.add("Track/hTPCdEdxMC", "TPC dE/dx;p_{in} (GeV/c);TPC dE/dx (a.u.)", kTH2F, {{1000, 0, 10}, {200, 0, 200}}, false); - fRegistry.add("Track/hTPCNsigmaEl", "TPC n sigma el;p_{in} (GeV/c);n #sigma_{e}^{TPC}", kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); - fRegistry.add("Track/hTPCNsigmaPi", "TPC n sigma pi;p_{in} (GeV/c);n #sigma_{#pi}^{TPC}", kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); - - // ITS - fRegistry.add("Track/hNclsITS", "number of ITS clusters", kTH1F, {{8, -0.5, 7.5}}, false); - fRegistry.add("Track/hChi2ITS", "chi2/number of ITS clusters", kTH1F, {{400, 0, 40}}, false); - fRegistry.add("Track/hITSClusterMap", "ITS cluster map", kTH1F, {{128, -0.5, 127.5}}, false); - fRegistry.add("Track/hMeanClusterSizeITS", "mean cluster size ITS;p_{pv} (GeV/c); #times cos(#lambda)", kTH2F, {{1000, 0, 10}, {150, 0, 15}}, false); - fRegistry.add("Track/hMeanClusterSizeITSib", "mean cluster size ITSib;p_{pv} (GeV/c); #times cos(#lambda)", kTH2F, {{1000, 0, 10}, {150, 0, 15}}, false); - fRegistry.add("Track/hMeanClusterSizeITSob", "mean cluster size ITSob;p_{pv} (GeV/c); #times cos(#lambda)", kTH2F, {{1000, 0, 10}, {150, 0, 15}}, false); - - // TOF - fRegistry.add("Track/hChi2TOF", "chi2 of TOF", kTH1F, {{100, 0, 10}}, false); - fRegistry.add("Track/hTOFbeta", "TOF beta;p_{pv} (GeV/c);#beta", kTH2F, {{1000, 0, 10}, {240, 0, 1.2}}, false); - fRegistry.add("Track/hTOFNsigmaEl", "TOF n sigma el;p_{pv} (GeV/c);n #sigma_{e}^{TOF}", kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); - fRegistry.add("Track/hTOFNsigmaPi", "TOF n sigma pi;p_{pv} (GeV/c);n #sigma_{#pi}^{TOF}", kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); - - // pair - fRegistry.add("Pair/uls/hTrackMvsPt", "m_{ee} vs. p_{T,ee};m_{ee} (GeV/c^{2});p_{T,ee} (GeV/c)", kTH2F, {{100, 0, 0.1}, {200, 0, 2}}, false); - fRegistry.add("Pair/uls/hCheckEMvsPt", "m_{ee} vs. p_{T,ee};m_{ee} (GeV/c^{2});p_{T,ee} (GeV/c)", kTH2F, {{100, 0, 0.1}, {200, 0, 2}}, false); - fRegistry.add("Pair/uls/hMvsPt", "m_{ee} vs. p_{T,ee};m_{ee} (GeV/c^{2});p_{T,ee} (GeV/c)", kTH2F, {{100, 0, 0.1}, {200, 0, 2}}, false); - fRegistry.add("Pair/uls/hMCutMvsPt", "m_{ee} vs. p_{T,ee};m_{ee} (GeV/c^{2});p_{T,ee} (GeV/c)", kTH2F, {{100, 0, 0.1}, {200, 0, 2}}, false); - fRegistry.add("Pair/uls/hMPhiCutMvsPt", "m_{ee} vs. p_{T,ee};m_{ee} (GeV/c^{2});p_{T,ee} (GeV/c)", kTH2F, {{100, 0, 0.1}, {200, 0, 2}}, false); - - fRegistry.add("Pair/uls/hTrackMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi_{V} (rad.);m_{ee} (GeV/c^{2})", kTH2F, {{180, 0, o2::constants::math::PI}, {100, 0, 0.1}}, false); - fRegistry.add("Pair/uls/hCheckEMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi_{V} (rad.);m_{ee} (GeV/c^{2})", kTH2F, {{180, 0, o2::constants::math::PI}, {100, 0, 0.1}}, false); - fRegistry.add("Pair/uls/hMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi_{V} (rad.);m_{ee} (GeV/c^{2})", kTH2F, {{180, 0, o2::constants::math::PI}, {100, 0, 0.1}}, false); - fRegistry.add("Pair/uls/hMCutMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi_{V} (rad.);m_{ee} (GeV/c^{2})", kTH2F, {{180, 0, o2::constants::math::PI}, {100, 0, 0.1}}, false); - fRegistry.add("Pair/uls/hMPhiCutMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi_{V} (rad.);m_{ee} (GeV/c^{2})", kTH2F, {{180, 0, o2::constants::math::PI}, {100, 0, 0.1}}, false); - - fRegistry.addClone("Pair/uls/", "Pair/lspp/"); - fRegistry.addClone("Pair/uls/", "Pair/lsmm/"); - } - - void initCCDB(MyBCs::iterator const& bc) - { - if (mRunNumber == bc.runNumber()) { - return; - } - mRunNumber = bc.runNumber(); - // In case override, don't proceed, please - no CCDB access required - if (dBzInput > -990) { // o2-linter: disable=magic-number (check against some default number) - dBz = dBzInput; - o2::parameters::GRPMagField grpmag; - if (std::fabs(dBz) > 1e-5) { // o2-linter: disable=magic-number (check against some default number) - grpmag.setL3Current(30000.f / (dBz / 5.0f)); // o2-linter: disable=magic-number (override value) - } - o2::base::Propagator::initFieldFromGRP(&grpmag); - return; - } - - o2::base::Propagator::initFieldFromGRP(&bc.grpMagField()); - - auto run3grpTimestamp = bc.timestamp(); - - dBz = bc.grpMagField().getNominalL3Field(); - LOG(info) << "Retrieved GRP for timestamp " << run3grpTimestamp << " with magnetic field of " << dBz << " kZG"; - } - - template - void calculateTOFNSigmaWithReassociation(TCollisions const& collisions, TBCs const&, TTracks const& tracks, TTrackAssoc const& trackIndices) - { - if (useTOFNSigmaDeltaBC) { - if constexpr (withTTCA) { - for (const auto& collision : collisions) { - if (mapCollisionTime.find(collision.globalIndex()) == mapCollisionTime.end()) { - continue; - } - auto bcCollision = collision.template bc_as(); - auto trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); - for (const auto& trackId : trackIdsThisCollision) { - auto track = trackId.template track_as(); - if (!track.hasITS() || !track.hasTPC()) { // apply only minimal cut - continue; - } - - if (track.hasTOF() && track.has_collision()) { // TTCA may use orphan tracks. - auto bcTrack = track.template collision_as().template bc_as(); - float tofNSigmaEl = mTOFResponse->nSigma(track.tofSignalInAnotherBC(bcTrack.globalBC(), bcCollision.globalBC()), track.tofExpMom(), track.length(), track.p(), track.eta(), mapCollisionTime[collision.globalIndex()], mapCollisionTimeError[collision.globalIndex()]); - float beta = track.length() / (track.tofSignalInAnotherBC(bcTrack.globalBC(), bcCollision.globalBC()) - mapCollisionTime[collision.globalIndex()]) / (o2::constants::physics::LightSpeedCm2S * 1e-12); - mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = tofNSigmaEl; - mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = beta; - emtofs(collision.globalIndex(), track.globalIndex(), mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())], mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]); - } else { - mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaEl(); - mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.beta(); - emtofs(collision.globalIndex(), track.globalIndex(), mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())], mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]); - } - } // end of track loop - } // end of collision loop - } else { - for (const auto& collision : collisions) { - auto tracks_per_coll = tracks.sliceBy(perCol, collision.globalIndex()); - for (const auto& track : tracks_per_coll) { - if (!track.hasITS() || !track.hasTPC()) { // apply only minimal cut - continue; - } - mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaEl(); - mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.beta(); - emtofs(collision.globalIndex(), track.globalIndex(), mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())], mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]); - } - } // end of track loop - } // end of collision loop - } else { - if constexpr (withTTCA) { - for (const auto& collision : collisions) { - auto trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); - for (const auto& trackId : trackIdsThisCollision) { - auto track = trackId.template track_as(); - if (!track.hasITS() || !track.hasTPC()) { // apply only minimal cut - continue; - } - mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaEl(); - mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.beta(); - emtofs(collision.globalIndex(), track.globalIndex(), mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())], mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]); - } // end of track loop - } // end of collision loop - } else { - for (const auto& collision : collisions) { - auto tracks_per_coll = tracks.sliceBy(perCol, collision.globalIndex()); - for (const auto& track : tracks_per_coll) { - if (!track.hasITS() || !track.hasTPC()) { // apply only minimal cut - continue; - } - mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaEl(); - mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.beta(); - emtofs(collision.globalIndex(), track.globalIndex(), mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())], mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]); - } - } // end of track loop - } // end of collision loop - } - } - - template - bool checkTrack(TCollision const& collision, TTrack const& track) - { - if constexpr (isMC) { - if (!track.has_mcParticle()) { - return false; - } - if (storeOnlyTrueElectronMC) { - const auto& mcParticle = track.template mcParticle_as(); - if (std::abs(mcParticle.pdgCode()) != 11) { - return false; - } - } - } - - float tofNSigmaEl = mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]; - if (requireTOF && !(track.hasTOF() && std::fabs(tofNSigmaEl) < maxTOFNsigmaEl)) { - return false; - } - - if (!track.hasITS()) { - return false; - } - - if (track.itsChi2NCl() < minchi2its || maxchi2its < track.itsChi2NCl()) { - return false; - } - - if (track.itsNCls() < min_ncluster_its) { - return false; - } - if (track.itsNClsInnerBarrel() < min_ncluster_itsib) { - return false; - } - - if (!includeITSsa && !track.hasTPC()) { - return false; - } - - if (track.hasTPC()) { - if (track.tpcChi2NCl() < minchi2tpc || maxchi2tpc < track.tpcChi2NCl()) { - return false; - } - - if (track.tpcNClsFound() < min_ncluster_tpc) { - return false; - } - - if (track.tpcNClsCrossedRows() < mincrossedrows) { - return false; - } - - if (track.tpcCrossedRowsOverFindableCls() < min_tpc_cr_findable_ratio) { - return false; - } - - if (track.tpcFractionSharedCls() > max_frac_shared_clusters_tpc) { - return false; - } - } - - o2::dataformats::DCA mDcaInfoCov; // for Track association - mDcaInfoCov.set(999, 999, 999, 999, 999); - auto trackParCov = getTrackParCov(track); - trackParCov.setPID(o2::track::PID::Electron); - mVtx.setPos({collision.posX(), collision.posY(), collision.posZ()}); - mVtx.setCov(collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()); - bool isPropOK = o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackParCov, 2.f, matCorr, &mDcaInfoCov); - if (!isPropOK) { - return false; - } - float dcaXY = mDcaInfoCov.getY(); - float dcaZ = mDcaInfoCov.getZ(); - - if (std::fabs(dcaXY) > dca_xy_max || std::fabs(dcaZ) > dca_z_max) { - return false; - } - - float dca3D = 999.f; - float det = trackParCov.getSigmaY2() * trackParCov.getSigmaZ2() - trackParCov.getSigmaZY() * trackParCov.getSigmaZY(); - if (det < 0) { - dca3D = 999.f; - } else { - float chi2 = (dcaXY * dcaXY * trackParCov.getSigmaZ2() + dcaZ * dcaZ * trackParCov.getSigmaY2() - 2. * dcaXY * dcaZ * trackParCov.getSigmaZY()) / det; - dca3D = std::sqrt(std::fabs(chi2) / 2.); - } - if (dca3D > dca_3d_sigma_max) { - return false; - } - - if (trackParCov.getPt() < minpt || std::fabs(trackParCov.getEta()) > maxeta) { - return false; - } - - int total_cluster_size = 0, nl = 0; - for (unsigned int layer = 0; layer < 7; layer++) { - int cluster_size_per_layer = track.itsClsSizeInLayer(layer); - if (cluster_size_per_layer > 0) { - nl++; - } - total_cluster_size += cluster_size_per_layer; - } - - if (maxMeanITSClusterSize < static_cast(total_cluster_size) / static_cast(nl) * std::cos(std::atan(trackParCov.getTgl()))) { - return false; - } - - if ((track.hasITS() && !track.hasTPC() && !track.hasTRD() && !track.hasTOF()) && maxpt_itssa < track.pt()) { - return false; - } - - // if ((track.hasITS() && !track.hasTPC() && !track.hasTRD() && !track.hasTOF()) && maxpt_itssa < track.pt()) { - // return false; - // } - - return true; - } - - template - bool isElectron(TCollision const& collision, TTrack const& track) - { - if (includeITSsa && (track.hasITS() && !track.hasTPC() && !track.hasTRD() && !track.hasTOF())) [[unlikely]] { - int total_cluster_size = 0, nl = 0; - for (unsigned int layer = 0; layer < 7; layer++) { - int cluster_size_per_layer = track.itsClsSizeInLayer(layer); - if (cluster_size_per_layer > 0) { - nl++; - } - total_cluster_size += cluster_size_per_layer; - } - - return (maxMeanITSClusterSize > static_cast(total_cluster_size) / static_cast(nl) * std::cos(std::atan(track.tgl()))); - } - return isElectron_TPChadrej(collision, track) || isElectron_TOFreq(collision, track); - } - - template - bool isElectron_TPChadrej(TCollision const& collision, TTrack const& track) - { - float tofNSigmaEl = mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]; - - if (track.tpcNSigmaEl() < minTPCNsigmaEl || maxTPCNsigmaEl < track.tpcNSigmaEl()) { - return false; - } - if (minTPCNsigmaPi < track.tpcNSigmaPi() && track.tpcNSigmaPi() < maxTPCNsigmaPi && track.tpcInnerParam() < max_pin_for_pion_rejection) { - return false; - } - if (minTPCNsigmaKa < track.tpcNSigmaKa() && track.tpcNSigmaKa() < maxTPCNsigmaKa) { - return false; - } - if (minTPCNsigmaPr < track.tpcNSigmaPr() && track.tpcNSigmaPr() < maxTPCNsigmaPr) { - return false; - } - if (track.hasTOF() && (maxTOFNsigmaEl < std::fabs(tofNSigmaEl))) { - return false; - } - return true; - } - - template - bool isElectron_TOFreq(TCollision const& collision, TTrack const& track) - { - float tofNSigmaEl = mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]; - - if (minTPCNsigmaPi < track.tpcNSigmaPi() && track.tpcNSigmaPi() < maxTPCNsigmaPi && (min_pin_for_pion_rejection < track.tpcInnerParam() && track.tpcInnerParam() < max_pin_for_pion_rejection)) { - return false; - } - return minTPCNsigmaEl < track.tpcNSigmaEl() && track.tpcNSigmaEl() < maxTPCNsigmaEl && std::fabs(tofNSigmaEl) < maxTOFNsigmaEl; - } - - template - void fillTrackInfo(TCollision const& collision, TTracks const& tracks) - { - - for (const auto& track : tracks) { - if (!checkTrack(collision, track)) { - continue; - } - - if (!isElectron(collision, track)) { - continue; - } - - fillTrackHistograms(track); - fillTrackTable(collision, track); - } - } - - template - void fillPairInfo(TCollision const& collision, TTracks1 const& tracks1, TTracks2 const& tracks2) - { - if constexpr (pairtype == 0) { // ULS - for (const auto& [t1, t2] : combinations(CombinationsFullIndexPolicy(tracks1, tracks2))) { - - if (!checkTrack(collision, t1) || !checkTrack(collision, t2)) { - continue; - } - - if (!isElectron(collision, t1) || !isElectron(collision, t2)) { - continue; - } - - ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), o2::constants::physics::MassElectron); - ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), o2::constants::physics::MassElectron); - ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; - float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz(), t1.sign(), t2.sign(), dBz); - - fRegistry.fill(HIST("Pair/") + HIST(DileptonSigns[pairtype]) + HIST("hMvsPt"), v12.M(), v12.Pt()); - fRegistry.fill(HIST("Pair/") + HIST(DileptonSigns[pairtype]) + HIST("hMvsPhiV"), phiv, v12.M()); - - // to do: test if minMee > Mpion show effect on S/B - if (v12.M() < minMee || v12.M() > maxMee) { // don't store //|| v12.M() > maxMee - continue; - } - - fRegistry.fill(HIST("Pair/") + HIST(DileptonSigns[pairtype]) + HIST("hMCutMvsPt"), v12.M(), v12.Pt()); - fRegistry.fill(HIST("Pair/") + HIST(DileptonSigns[pairtype]) + HIST("hMCutMvsPhiV"), phiv, v12.M()); - - if (v12.M() < slope * phiv + intercept) { - continue; - } - - fRegistry.fill(HIST("Pair/") + HIST(DileptonSigns[pairtype]) + HIST("hMPhiCutMvsPt"), v12.M(), v12.Pt()); - fRegistry.fill(HIST("Pair/") + HIST(DileptonSigns[pairtype]) + HIST("hMPhiCutMvsPhiV"), phiv, v12.M()); - - if (t1.sign() > 0) { // for positron - if (std::find(acceptedPosTrackIds_per_collision.begin(), acceptedPosTrackIds_per_collision.end(), t1.globalIndex()) == acceptedPosTrackIds_per_collision.end()) { - fillTrackHistograms(t1); - acceptedPosTrackIds_per_collision.emplace_back(t1.globalIndex()); - } - } else { // for electron - if (std::find(acceptedNegTrackIds_per_collision.begin(), acceptedNegTrackIds_per_collision.end(), t1.globalIndex()) == acceptedNegTrackIds_per_collision.end()) { - fillTrackHistograms(t1); - acceptedNegTrackIds_per_collision.emplace_back(t1.globalIndex()); - } - } - - if (t2.sign() > 0) { // for positron - if (std::find(acceptedPosTrackIds_per_collision.begin(), acceptedPosTrackIds_per_collision.end(), t2.globalIndex()) == acceptedPosTrackIds_per_collision.end()) { - fillTrackHistograms(t2); - acceptedPosTrackIds_per_collision.emplace_back(t2.globalIndex()); - } - } else { // for electron - if (std::find(acceptedNegTrackIds_per_collision.begin(), acceptedNegTrackIds_per_collision.end(), t2.globalIndex()) == acceptedNegTrackIds_per_collision.end()) { - fillTrackHistograms(t2); - acceptedNegTrackIds_per_collision.emplace_back(t2.globalIndex()); - } - } - } // end of ULS pairing - } else { // LS - for (auto& [t1, t2] : combinations(CombinationsStrictlyUpperIndexPolicy(tracks1, tracks2))) { - if (!checkTrack(collision, t1) || !checkTrack(collision, t2)) { - continue; - } - if (!isElectron(collision, t1) || !isElectron(collision, t2)) { - continue; - } - - ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), o2::constants::physics::MassElectron); - ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), o2::constants::physics::MassElectron); - ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; - float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz(), t1.sign(), t2.sign(), dBz); - fRegistry.fill(HIST("Pair/") + HIST(DileptonSigns[pairtype]) + HIST("hMvsPt"), v12.M(), v12.Pt()); - fRegistry.fill(HIST("Pair/") + HIST(DileptonSigns[pairtype]) + HIST("hMvsPhiV"), phiv, v12.M()); - } // end of LS pairing - } - } - - template - void fillTrackHistograms(TTrack const& track) - { - float mcTunedTPCSignal = 0.f; - if constexpr (isMC) { - mcTunedTPCSignal = track.mcTunedTPCSignal(); - } - - fRegistry.fill(HIST("Track/hPt"), track.pt()); - fRegistry.fill(HIST("Track/hEtaPhi"), track.phi(), track.eta()); - fRegistry.fill(HIST("Track/hQoverPt"), track.sign() / track.pt()); - fRegistry.fill(HIST("Track/hRelDeltaPt"), track.pt(), track.sigma1Pt() * track.pt()); - fRegistry.fill(HIST("Track/hDCAxyz"), track.dcaXY(), track.dcaZ()); - fRegistry.fill(HIST("Track/hDCAxy_Pt"), track.pt(), track.dcaXY()); - fRegistry.fill(HIST("Track/hDCAz_Pt"), track.pt(), track.dcaZ()); - fRegistry.fill(HIST("Track/hDCAxyzSigma"), track.dcaXY() / std::sqrt(track.cYY()), track.dcaZ() / std::sqrt(track.cZZ())); - fRegistry.fill(HIST("Track/hDCAxyRes_Pt"), track.pt(), std::sqrt(track.cYY()) * 1e+4); // convert cm to um - fRegistry.fill(HIST("Track/hDCAzRes_Pt"), track.pt(), std::sqrt(track.cZZ()) * 1e+4); // convert cm to um - - fRegistry.fill(HIST("Track/hNclsTPC"), track.tpcNClsFound()); - fRegistry.fill(HIST("Track/hNcrTPC"), track.tpcNClsCrossedRows()); - fRegistry.fill(HIST("Track/hChi2TPC"), track.tpcChi2NCl()); - fRegistry.fill(HIST("Track/hTPCNcr2Nf"), track.tpcCrossedRowsOverFindableCls()); - fRegistry.fill(HIST("Track/hTPCNcls2Nf"), track.tpcFoundOverFindableCls()); - fRegistry.fill(HIST("Track/hTPCNclsShared"), track.pt(), track.tpcFractionSharedCls()); - fRegistry.fill(HIST("Track/hTPCdEdx"), track.tpcInnerParam(), track.tpcSignal()); - fRegistry.fill(HIST("Track/hTPCdEdxMC"), track.tpcInnerParam(), mcTunedTPCSignal); - fRegistry.fill(HIST("Track/hTPCNsigmaEl"), track.tpcInnerParam(), track.tpcNSigmaEl()); - fRegistry.fill(HIST("Track/hTPCNsigmaPi"), track.tpcInnerParam(), track.tpcNSigmaPi()); - - fRegistry.fill(HIST("Track/hChi2TOF"), track.tofChi2()); - fRegistry.fill(HIST("Track/hTOFbeta"), track.p(), track.beta()); - fRegistry.fill(HIST("Track/hTOFNsigmaEl"), track.p(), track.tofNSigmaEl()); - fRegistry.fill(HIST("Track/hTOFNsigmaPi"), track.p(), track.tofNSigmaPi()); - - fRegistry.fill(HIST("Track/hNclsITS"), track.itsNCls()); - fRegistry.fill(HIST("Track/hChi2ITS"), track.itsChi2NCl()); - fRegistry.fill(HIST("Track/hITSClusterMap"), track.itsClusterMap()); - - int totalClusterSize = 0, nl = 0; - for (unsigned int layer = 0; layer < 7; layer++) { // o2-linter: disable=magic-number (number of ITS layers) - int clusterSizePerLayer = track.itsClsSizeInLayer(layer); - if (clusterSizePerLayer > 0) { - nl++; - } - totalClusterSize += clusterSizePerLayer; - } - - int totalClusterSizeIb = 0, nl_ib = 0; - for (unsigned int layer = 0; layer < 3; layer++) { // o2-linter: disable=magic-number (number of inner barrel layers) - int clusterSizePerLayer = track.itsClsSizeInLayer(layer); - if (clusterSizePerLayer > 0) { - nl_ib++; - } - totalClusterSizeIb += clusterSizePerLayer; - } - - int totalClusterSizeOb = 0, nlOb = 0; - for (unsigned int layer = 3; layer < 7; layer++) { // o2-linter: disable=magic-number (number of outer barrel layers) - int clusterSizePerLayer = track.itsClsSizeInLayer(layer); - if (clusterSizePerLayer > 0) { - nlOb++; - } - totalClusterSizeOb += clusterSizePerLayer; - } - fRegistry.fill(HIST("Track/hMeanClusterSizeITS"), track.p(), static_cast(totalClusterSize) / static_cast(nl) * std::cos(std::atan(track.tgl()))); - fRegistry.fill(HIST("Track/hMeanClusterSizeITSib"), track.p(), static_cast(totalClusterSizeIb) / static_cast(nl_ib) * std::cos(std::atan(track.tgl()))); - fRegistry.fill(HIST("Track/hMeanClusterSizeITSob"), track.p(), static_cast(totalClusterSizeOb) / static_cast(nlOb) * std::cos(std::atan(track.tgl()))); - } - - template - void fillTrackTable(TCollision const& collision, TTrack const& track) - { - emprimaryelectrons(collision.globalIndex(), track.globalIndex(), track.sign(), - track.pt(), track.eta(), track.phi(), track.dcaXY(), track.dcaZ(), track.cYY(), track.cZY(), track.cZZ(), - track.tpcNClsFindable(), track.tpcNClsFindableMinusFound(), track.tpcNClsFindableMinusCrossedRows(), track.tpcNClsShared(), - track.tpcChi2NCl(), track.tpcInnerParam(), - track.tpcSignal(), track.tpcNSigmaEl(), track.tpcNSigmaPi(), - track.beta(), track.tofNSigmaEl(), - track.itsClusterSizes(), track.itsChi2NCl(), track.tofChi2(), track.detectorMap()); - - if constexpr (isMC) { - emprimaryelectronsDeDxMC(track.mcTunedTPCSignal()); - } - } - - std::vector acceptedPosTrackIds_per_collision; - std::vector acceptedNegTrackIds_per_collision; - std::vector acceptedTrackIds_per_collision; - // Filter trackFilter = minpt < o2::aod::track::pt && nabs(o2::aod::track::eta) < maxeta && o2::aod::track::itsChi2NCl < maxchi2its && ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::ITS) == true && nabs(o2::aod::track::dcaXY) < dca_xy_max && nabs(o2::aod::track::dcaZ) < dca_z_max; - // Filter trackFilter - // using MyFilteredTracks = soa::Filtered; - Partition posTracks = o2::aod::track::signed1Pt > 0.f; - Partition negTracks = o2::aod::track::signed1Pt < 0.f; - - std::unordered_map mapCollisionTime; - std::unordered_map mapCollisionTimeError; - - std::map, float> mapTOFNsigmaReassociated; // map pair(collisionId, trackId) -> tof n sigma - std::map, float> mapTOFBetaReassociated; // map pair(collisionId, trackId) -> tof beta - - // ---------- for data ---------- - void processRec(MyCollisions const& collisions, MyBCs const& bcs, MyTracks const& tracks, aod::V0PhotonsKF const& v0photons, aod::TrackAssoc const& trackIndices) - { - initCCDB(bcs.iteratorAt(0)); - mTOFResponse->processSetup(bcs.iteratorAt(0)); - - for (const auto& track : tracks) { - mapCollisionTime.try_emplace(track.collisionId(), track.tofEvTime()); - mapCollisionTimeError.try_emplace(track.collisionId(), track.tofEvTimeErr()); - } - calculateTOFNSigmaWithReassociation(collisions, bcs, tracks, trackIndices); - - for (const auto& collision : collisions) { - if (!collision.isSelected()) { - continue; - } - - auto varFillingMode = static_cast(fillingMode.value); - - const auto& v0photons_per_coll = v0photons.sliceBy(perCol_pcm, collision.globalIndex()); - const auto& posTracks_per_coll = posTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); - const auto& negTracks_per_coll = negTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); - const auto& slicedTracks = tracks.sliceBy(perTracksCollision, collision.globalIndex()); - acceptedPosTrackIds_per_collision.reserve(posTracks_per_coll.size()); - acceptedNegTrackIds_per_collision.reserve(negTracks_per_coll.size()); - - auto iterEp = tracks.begin(); - auto iterEm = tracks.begin(); - auto iterTrack = tracks.begin(); - auto iterPhoton = v0photons.begin(); - - switch (varFillingMode) { - case enumFillingMode::SingleTrack: - fillTrackInfo(collision, slicedTracks); - break; - case enumFillingMode::EpEmPairs: - fillPairInfo(collision, posTracks_per_coll, negTracks_per_coll); // ULS - if (fillLS) { - fillPairInfo(collision, posTracks_per_coll, posTracks_per_coll); // LS++ - fillPairInfo(collision, negTracks_per_coll, negTracks_per_coll); // LS-- - } - if ((!acceptedPosTrackIds_per_collision.empty() && !acceptedNegTrackIds_per_collision.empty())) { - for (const auto& posId : acceptedPosTrackIds_per_collision) { - iterEp.setCursor(posId); - fillTrackTable(collision, iterEp); - } - for (const auto& eleId : acceptedNegTrackIds_per_collision) { - iterEm.setCursor(eleId); - fillTrackTable(collision, iterEm); - } - } - break; - case enumFillingMode::EpEmPairsAndPhoton: - std::set tracksToFill; - - fillPairInfo(collision, posTracks_per_coll, negTracks_per_coll); // ULS - if (fillLS) { - fillPairInfo(collision, posTracks_per_coll, posTracks_per_coll); // LS++ - fillPairInfo(collision, negTracks_per_coll, negTracks_per_coll); // LS-- - } - if (v0photons_per_coll.size() >= 1 && ((!acceptedPosTrackIds_per_collision.empty() && !acceptedNegTrackIds_per_collision.empty()))) { - if (!fillWithEtaMassCut) { - for (const auto& posId : acceptedPosTrackIds_per_collision) { - iterEp.setCursor(posId); - fillTrackTable(collision, iterEp); - } - for (const auto& eleId : acceptedNegTrackIds_per_collision) { - iterEm.setCursor(eleId); - fillTrackTable(collision, iterEm); - } - } else { - for (const auto& posId : acceptedPosTrackIds_per_collision) { - iterEp.setCursor(posId); - ROOT::Math::PtEtaPhiMVector vEp(iterEp.pt(), iterEp.eta(), iterEp.phi(), o2::constants::physics::MassElectron); - for (const auto& eleId : acceptedNegTrackIds_per_collision) { - iterEm.setCursor(eleId); - ROOT::Math::PtEtaPhiMVector vEm(iterEm.pt(), iterEm.eta(), iterEm.phi(), o2::constants::physics::MassElectron); - for (const auto& photonId : v0photons_per_coll) { - iterPhoton.setCursor(photonId.globalIndex()); - ROOT::Math::PtEtaPhiMVector vPhoton(iterPhoton.pt(), iterPhoton.eta(), iterPhoton.phi(), o2::constants::physics::MassPhoton); - ROOT::Math::PtEtaPhiMVector vTotal = vEp + vEm + vPhoton; - - if (vTotal.M() < minMeegamma || vTotal.M() > maxMeegamma) { // don't store - continue; - } - tracksToFill.insert(posId); // store independently - tracksToFill.insert(eleId); - } - } - } - } - } - if (fillWithEtaMassCut) { - for (const auto& trackId : tracksToFill) { - iterTrack.setCursor(trackId); - fillTrackTable(collision, iterTrack); - } - } - break; - } - - acceptedPosTrackIds_per_collision.clear(); - acceptedNegTrackIds_per_collision.clear(); - - } // end of collision loop - } - PROCESS_SWITCH(skimmerPrimaryElectronFromDalitzEE, processRec, "process reconstructed info only", false); // standalone - - // void processRec_SWT(MyCollisionsWithSWT const& collisions, MyBCs const& bcs, MyTracks const& tracks, aod::V0PhotonsKF const& v0photons, aod::TrackAssoc const& trackIndices) - // { - // initCCDB(bcs.iteratorAt(0)); - - // mTOFResponse->processSetup(bcs.iteratorAt(0)); - // for (const auto& track : tracks) { - // mapCollisionTime.try_emplace(track.collisionId(), track.tofEvTime()); - // mapCollisionTimeError.try_emplace(track.collisionId(), track.tofEvTimeErr()); - // } - // calculateTOFNSigmaWithReassociation(collisions, bcs, tracks, trackIndices); - - // for (const auto& collision : collisions) { - // if (!collision.isSelected()) { - // continue; - // } - - // if (collision.swtaliastmp_raw() == 0) { - // continue; - // } - - // const auto& v0photons_per_coll = v0photons.sliceBy(perCol_pcm, collision.globalIndex()); - // const auto& posTracks_per_coll = posTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); - // const auto& negTracks_per_coll = negTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); - // const auto& slicedTracks = tracks.sliceBy(perTracksCollision, collision.globalIndex()); - // acceptedPosTrackIds_per_collision.reserve(posTracks_per_coll.size()); - // acceptedNegTrackIds_per_collision.reserve(negTracks_per_coll.size()); - - // if(!fillWithPairs){ - // fillTrackInfo(collision, slicedTracks); - // }else{ - // fillPairInfo(collision, posTracks_per_coll, negTracks_per_coll); // ULS - // if (fillLS) { - // fillPairInfo(collision, posTracks_per_coll, posTracks_per_coll); // LS++ - // fillPairInfo(collision, negTracks_per_coll, negTracks_per_coll); // LS-- - // } - - // if ((v0photons_per_coll.size() >= 1 && !acceptedPosTrackIds_per_collision.empty() && !acceptedNegTrackIds_per_collision.empty()) || (acceptedPosTrackIds_per_collision.size() >= 2 && acceptedNegTrackIds_per_collision.size() >= 2)) { - // // LOGF(info, "v0photons_per_coll.size() = %d, acceptedPosTrackIds_per_collision.size() = %d, acceptedNegTrackIds_per_collision.size() = %d", v0photons_per_coll.size(), acceptedPosTrackIds_per_collision.size(), acceptedNegTrackIds_per_collision.size()); - // for (const auto& posId : acceptedPosTrackIds_per_collision) { - // const auto& pos = tracks.rawIteratorAt(posId); - // fillTrackTable(collision, pos); - // } - // for (const auto& eleId : acceptedNegTrackIds_per_collision) { - // const auto& ele = tracks.rawIteratorAt(eleId); - // fillTrackTable(collision, ele); - // } - // } - // } - - // acceptedPosTrackIds_per_collision.clear(); - // acceptedNegTrackIds_per_collision.clear(); - - // } // end of collision loop - // } - // PROCESS_SWITCH(skimmerPrimaryElectronFromDalitzEE, processRec_SWT, "process reconstructed info with CEFP", false); // with cefp - - // using MyFilteredTracksMC = soa::Filtered; - Partition posTracksMC = o2::aod::track::signed1Pt > 0.f; - Partition negTracksMC = o2::aod::track::signed1Pt < 0.f; - // ---------- for MC ---------- - void processMC(MyCollisionsMC const& collisions, aod::McCollisions const&, MyBCs const& bcs, MyTracksMC const& tracks, aod::V0PhotonsKF const& v0photons, aod::TrackAssoc const& trackIndices, aod::McParticles const&) - { - uint64_t nCollisSel = 0; - uint64_t nNoMcColl = 0; - uint64_t nProcessedCollisions = 0; - uint64_t nCheckTrack = 0; - - initCCDB(bcs.iteratorAt(0)); - - mTOFResponse->processSetup(bcs.iteratorAt(0)); - for (const auto& track : tracks) { - mapCollisionTime.try_emplace(track.collisionId(), track.tofEvTime()); - mapCollisionTimeError.try_emplace(track.collisionId(), track.tofEvTimeErr()); - } - calculateTOFNSigmaWithReassociation(collisions, bcs, tracks, trackIndices); - - for (const auto& collision : collisions) { - if (!collision.has_mcCollision()) { - nNoMcColl++; - continue; - } - - if (!collision.isSelected()) { - nCollisSel++; - continue; - } - nProcessedCollisions++; - - auto varFillingMode = static_cast(fillingMode.value); - - const auto& v0photons_per_coll = v0photons.sliceBy(perCol_pcm, collision.globalIndex()); - const auto& posTracks_per_coll = posTracksMC->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); - const auto& negTracks_per_coll = negTracksMC->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); - const auto& slicedTracks = tracks.sliceBy(perTracksCollision, collision.globalIndex()); - acceptedPosTrackIds_per_collision.reserve(posTracks_per_coll.size()); - acceptedNegTrackIds_per_collision.reserve(negTracks_per_coll.size()); - acceptedTrackIds_per_collision.reserve(2 * (negTracks_per_coll.size())); - - auto iterEp = tracks.begin(); - auto iterEm = tracks.begin(); - auto iterTrack = tracks.begin(); - auto iterPhoton = v0photons.begin(); - - switch (varFillingMode) { - case enumFillingMode::SingleTrack: - fillTrackInfo(collision, slicedTracks); - break; - case enumFillingMode::EpEmPairs: - fillPairInfo(collision, posTracks_per_coll, negTracks_per_coll); // ULS - if (fillLS) { - fillPairInfo(collision, posTracks_per_coll, posTracks_per_coll); // LS++ - fillPairInfo(collision, negTracks_per_coll, negTracks_per_coll); // LS-- - } - if ((!acceptedPosTrackIds_per_collision.empty() && !acceptedNegTrackIds_per_collision.empty())) { - for (const auto& posId : acceptedPosTrackIds_per_collision) { - iterEp.setCursor(posId); - fillTrackTable(collision, iterEp); - } - for (const auto& eleId : acceptedNegTrackIds_per_collision) { - iterEm.setCursor(eleId); - fillTrackTable(collision, iterEm); - } - } - break; - case enumFillingMode::EpEmPairsAndPhoton: - std::set tracksToFill; - - fillPairInfo(collision, posTracks_per_coll, negTracks_per_coll); // ULS - if (fillLS) { - fillPairInfo(collision, posTracks_per_coll, posTracks_per_coll); // LS++ - fillPairInfo(collision, negTracks_per_coll, negTracks_per_coll); // LS-- - } - if (v0photons_per_coll.size() >= 1 && ((!acceptedPosTrackIds_per_collision.empty() && !acceptedNegTrackIds_per_collision.empty()))) { - if (!fillWithEtaMassCut) { - for (const auto& posId : acceptedPosTrackIds_per_collision) { - iterEp.setCursor(posId); - fillTrackTable(collision, iterEp); - } - for (const auto& eleId : acceptedNegTrackIds_per_collision) { - iterEm.setCursor(eleId); - fillTrackTable(collision, iterEm); - } - } else { - for (const auto& posId : acceptedPosTrackIds_per_collision) { - iterEp.setCursor(posId); - ROOT::Math::PtEtaPhiMVector vEp(iterEp.pt(), iterEp.eta(), iterEp.phi(), o2::constants::physics::MassElectron); - for (const auto& eleId : acceptedNegTrackIds_per_collision) { - iterEm.setCursor(eleId); - ROOT::Math::PtEtaPhiMVector vEm(iterEm.pt(), iterEm.eta(), iterEm.phi(), o2::constants::physics::MassElectron); - for (const auto& photonId : v0photons_per_coll) { - iterPhoton.setCursor(photonId.globalIndex()); - ROOT::Math::PtEtaPhiMVector vPhoton(iterPhoton.pt(), iterPhoton.eta(), iterPhoton.phi(), o2::constants::physics::MassPhoton); - ROOT::Math::PtEtaPhiMVector vTotal = vEp + vEm + vPhoton; - - if (vTotal.M() < minMeegamma || vTotal.M() > maxMeegamma) { // don't store - continue; - } - tracksToFill.insert(posId); // store independently - tracksToFill.insert(eleId); - } - } - } - } - } - if (fillWithEtaMassCut) { - for (const auto& trackId : tracksToFill) { - iterTrack.setCursor(trackId); - fillTrackTable(collision, iterTrack); - } - } - break; - } - - acceptedPosTrackIds_per_collision.clear(); - acceptedNegTrackIds_per_collision.clear(); - - } // end of collision loop - - LOG(info) << "Total collisions: " << collisions.size(); - LOG(info) << "No MC collision: " << nNoMcColl; - LOG(info) << "Rejected by selection: " << nCollisSel; - LOG(info) << "Processed collisions: " << nProcessedCollisions; - LOG(info) << "Invalid track: " << nCheckTrack; - } - PROCESS_SWITCH(skimmerPrimaryElectronFromDalitzEE, processMC, "process reconstructed and MC info ", true); -}; WorkflowSpec defineDataProcessing(ConfigContext const& context) { o2::pid::tof::TOFResponseImpl::metadataInfo.initMetadata(context); return WorkflowSpec{ - adaptAnalysisTask(context, TaskName{"skimmer-primary-electron-from-dalitzee"})}; + adaptAnalysisTask>(context, TaskName{"skimmer-primary-electron-from-dalitzee"})}; } diff --git a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h new file mode 100644 index 00000000000..7c726a58999 --- /dev/null +++ b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h @@ -0,0 +1,1023 @@ +// Copyright 2019-2020 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 skimmerPrimaryElectronFromDalitzEE.h +/// \brief write relevant information about primary electrons. +/// \author daiki.sekihata@cern.ch + +#include "PWGEM/Dilepton/Utils/PairUtilities.h" +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" + +#include "Common/Core/PID/PIDTOFParamService.h" +#include "Common/DataModel/CollisionAssociationTables.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// using namespace o2; +// using namespace o2::soa; +// using namespace o2::framework; +// using namespace o2::framework::expressions; +// using namespace o2::constants::physics; + +using o2::framework::AxisSpec; +using o2::framework::Configurable; +using o2::framework::ConfigurableGroup; +using o2::framework::HistType; +using o2::framework::kTH1D; +using o2::framework::kTH1F; +using o2::framework::kTH2F; +using o2::framework::kTHnSparseF; +using o2::framework::Preslice; +using o2::framework::Produces; + +using MyCollisions = o2::soa::Join; +using MyCollisionsWithSWT = o2::soa::Join; +using MyBCs = o2::soa::Join; + +using MyCollisionsMC = o2::soa::Join; +using MyTracks = o2::soa::Join; +using MyTrack = MyTracks::iterator; +using MyTracksMC = o2::soa::Join; +using MyTrackMC = MyTracksMC::iterator; + +namespace o2::aod +{ +namespace pwgem::pm::recalculatedtofpid +{ +DECLARE_SOA_COLUMN(BetaRecalculated, betaRecalculated, float); +DECLARE_SOA_COLUMN(TOFNSigmaElRecalculated, tofNSigmaElRecalculated, float); +} // namespace pwgem::pm::recalculatedtofpid + +DECLARE_SOA_TABLE(EMTOFNSigmas, "AOD", "EMTOFNSIGMA", // make std::map in your tasks later. // Don't store this table in the derived data. + o2::aod::emprimaryelectron::CollisionId, o2::aod::emprimaryelectron::TrackId, + o2::aod::pwgem::pm::recalculatedtofpid::BetaRecalculated, o2::aod::pwgem::pm::recalculatedtofpid::TOFNSigmaElRecalculated); + +using EMTOFNSigma = EMTOFNSigmas::iterator; +} // namespace o2::aod + +template +struct skimmerPrimaryElectronFromDalitzEE { + + o2::framework::SliceCache cache; + Preslice perCol = o2::aod::track::collisionId; + o2::framework::PresliceOptional perTracksCollision = o2::aod::track::collisionId; + Preslice perCol_pcm = o2::aod::v0photonkf::collisionId; // o2::aod::V0PhotonsKF + Preslice trackIndicesPerCollision = o2::aod::track_association::collisionId; + Produces emprimaryelectrons; + Produces emprimaryelectronsDeDxMC; + o2::framework::Service mTOFResponse{}; + + Produces emtofs; + + enum class enumFillingMode { + SingleTrack = 1, + EpEmPairs = 2, + EpEmPairsAndPhoton = 3 + }; + + // Configurables + Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; + Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + Configurable skipGRPOquery{"skipGRPOquery", true, "skip grpo query"}; + + // Operation and minimisation criteria + Configurable dBzInput{"dBzInput", -999, "bz field in kG, -999 is automatic"}; + Configurable min_ncluster_tpc{"min_ncluster_tpc", 0, "min ncluster tpc"}; // o2-linter: disable=name/function-variable (renaming configs would mess up hyperloop) + Configurable mincrossedrows{"mincrossedrows", 70, "min. crossed rows"}; + Configurable min_tpc_cr_findable_ratio{"min_tpc_cr_findable_ratio", 0.8, "min. TPC Ncr/Nf ratio"}; + Configurable max_frac_shared_clusters_tpc{"max_frac_shared_clusters_tpc", 999.f, "max fraction of shared clusters in TPC"}; + Configurable min_ncluster_its{"min_ncluster_its", 4, "min ncluster its"}; + Configurable min_ncluster_itsib{"min_ncluster_itsib", 1, "min ncluster itsib"}; + Configurable minchi2tpc{"minchi2tpc", 0.0, "min. chi2/NclsTPC"}; + Configurable maxchi2tpc{"maxchi2tpc", 5.0, "max. chi2/NclsTPC"}; + Configurable minchi2its{"minchi2its", 0.0, "min. chi2/NclsITS"}; + Configurable maxchi2its{"maxchi2its", 36.0, "max. chi2/NclsITS"}; + Configurable minpt{"minpt", 0.05, "min pt for ITS-TPC track"}; + Configurable maxeta{"maxeta", 2.0, "max eta acceptance"}; + Configurable dca_xy_max{"dca_xy_max", 1, "max DCAxy in cm"}; // o2-linter: disable=name/function-variable (renaming configs would mess up hyperloop) + Configurable dca_z_max{"dca_z_max", 1, "max DCAz in cm"}; // o2-linter: disable=name/function-variable (renaming configs would mess up hyperloop) + Configurable dca_3d_sigma_max{"dca_3d_sigma_max", 2, "max DCA 3D in sigma"}; // o2-linter: disable=name/function-variable (renaming configs would mess up hyperloop) + Configurable minTPCNsigmaEl{"minTPCNsigmaEl", -2.5, "min. TPC n sigma for electron inclusion"}; + Configurable maxTPCNsigmaEl{"maxTPCNsigmaEl", +3.5, "max. TPC n sigma for electron inclusion"}; + Configurable maxTPCNsigmaPi{"maxTPCNsigmaPi", 0.0, "max. TPC n sigma for pion exclusion"}; + Configurable minTPCNsigmaPi{"minTPCNsigmaPi", 0.0, "min. TPC n sigma for pion exclusion"}; + Configurable minTOFNsigmaEl{"minTOFNsigmaEl", -3.5, "min. TOF n sigma for electron inclusion"}; + Configurable maxTOFNsigmaEl{"maxTOFNsigmaEl", +3.5, "max. TOF n sigma for electron inclusion"}; + Configurable minTPCNsigmaKa{"minTPCNsigmaKa", -2.5, "min. TPC n sigma for kaon exclusion"}; + Configurable maxTPCNsigmaKa{"maxTPCNsigmaKa", 2.5, "max. TPC n sigma for kaon exclusion"}; + Configurable minTPCNsigmaPr{"minTPCNsigmaPr", -2.5, "min. TPC n sigma for proton exclusion"}; + Configurable maxTPCNsigmaPr{"maxTPCNsigmaPr", 2.5, "max. TPC n sigma for proton exclusion"}; + Configurable requireTOF{"requireTOF", false, "require TOF hit"}; + Configurable min_pin_for_pion_rejection{"min_pin_for_pion_rejection", 0.0, "pion rejection is applied above this pin"}; // this is used only in TOFreq + Configurable max_pin_for_pion_rejection{"max_pin_for_pion_rejection", 0.5, "pion rejection is applied below this pin"}; + Configurable minMee{"minMee", 0., "min. mee to store dalitz ee pairs"}; + Configurable maxMee{"maxMee", 0.5, "max. mee to store dalitz ee pairs"}; + Configurable minMeegamma{"minMeegamma", 0.3, "min. mee to store eegamma candidates"}; + Configurable maxMeegamma{"maxMeegamma", 0.8, "max. mee to store eegamma candidates"}; + Configurable fillLS{"fillLS", true, "flag to fill LS histograms for QA"}; + Configurable fillingMode{"fillingMode", 1, "Filling mode| 1: fill tracks without pair selection, 2: fill tracks from selected pairs, 3: fill tracks from selected pairs that can be combined with a photon"}; + Configurable fillWithEtaMassCut{"fillWithEtaMassCut", true, "only valid for fillingmode 3; true: filling tabled based on eta candidate selection with minMeegamma < M < maxMeegamma, false: fill identical to fillingmode 2"}; + Configurable includeITSsa{"includeITSsa", false, "Flag to include ITSsa tracks"}; + Configurable maxpt_itssa{"maxpt_itssa", 0.15, "max pt for ITSsa track"}; // o2-linter: disable=name/function-variable (renaming configs would mess up hyperloop) + Configurable maxMeanITSClusterSize{"maxMeanITSClusterSize", 16, "max x cos(lambda)"}; + Configurable slope{"slope", 0.0185, "slope for m vs. phiv"}; + Configurable intercept{"intercept", -0.0380, "intercept for m vs. phiv"}; + Configurable useTOFNSigmaDeltaBC{"useTOFNSigmaDeltaBC", false, "Flag to shift delta BC for TOF n sigma (only with TTCA)"}; + Configurable storeOnlyTrueElectronMC{"storeOnlyTrueElectronMC", false, "Flag to store only true electron in MC"}; + + o2::framework::HistogramRegistry fRegistry{"output", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject, false, false}; + static constexpr std::array DileptonSigns = {"uls/", "lspp/", "lsmm/"}; + + int mRunNumber = 0; + float dBz = 0.; + o2::framework::Service ccdb{}; + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; + o2::dataformats::VertexBase mVtx; + + void init(o2::framework::InitContext& initContext) + { + // if (doprocessRec && doprocessRec_SWT) { + // LOGF(fatal, "Cannot enable doprocessRec and doprocessRec_SWT at the same time. Please choose one."); + // } + + mRunNumber = 0; + dBz = 0; + + ccdb->setURL(ccdburl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + + LOGF(info, "before TOF initSetup"); + mTOFResponse->initSetup(ccdb, initContext); + LOGF(info, "after TOF initSetup"); + + fRegistry.add("Track/hPt", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("Track/hEtaPhi", "#eta vs. #varphi;#varphi (rad.);#eta", kTH2F, {{180, 0, o2::constants::math::TwoPI}, {400, -2.0f, 2.0f}}, false); + fRegistry.add("Track/hQoverPt", "q/pT;q/p_{T} (GeV/c)^{-1}", kTH1F, {{400, -20, 20}}, false); + fRegistry.add("Track/hRelDeltaPt", "pT resolution;p_{T} (GeV/c);#Deltap_{T}/p_{T}", kTH2F, {{1000, 0, 10}, {100, 0, 0.1}}, false); + fRegistry.add("Track/hDCAxyz", "DCA xy vs. z;DCA_{xy} (cm);DCA_{z} (cm)", kTH2F, {{200, -1.0f, 1.0f}, {200, -1.0f, 1.0f}}, false); + fRegistry.add("Track/hDCAxy_Pt", "DCA_{xy} vs. pT;p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {{200, 0, 10}, {200, -1, 1}}, false); + fRegistry.add("Track/hDCAz_Pt", "DCA_{z} vs. pT;p_{T} (GeV/c);DCA_{z} (cm)", kTH2F, {{200, 0, 10}, {200, -1, 1}}, false); + fRegistry.add("Track/hDCAxyzSigma", "DCA xy vs. z;DCA_{xy} (#sigma);DCA_{z} (#sigma)", kTH2F, {{200, -10.0f, 10.0f}, {200, -10.0f, 10.0f}}, false); + fRegistry.add("Track/hDCAxyRes_Pt", "DCA_{xy} resolution vs. pT;p_{T} (GeV/c);DCA_{xy} resolution (#mum)", kTH2F, {{200, 0, 10}, {500, 0., 500}}, false); + fRegistry.add("Track/hDCAzRes_Pt", "DCA_{z} resolution vs. pT;p_{T} (GeV/c);DCA_{z} resolution (#mum)", kTH2F, {{200, 0, 10}, {500, 0., 500}}, false); + + // TPC + fRegistry.add("Track/hNclsTPC", "number of TPC clusters", kTH1F, {{161, -0.5, 160.5}}, false); + fRegistry.add("Track/hNcrTPC", "number of TPC crossed rows", kTH1F, {{161, -0.5, 160.5}}, false); + fRegistry.add("Track/hChi2TPC", "chi2/number of TPC clusters", kTH1F, {{100, 0, 10}}, false); + fRegistry.add("Track/hTPCNcr2Nf", "TPC Ncr/Nfindable", kTH1F, {{200, 0, 2}}, false); + fRegistry.add("Track/hTPCNcls2Nf", "TPC Ncls/Nfindable", kTH1F, {{200, 0, 2}}, false); + fRegistry.add("Track/hTPCNclsShared", "TPC Ncls shared/Ncls;p_{T} (GeV/c);N_{cls}^{shared}/N_{cls} in TPC", kTH2F, {{1000, 0, 10}, {100, 0, 1}}, false); + fRegistry.add("Track/hTPCdEdx", "TPC dE/dx;p_{in} (GeV/c);TPC dE/dx (a.u.)", kTH2F, {{1000, 0, 10}, {200, 0, 200}}, false); + fRegistry.add("Track/hTPCdEdxMC", "TPC dE/dx;p_{in} (GeV/c);TPC dE/dx (a.u.)", kTH2F, {{1000, 0, 10}, {200, 0, 200}}, false); + fRegistry.add("Track/hTPCNsigmaEl", "TPC n sigma el;p_{in} (GeV/c);n #sigma_{e}^{TPC}", kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); + fRegistry.add("Track/hTPCNsigmaPi", "TPC n sigma pi;p_{in} (GeV/c);n #sigma_{#pi}^{TPC}", kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); + + // ITS + fRegistry.add("Track/hNclsITS", "number of ITS clusters", kTH1F, {{8, -0.5, 7.5}}, false); + fRegistry.add("Track/hChi2ITS", "chi2/number of ITS clusters", kTH1F, {{400, 0, 40}}, false); + fRegistry.add("Track/hITSClusterMap", "ITS cluster map", kTH1F, {{128, -0.5, 127.5}}, false); + fRegistry.add("Track/hMeanClusterSizeITS", "mean cluster size ITS;p_{pv} (GeV/c); #times cos(#lambda)", kTH2F, {{1000, 0, 10}, {150, 0, 15}}, false); + fRegistry.add("Track/hMeanClusterSizeITSib", "mean cluster size ITSib;p_{pv} (GeV/c); #times cos(#lambda)", kTH2F, {{1000, 0, 10}, {150, 0, 15}}, false); + fRegistry.add("Track/hMeanClusterSizeITSob", "mean cluster size ITSob;p_{pv} (GeV/c); #times cos(#lambda)", kTH2F, {{1000, 0, 10}, {150, 0, 15}}, false); + + // TOF + fRegistry.add("Track/hChi2TOF", "chi2 of TOF", kTH1F, {{100, 0, 10}}, false); + fRegistry.add("Track/hTOFbeta", "TOF beta;p_{pv} (GeV/c);#beta", kTH2F, {{1000, 0, 10}, {240, 0, 1.2}}, false); + fRegistry.add("Track/hTOFNsigmaEl", "TOF n sigma el;p_{pv} (GeV/c);n #sigma_{e}^{TOF}", kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); + fRegistry.add("Track/hTOFNsigmaPi", "TOF n sigma pi;p_{pv} (GeV/c);n #sigma_{#pi}^{TOF}", kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); + + // pair + fRegistry.add("Pair/uls/hTrackMvsPt", "m_{ee} vs. p_{T,ee};m_{ee} (GeV/c^{2});p_{T,ee} (GeV/c)", kTH2F, {{100, 0, 0.1}, {200, 0, 2}}, false); + fRegistry.add("Pair/uls/hCheckEMvsPt", "m_{ee} vs. p_{T,ee};m_{ee} (GeV/c^{2});p_{T,ee} (GeV/c)", kTH2F, {{100, 0, 0.1}, {200, 0, 2}}, false); + fRegistry.add("Pair/uls/hMvsPt", "m_{ee} vs. p_{T,ee};m_{ee} (GeV/c^{2});p_{T,ee} (GeV/c)", kTH2F, {{100, 0, 0.1}, {200, 0, 2}}, false); + fRegistry.add("Pair/uls/hMCutMvsPt", "m_{ee} vs. p_{T,ee};m_{ee} (GeV/c^{2});p_{T,ee} (GeV/c)", kTH2F, {{100, 0, 0.1}, {200, 0, 2}}, false); + fRegistry.add("Pair/uls/hMPhiCutMvsPt", "m_{ee} vs. p_{T,ee};m_{ee} (GeV/c^{2});p_{T,ee} (GeV/c)", kTH2F, {{100, 0, 0.1}, {200, 0, 2}}, false); + + fRegistry.add("Pair/uls/hTrackMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi_{V} (rad.);m_{ee} (GeV/c^{2})", kTH2F, {{180, 0, o2::constants::math::PI}, {100, 0, 0.1}}, false); + fRegistry.add("Pair/uls/hCheckEMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi_{V} (rad.);m_{ee} (GeV/c^{2})", kTH2F, {{180, 0, o2::constants::math::PI}, {100, 0, 0.1}}, false); + fRegistry.add("Pair/uls/hMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi_{V} (rad.);m_{ee} (GeV/c^{2})", kTH2F, {{180, 0, o2::constants::math::PI}, {100, 0, 0.1}}, false); + fRegistry.add("Pair/uls/hMCutMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi_{V} (rad.);m_{ee} (GeV/c^{2})", kTH2F, {{180, 0, o2::constants::math::PI}, {100, 0, 0.1}}, false); + fRegistry.add("Pair/uls/hMPhiCutMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi_{V} (rad.);m_{ee} (GeV/c^{2})", kTH2F, {{180, 0, o2::constants::math::PI}, {100, 0, 0.1}}, false); + + fRegistry.addClone("Pair/uls/", "Pair/lspp/"); + fRegistry.addClone("Pair/uls/", "Pair/lsmm/"); + } + + void initCCDB(MyBCs::iterator const& bc) + { + if (mRunNumber == bc.runNumber()) { + return; + } + mRunNumber = bc.runNumber(); + // In case override, don't proceed, please - no CCDB access required + if (dBzInput > -990) { // o2-linter: disable=magic-number (check against some default number) + dBz = dBzInput; + o2::parameters::GRPMagField grpmag; + if (std::fabs(dBz) > 1e-5) { // o2-linter: disable=magic-number (check against some default number) + grpmag.setL3Current(30000.f / (dBz / 5.0f)); // o2-linter: disable=magic-number (override value) + } + o2::base::Propagator::initFieldFromGRP(&grpmag); + return; + } + + o2::base::Propagator::initFieldFromGRP(&bc.grpMagField()); + + auto run3grpTimestamp = bc.timestamp(); + + dBz = bc.grpMagField().getNominalL3Field(); + LOG(info) << "Retrieved GRP for timestamp " << run3grpTimestamp << " with magnetic field of " << dBz << " kZG"; + } + + template + void calculateTOFNSigmaWithReassociation(TCollisions const& collisions, TBCs const&, TTracks const& tracks, TTrackAssoc const& trackIndices) + { + if (useTOFNSigmaDeltaBC) { + if constexpr (withTTCA) { + for (const auto& collision : collisions) { + if (mapCollisionTime.find(collision.globalIndex()) == mapCollisionTime.end()) { + continue; + } + auto bcCollision = collision.template bc_as(); + auto trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); + for (const auto& trackId : trackIdsThisCollision) { + auto track = trackId.template track_as(); + if (!track.hasITS() || !track.hasTPC()) { // apply only minimal cut + continue; + } + + if (track.hasTOF() && track.has_collision()) { // TTCA may use orphan tracks. + auto bcTrack = track.template collision_as().template bc_as(); + float tofNSigmaEl = mTOFResponse->nSigma(track.tofSignalInAnotherBC(bcTrack.globalBC(), bcCollision.globalBC()), track.tofExpMom(), track.length(), track.p(), track.eta(), mapCollisionTime[collision.globalIndex()], mapCollisionTimeError[collision.globalIndex()]); + float beta = track.length() / (track.tofSignalInAnotherBC(bcTrack.globalBC(), bcCollision.globalBC()) - mapCollisionTime[collision.globalIndex()]) / (o2::constants::physics::LightSpeedCm2S * 1e-12); + mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = tofNSigmaEl; + mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = beta; + emtofs(collision.globalIndex(), track.globalIndex(), mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())], mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]); + } else { + mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaEl(); + mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.beta(); + emtofs(collision.globalIndex(), track.globalIndex(), mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())], mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]); + } + } // end of track loop + } // end of collision loop + } else { + for (const auto& collision : collisions) { + auto tracks_per_coll = tracks.sliceBy(perCol, collision.globalIndex()); + for (const auto& track : tracks_per_coll) { + if (!track.hasITS() || !track.hasTPC()) { // apply only minimal cut + continue; + } + mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaEl(); + mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.beta(); + emtofs(collision.globalIndex(), track.globalIndex(), mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())], mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]); + } + } // end of track loop + } // end of collision loop + } else { + if constexpr (withTTCA) { + for (const auto& collision : collisions) { + auto trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); + for (const auto& trackId : trackIdsThisCollision) { + auto track = trackId.template track_as(); + if (!track.hasITS() || !track.hasTPC()) { // apply only minimal cut + continue; + } + mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaEl(); + mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.beta(); + emtofs(collision.globalIndex(), track.globalIndex(), mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())], mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]); + } // end of track loop + } // end of collision loop + } else { + for (const auto& collision : collisions) { + auto tracks_per_coll = tracks.sliceBy(perCol, collision.globalIndex()); + for (const auto& track : tracks_per_coll) { + if (!track.hasITS() || !track.hasTPC()) { // apply only minimal cut + continue; + } + mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaEl(); + mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.beta(); + emtofs(collision.globalIndex(), track.globalIndex(), mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())], mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]); + } + } // end of track loop + } // end of collision loop + } + } + + template + bool checkTrack(TCollision const& collision, TTrack const& track) + { + if constexpr (isMC) { + if (!track.has_mcParticle()) { + return false; + } + if (storeOnlyTrueElectronMC) { + const auto& mcParticle = track.template mcParticle_as(); + if (std::abs(mcParticle.pdgCode()) != 11) { + return false; + } + } + } + + float tofNSigmaEl = mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]; + if (requireTOF && !(track.hasTOF() && std::fabs(tofNSigmaEl) < maxTOFNsigmaEl)) { + return false; + } + + if (!track.hasITS()) { + return false; + } + + if (track.itsChi2NCl() < minchi2its || maxchi2its < track.itsChi2NCl()) { + return false; + } + + if (track.itsNCls() < min_ncluster_its) { + return false; + } + if (track.itsNClsInnerBarrel() < min_ncluster_itsib) { + return false; + } + + if (!includeITSsa && !track.hasTPC()) { + return false; + } + + if (track.hasTPC()) { + if (track.tpcChi2NCl() < minchi2tpc || maxchi2tpc < track.tpcChi2NCl()) { + return false; + } + + if (track.tpcNClsFound() < min_ncluster_tpc) { + return false; + } + + if (track.tpcNClsCrossedRows() < mincrossedrows) { + return false; + } + + if (track.tpcCrossedRowsOverFindableCls() < min_tpc_cr_findable_ratio) { + return false; + } + + if (track.tpcFractionSharedCls() > max_frac_shared_clusters_tpc) { + return false; + } + } + + o2::dataformats::DCA mDcaInfoCov; // for Track association + mDcaInfoCov.set(999, 999, 999, 999, 999); + auto trackParCov = getTrackParCov(track); + trackParCov.setPID(o2::track::PID::Electron); + mVtx.setPos({collision.posX(), collision.posY(), collision.posZ()}); + mVtx.setCov(collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()); + bool isPropOK = o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackParCov, 2.f, matCorr, &mDcaInfoCov); + if (!isPropOK) { + return false; + } + float dcaXY = mDcaInfoCov.getY(); + float dcaZ = mDcaInfoCov.getZ(); + + if (std::fabs(dcaXY) > dca_xy_max || std::fabs(dcaZ) > dca_z_max) { + return false; + } + + float dca3D = 999.f; + float det = trackParCov.getSigmaY2() * trackParCov.getSigmaZ2() - trackParCov.getSigmaZY() * trackParCov.getSigmaZY(); + if (det < 0) { + dca3D = 999.f; + } else { + float chi2 = (dcaXY * dcaXY * trackParCov.getSigmaZ2() + dcaZ * dcaZ * trackParCov.getSigmaY2() - 2. * dcaXY * dcaZ * trackParCov.getSigmaZY()) / det; + dca3D = std::sqrt(std::fabs(chi2) / 2.); + } + if (dca3D > dca_3d_sigma_max) { + return false; + } + + if (trackParCov.getPt() < minpt || std::fabs(trackParCov.getEta()) > maxeta) { + return false; + } + + int total_cluster_size = 0, nl = 0; + for (unsigned int layer = 0; layer < 7; layer++) { + int cluster_size_per_layer = track.itsClsSizeInLayer(layer); + if (cluster_size_per_layer > 0) { + nl++; + } + total_cluster_size += cluster_size_per_layer; + } + + if (maxMeanITSClusterSize < static_cast(total_cluster_size) / static_cast(nl) * std::cos(std::atan(trackParCov.getTgl()))) { + return false; + } + + if ((track.hasITS() && !track.hasTPC() && !track.hasTRD() && !track.hasTOF()) && maxpt_itssa < track.pt()) { + return false; + } + + // if ((track.hasITS() && !track.hasTPC() && !track.hasTRD() && !track.hasTOF()) && maxpt_itssa < track.pt()) { + // return false; + // } + + return true; + } + + template + bool isElectron(TCollision const& collision, TTrack const& track) + { + if (includeITSsa && (track.hasITS() && !track.hasTPC() && !track.hasTRD() && !track.hasTOF())) [[unlikely]] { + int total_cluster_size = 0, nl = 0; + for (unsigned int layer = 0; layer < 7; layer++) { + int cluster_size_per_layer = track.itsClsSizeInLayer(layer); + if (cluster_size_per_layer > 0) { + nl++; + } + total_cluster_size += cluster_size_per_layer; + } + + return (maxMeanITSClusterSize > static_cast(total_cluster_size) / static_cast(nl) * std::cos(std::atan(track.tgl()))); + } + return isElectron_TPChadrej(collision, track) || isElectron_TOFreq(collision, track); + } + + template + bool isElectron_TPChadrej(TCollision const& collision, TTrack const& track) + { + float tofNSigmaEl = mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]; + + if (track.tpcNSigmaEl() < minTPCNsigmaEl || maxTPCNsigmaEl < track.tpcNSigmaEl()) { + return false; + } + if (minTPCNsigmaPi < track.tpcNSigmaPi() && track.tpcNSigmaPi() < maxTPCNsigmaPi && track.tpcInnerParam() < max_pin_for_pion_rejection) { + return false; + } + if (minTPCNsigmaKa < track.tpcNSigmaKa() && track.tpcNSigmaKa() < maxTPCNsigmaKa) { + return false; + } + if (minTPCNsigmaPr < track.tpcNSigmaPr() && track.tpcNSigmaPr() < maxTPCNsigmaPr) { + return false; + } + if (track.hasTOF() && (maxTOFNsigmaEl < std::fabs(tofNSigmaEl))) { + return false; + } + return true; + } + + template + bool isElectron_TOFreq(TCollision const& collision, TTrack const& track) + { + float tofNSigmaEl = mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]; + + if (minTPCNsigmaPi < track.tpcNSigmaPi() && track.tpcNSigmaPi() < maxTPCNsigmaPi && (min_pin_for_pion_rejection < track.tpcInnerParam() && track.tpcInnerParam() < max_pin_for_pion_rejection)) { + return false; + } + return minTPCNsigmaEl < track.tpcNSigmaEl() && track.tpcNSigmaEl() < maxTPCNsigmaEl && std::fabs(tofNSigmaEl) < maxTOFNsigmaEl; + } + + template + void fillTrackInfo(TCollision const& collision, TTracks const& tracks) + { + + for (const auto& track : tracks) { + if (!checkTrack(collision, track)) { + continue; + } + + if (!isElectron(collision, track)) { + continue; + } + + fillTrackHistograms(track); + fillTrackTable(collision, track); + } + } + + template + void fillPairInfo(TCollision const& collision, TTracks1 const& tracks1, TTracks2 const& tracks2) + { + if constexpr (pairtype == 0) { // ULS + for (const auto& [t1, t2] : combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + + if (!checkTrack(collision, t1) || !checkTrack(collision, t2)) { + continue; + } + + if (!isElectron(collision, t1) || !isElectron(collision, t2)) { + continue; + } + + ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz(), t1.sign(), t2.sign(), dBz); + + fRegistry.fill(HIST("Pair/") + HIST(DileptonSigns[pairtype]) + HIST("hMvsPt"), v12.M(), v12.Pt()); + fRegistry.fill(HIST("Pair/") + HIST(DileptonSigns[pairtype]) + HIST("hMvsPhiV"), phiv, v12.M()); + + // to do: test if minMee > Mpion show effect on S/B + if (v12.M() < minMee || v12.M() > maxMee) { // don't store //|| v12.M() > maxMee + continue; + } + + fRegistry.fill(HIST("Pair/") + HIST(DileptonSigns[pairtype]) + HIST("hMCutMvsPt"), v12.M(), v12.Pt()); + fRegistry.fill(HIST("Pair/") + HIST(DileptonSigns[pairtype]) + HIST("hMCutMvsPhiV"), phiv, v12.M()); + + if (v12.M() < slope * phiv + intercept) { + continue; + } + + fRegistry.fill(HIST("Pair/") + HIST(DileptonSigns[pairtype]) + HIST("hMPhiCutMvsPt"), v12.M(), v12.Pt()); + fRegistry.fill(HIST("Pair/") + HIST(DileptonSigns[pairtype]) + HIST("hMPhiCutMvsPhiV"), phiv, v12.M()); + + if (t1.sign() > 0) { // for positron + if (std::find(acceptedPosTrackIds_per_collision.begin(), acceptedPosTrackIds_per_collision.end(), t1.globalIndex()) == acceptedPosTrackIds_per_collision.end()) { + fillTrackHistograms(t1); + acceptedPosTrackIds_per_collision.emplace_back(t1.globalIndex()); + } + } else { // for electron + if (std::find(acceptedNegTrackIds_per_collision.begin(), acceptedNegTrackIds_per_collision.end(), t1.globalIndex()) == acceptedNegTrackIds_per_collision.end()) { + fillTrackHistograms(t1); + acceptedNegTrackIds_per_collision.emplace_back(t1.globalIndex()); + } + } + + if (t2.sign() > 0) { // for positron + if (std::find(acceptedPosTrackIds_per_collision.begin(), acceptedPosTrackIds_per_collision.end(), t2.globalIndex()) == acceptedPosTrackIds_per_collision.end()) { + fillTrackHistograms(t2); + acceptedPosTrackIds_per_collision.emplace_back(t2.globalIndex()); + } + } else { // for electron + if (std::find(acceptedNegTrackIds_per_collision.begin(), acceptedNegTrackIds_per_collision.end(), t2.globalIndex()) == acceptedNegTrackIds_per_collision.end()) { + fillTrackHistograms(t2); + acceptedNegTrackIds_per_collision.emplace_back(t2.globalIndex()); + } + } + } // end of ULS pairing + } else { // LS + for (auto& [t1, t2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(tracks1, tracks2))) { + if (!checkTrack(collision, t1) || !checkTrack(collision, t2)) { + continue; + } + if (!isElectron(collision, t1) || !isElectron(collision, t2)) { + continue; + } + + ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz(), t1.sign(), t2.sign(), dBz); + fRegistry.fill(HIST("Pair/") + HIST(DileptonSigns[pairtype]) + HIST("hMvsPt"), v12.M(), v12.Pt()); + fRegistry.fill(HIST("Pair/") + HIST(DileptonSigns[pairtype]) + HIST("hMvsPhiV"), phiv, v12.M()); + } // end of LS pairing + } + } + + template + void fillTrackHistograms(TTrack const& track) + { + float mcTunedTPCSignal = 0.f; + if constexpr (isMC) { + mcTunedTPCSignal = track.mcTunedTPCSignal(); + } + + fRegistry.fill(HIST("Track/hPt"), track.pt()); + fRegistry.fill(HIST("Track/hEtaPhi"), track.phi(), track.eta()); + fRegistry.fill(HIST("Track/hQoverPt"), track.sign() / track.pt()); + fRegistry.fill(HIST("Track/hRelDeltaPt"), track.pt(), track.sigma1Pt() * track.pt()); + fRegistry.fill(HIST("Track/hDCAxyz"), track.dcaXY(), track.dcaZ()); + fRegistry.fill(HIST("Track/hDCAxy_Pt"), track.pt(), track.dcaXY()); + fRegistry.fill(HIST("Track/hDCAz_Pt"), track.pt(), track.dcaZ()); + fRegistry.fill(HIST("Track/hDCAxyzSigma"), track.dcaXY() / std::sqrt(track.cYY()), track.dcaZ() / std::sqrt(track.cZZ())); + fRegistry.fill(HIST("Track/hDCAxyRes_Pt"), track.pt(), std::sqrt(track.cYY()) * 1e+4); // convert cm to um + fRegistry.fill(HIST("Track/hDCAzRes_Pt"), track.pt(), std::sqrt(track.cZZ()) * 1e+4); // convert cm to um + + fRegistry.fill(HIST("Track/hNclsTPC"), track.tpcNClsFound()); + fRegistry.fill(HIST("Track/hNcrTPC"), track.tpcNClsCrossedRows()); + fRegistry.fill(HIST("Track/hChi2TPC"), track.tpcChi2NCl()); + fRegistry.fill(HIST("Track/hTPCNcr2Nf"), track.tpcCrossedRowsOverFindableCls()); + fRegistry.fill(HIST("Track/hTPCNcls2Nf"), track.tpcFoundOverFindableCls()); + fRegistry.fill(HIST("Track/hTPCNclsShared"), track.pt(), track.tpcFractionSharedCls()); + fRegistry.fill(HIST("Track/hTPCdEdx"), track.tpcInnerParam(), track.tpcSignal()); + fRegistry.fill(HIST("Track/hTPCdEdxMC"), track.tpcInnerParam(), mcTunedTPCSignal); + fRegistry.fill(HIST("Track/hTPCNsigmaEl"), track.tpcInnerParam(), track.tpcNSigmaEl()); + fRegistry.fill(HIST("Track/hTPCNsigmaPi"), track.tpcInnerParam(), track.tpcNSigmaPi()); + + fRegistry.fill(HIST("Track/hChi2TOF"), track.tofChi2()); + fRegistry.fill(HIST("Track/hTOFbeta"), track.p(), track.beta()); + fRegistry.fill(HIST("Track/hTOFNsigmaEl"), track.p(), track.tofNSigmaEl()); + fRegistry.fill(HIST("Track/hTOFNsigmaPi"), track.p(), track.tofNSigmaPi()); + + fRegistry.fill(HIST("Track/hNclsITS"), track.itsNCls()); + fRegistry.fill(HIST("Track/hChi2ITS"), track.itsChi2NCl()); + fRegistry.fill(HIST("Track/hITSClusterMap"), track.itsClusterMap()); + + int totalClusterSize = 0, nl = 0; + for (unsigned int layer = 0; layer < 7; layer++) { // o2-linter: disable=magic-number (number of ITS layers) + int clusterSizePerLayer = track.itsClsSizeInLayer(layer); + if (clusterSizePerLayer > 0) { + nl++; + } + totalClusterSize += clusterSizePerLayer; + } + + int totalClusterSizeIb = 0, nl_ib = 0; + for (unsigned int layer = 0; layer < 3; layer++) { // o2-linter: disable=magic-number (number of inner barrel layers) + int clusterSizePerLayer = track.itsClsSizeInLayer(layer); + if (clusterSizePerLayer > 0) { + nl_ib++; + } + totalClusterSizeIb += clusterSizePerLayer; + } + + int totalClusterSizeOb = 0, nlOb = 0; + for (unsigned int layer = 3; layer < 7; layer++) { // o2-linter: disable=magic-number (number of outer barrel layers) + int clusterSizePerLayer = track.itsClsSizeInLayer(layer); + if (clusterSizePerLayer > 0) { + nlOb++; + } + totalClusterSizeOb += clusterSizePerLayer; + } + fRegistry.fill(HIST("Track/hMeanClusterSizeITS"), track.p(), static_cast(totalClusterSize) / static_cast(nl) * std::cos(std::atan(track.tgl()))); + fRegistry.fill(HIST("Track/hMeanClusterSizeITSib"), track.p(), static_cast(totalClusterSizeIb) / static_cast(nl_ib) * std::cos(std::atan(track.tgl()))); + fRegistry.fill(HIST("Track/hMeanClusterSizeITSob"), track.p(), static_cast(totalClusterSizeOb) / static_cast(nlOb) * std::cos(std::atan(track.tgl()))); + } + + template + void fillTrackTable(TCollision const& collision, TTrack const& track) + { + emprimaryelectrons(collision.globalIndex(), track.globalIndex(), track.sign(), + track.pt(), track.eta(), track.phi(), track.dcaXY(), track.dcaZ(), track.cYY(), track.cZY(), track.cZZ(), + track.tpcNClsFindable(), track.tpcNClsFindableMinusFound(), track.tpcNClsFindableMinusCrossedRows(), track.tpcNClsShared(), + track.tpcChi2NCl(), track.tpcInnerParam(), + track.tpcSignal(), track.tpcNSigmaEl(), track.tpcNSigmaPi(), + track.beta(), track.tofNSigmaEl(), + track.itsClusterSizes(), track.itsChi2NCl(), track.tofChi2(), track.detectorMap()); + + if constexpr (isMC) { + emprimaryelectronsDeDxMC(track.mcTunedTPCSignal()); + } + } + + std::vector acceptedPosTrackIds_per_collision; + std::vector acceptedNegTrackIds_per_collision; + // Filter trackFilter = minpt < o2::aod::track::pt && nabs(o2::aod::track::eta) < maxeta && o2::aod::track::itsChi2NCl < maxchi2its && ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::ITS) == true && nabs(o2::aod::track::dcaXY) < dca_xy_max && nabs(o2::aod::track::dcaZ) < dca_z_max; + // Filter trackFilter + // using MyFilteredTracks = soa::Filtered; + o2::framework::Partition posTracks = o2::aod::track::signed1Pt > 0.f; + o2::framework::Partition negTracks = o2::aod::track::signed1Pt < 0.f; + + std::unordered_map mapCollisionTime; + std::unordered_map mapCollisionTimeError; + + std::map, float> mapTOFNsigmaReassociated; // map pair(collisionId, trackId) -> tof n sigma + std::map, float> mapTOFBetaReassociated; // map pair(collisionId, trackId) -> tof beta + + // ---------- for data ---------- + void processRec(MyCollisions const& collisions, MyBCs const& bcs, MyTracks const& tracks, TV0PhotonsKF const& v0photons, o2::aod::TrackAssoc const& trackIndices) + { + initCCDB(bcs.iteratorAt(0)); + mTOFResponse->processSetup(bcs.iteratorAt(0)); + + for (const auto& track : tracks) { + mapCollisionTime.try_emplace(track.collisionId(), track.tofEvTime()); + mapCollisionTimeError.try_emplace(track.collisionId(), track.tofEvTimeErr()); + } + calculateTOFNSigmaWithReassociation(collisions, bcs, tracks, trackIndices); + + for (const auto& collision : collisions) { + if (!collision.isSelected()) { + continue; + } + + auto varFillingMode = static_cast(fillingMode.value); + + const auto& v0photons_per_coll = v0photons.sliceBy(perCol_pcm, collision.globalIndex()); + const auto& posTracks_per_coll = posTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); + const auto& negTracks_per_coll = negTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); + const auto& slicedTracks = tracks.sliceBy(perTracksCollision, collision.globalIndex()); + acceptedPosTrackIds_per_collision.reserve(posTracks_per_coll.size()); + acceptedNegTrackIds_per_collision.reserve(negTracks_per_coll.size()); + + auto iterEp = tracks.begin(); + auto iterEm = tracks.begin(); + auto iterTrack = tracks.begin(); + auto iterPhoton = v0photons.begin(); + + switch (varFillingMode) { + case enumFillingMode::SingleTrack: + fillTrackInfo(collision, slicedTracks); + break; + case enumFillingMode::EpEmPairs: + fillPairInfo(collision, posTracks_per_coll, negTracks_per_coll); // ULS + if (fillLS) { + fillPairInfo(collision, posTracks_per_coll, posTracks_per_coll); // LS++ + fillPairInfo(collision, negTracks_per_coll, negTracks_per_coll); // LS-- + } + if ((!acceptedPosTrackIds_per_collision.empty() && !acceptedNegTrackIds_per_collision.empty())) { + for (const auto& posId : acceptedPosTrackIds_per_collision) { + iterEp.setCursor(posId); + fillTrackTable(collision, iterEp); + } + for (const auto& eleId : acceptedNegTrackIds_per_collision) { + iterEm.setCursor(eleId); + fillTrackTable(collision, iterEm); + } + } + break; + case enumFillingMode::EpEmPairsAndPhoton: + std::set tracksToFill; + + fillPairInfo(collision, posTracks_per_coll, negTracks_per_coll); // ULS + if (fillLS) { + fillPairInfo(collision, posTracks_per_coll, posTracks_per_coll); // LS++ + fillPairInfo(collision, negTracks_per_coll, negTracks_per_coll); // LS-- + } + if (v0photons_per_coll.size() >= 1 && ((!acceptedPosTrackIds_per_collision.empty() && !acceptedNegTrackIds_per_collision.empty()))) { + if (!fillWithEtaMassCut) { + for (const auto& posId : acceptedPosTrackIds_per_collision) { + iterEp.setCursor(posId); + fillTrackTable(collision, iterEp); + } + for (const auto& eleId : acceptedNegTrackIds_per_collision) { + iterEm.setCursor(eleId); + fillTrackTable(collision, iterEm); + } + } else { + for (const auto& posId : acceptedPosTrackIds_per_collision) { + iterEp.setCursor(posId); + ROOT::Math::PtEtaPhiMVector vEp(iterEp.pt(), iterEp.eta(), iterEp.phi(), o2::constants::physics::MassElectron); + for (const auto& eleId : acceptedNegTrackIds_per_collision) { + iterEm.setCursor(eleId); + ROOT::Math::PtEtaPhiMVector vEm(iterEm.pt(), iterEm.eta(), iterEm.phi(), o2::constants::physics::MassElectron); + for (const auto& photonId : v0photons_per_coll) { + iterPhoton.setCursor(photonId.globalIndex()); + ROOT::Math::PtEtaPhiMVector vPhoton(iterPhoton.pt(), iterPhoton.eta(), iterPhoton.phi(), o2::constants::physics::MassPhoton); + ROOT::Math::PtEtaPhiMVector vTotal = vEp + vEm + vPhoton; + + if (vTotal.M() < minMeegamma || vTotal.M() > maxMeegamma) { // don't store + continue; + } + tracksToFill.insert(posId); // store independently + tracksToFill.insert(eleId); + } + } + } + } + } + if (fillWithEtaMassCut) { + for (const auto& trackId : tracksToFill) { + iterTrack.setCursor(trackId); + fillTrackTable(collision, iterTrack); + } + } + break; + } + + acceptedPosTrackIds_per_collision.clear(); + acceptedNegTrackIds_per_collision.clear(); + } // end of collision loop + mapCollisionTime.clear(); + mapCollisionTimeError.clear(); + mapTOFNsigmaReassociated.clear(); + mapTOFBetaReassociated.clear(); + } + PROCESS_SWITCH(skimmerPrimaryElectronFromDalitzEE, processRec, "process reconstructed info only", false); // standalone + + // void processRec_SWT(MyCollisionsWithSWT const& collisions, MyBCs const& bcs, MyTracks const& tracks, aod::V0PhotonsKF const& v0photons, aod::TrackAssoc const& trackIndices) + // { + // initCCDB(bcs.iteratorAt(0)); + + // mTOFResponse->processSetup(bcs.iteratorAt(0)); + // for (const auto& track : tracks) { + // mapCollisionTime.try_emplace(track.collisionId(), track.tofEvTime()); + // mapCollisionTimeError.try_emplace(track.collisionId(), track.tofEvTimeErr()); + // } + // calculateTOFNSigmaWithReassociation(collisions, bcs, tracks, trackIndices); + + // for (const auto& collision : collisions) { + // if (!collision.isSelected()) { + // continue; + // } + + // if (collision.swtaliastmp_raw() == 0) { + // continue; + // } + + // const auto& v0photons_per_coll = v0photons.sliceBy(perCol_pcm, collision.globalIndex()); + // const auto& posTracks_per_coll = posTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); + // const auto& negTracks_per_coll = negTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); + // const auto& slicedTracks = tracks.sliceBy(perTracksCollision, collision.globalIndex()); + // acceptedPosTrackIds_per_collision.reserve(posTracks_per_coll.size()); + // acceptedNegTrackIds_per_collision.reserve(negTracks_per_coll.size()); + + // if(!fillWithPairs){ + // fillTrackInfo(collision, slicedTracks); + // }else{ + // fillPairInfo(collision, posTracks_per_coll, negTracks_per_coll); // ULS + // if (fillLS) { + // fillPairInfo(collision, posTracks_per_coll, posTracks_per_coll); // LS++ + // fillPairInfo(collision, negTracks_per_coll, negTracks_per_coll); // LS-- + // } + + // if ((v0photons_per_coll.size() >= 1 && !acceptedPosTrackIds_per_collision.empty() && !acceptedNegTrackIds_per_collision.empty()) || (acceptedPosTrackIds_per_collision.size() >= 2 && acceptedNegTrackIds_per_collision.size() >= 2)) { + // // LOGF(info, "v0photons_per_coll.size() = %d, acceptedPosTrackIds_per_collision.size() = %d, acceptedNegTrackIds_per_collision.size() = %d", v0photons_per_coll.size(), acceptedPosTrackIds_per_collision.size(), acceptedNegTrackIds_per_collision.size()); + // for (const auto& posId : acceptedPosTrackIds_per_collision) { + // const auto& pos = tracks.rawIteratorAt(posId); + // fillTrackTable(collision, pos); + // } + // for (const auto& eleId : acceptedNegTrackIds_per_collision) { + // const auto& ele = tracks.rawIteratorAt(eleId); + // fillTrackTable(collision, ele); + // } + // } + // } + + // acceptedPosTrackIds_per_collision.clear(); + // acceptedNegTrackIds_per_collision.clear(); + + // } // end of collision loop + // } + // PROCESS_SWITCH(skimmerPrimaryElectronFromDalitzEE, processRec_SWT, "process reconstructed info with CEFP", false); // with cefp + + // using MyFilteredTracksMC = soa::Filtered; + o2::framework::Partition posTracksMC = o2::aod::track::signed1Pt > 0.f; + o2::framework::Partition negTracksMC = o2::aod::track::signed1Pt < 0.f; + // ---------- for MC ---------- + void processMC(MyCollisionsMC const& collisions, o2::aod::McCollisions const&, MyBCs const& bcs, MyTracksMC const& tracks, TV0PhotonsKF const& v0photons, o2::aod::TrackAssoc const& trackIndices, o2::aod::McParticles const&) + { + uint64_t nCollisSel = 0; + uint64_t nNoMcColl = 0; + uint64_t nProcessedCollisions = 0; + uint64_t nCheckTrack = 0; + + initCCDB(bcs.iteratorAt(0)); + + mTOFResponse->processSetup(bcs.iteratorAt(0)); + for (const auto& track : tracks) { + mapCollisionTime.try_emplace(track.collisionId(), track.tofEvTime()); + mapCollisionTimeError.try_emplace(track.collisionId(), track.tofEvTimeErr()); + } + calculateTOFNSigmaWithReassociation(collisions, bcs, tracks, trackIndices); + + for (const auto& collision : collisions) { + if (!collision.has_mcCollision()) { + nNoMcColl++; + continue; + } + + if (!collision.isSelected()) { + nCollisSel++; + continue; + } + nProcessedCollisions++; + + auto varFillingMode = static_cast(fillingMode.value); + + const auto& v0photons_per_coll = v0photons.sliceBy(perCol_pcm, collision.globalIndex()); + const auto& posTracks_per_coll = posTracksMC->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); + const auto& negTracks_per_coll = negTracksMC->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); + const auto& slicedTracks = tracks.sliceBy(perTracksCollision, collision.globalIndex()); + acceptedPosTrackIds_per_collision.reserve(posTracks_per_coll.size()); + acceptedNegTrackIds_per_collision.reserve(negTracks_per_coll.size()); + + auto iterEp = tracks.begin(); + auto iterEm = tracks.begin(); + auto iterTrack = tracks.begin(); + auto iterPhoton = v0photons.begin(); + + switch (varFillingMode) { + case enumFillingMode::SingleTrack: + fillTrackInfo(collision, slicedTracks); + break; + case enumFillingMode::EpEmPairs: + fillPairInfo(collision, posTracks_per_coll, negTracks_per_coll); // ULS + if (fillLS) { + fillPairInfo(collision, posTracks_per_coll, posTracks_per_coll); // LS++ + fillPairInfo(collision, negTracks_per_coll, negTracks_per_coll); // LS-- + } + if ((!acceptedPosTrackIds_per_collision.empty() && !acceptedNegTrackIds_per_collision.empty())) { + for (const auto& posId : acceptedPosTrackIds_per_collision) { + iterEp.setCursor(posId); + fillTrackTable(collision, iterEp); + } + for (const auto& eleId : acceptedNegTrackIds_per_collision) { + iterEm.setCursor(eleId); + fillTrackTable(collision, iterEm); + } + } + break; + case enumFillingMode::EpEmPairsAndPhoton: + std::set tracksToFill; + + fillPairInfo(collision, posTracks_per_coll, negTracks_per_coll); // ULS + if (fillLS) { + fillPairInfo(collision, posTracks_per_coll, posTracks_per_coll); // LS++ + fillPairInfo(collision, negTracks_per_coll, negTracks_per_coll); // LS-- + } + if (v0photons_per_coll.size() >= 1 && ((!acceptedPosTrackIds_per_collision.empty() && !acceptedNegTrackIds_per_collision.empty()))) { + if (!fillWithEtaMassCut) { + for (const auto& posId : acceptedPosTrackIds_per_collision) { + iterEp.setCursor(posId); + fillTrackTable(collision, iterEp); + } + for (const auto& eleId : acceptedNegTrackIds_per_collision) { + iterEm.setCursor(eleId); + fillTrackTable(collision, iterEm); + } + } else { + for (const auto& posId : acceptedPosTrackIds_per_collision) { + iterEp.setCursor(posId); + ROOT::Math::PtEtaPhiMVector vEp(iterEp.pt(), iterEp.eta(), iterEp.phi(), o2::constants::physics::MassElectron); + for (const auto& eleId : acceptedNegTrackIds_per_collision) { + iterEm.setCursor(eleId); + ROOT::Math::PtEtaPhiMVector vEm(iterEm.pt(), iterEm.eta(), iterEm.phi(), o2::constants::physics::MassElectron); + for (const auto& photonId : v0photons_per_coll) { + iterPhoton.setCursor(photonId.globalIndex()); + ROOT::Math::PtEtaPhiMVector vPhoton(iterPhoton.pt(), iterPhoton.eta(), iterPhoton.phi(), o2::constants::physics::MassPhoton); + ROOT::Math::PtEtaPhiMVector vTotal = vEp + vEm + vPhoton; + + if (vTotal.M() < minMeegamma || vTotal.M() > maxMeegamma) { // don't store + continue; + } + tracksToFill.insert(posId); // store independently + tracksToFill.insert(eleId); + } + } + } + } + } + if (fillWithEtaMassCut) { + for (const auto& trackId : tracksToFill) { + iterTrack.setCursor(trackId); + fillTrackTable(collision, iterTrack); + } + } + break; + } + + acceptedPosTrackIds_per_collision.clear(); + acceptedNegTrackIds_per_collision.clear(); + } // end of collision loop + mapCollisionTime.clear(); + mapCollisionTimeError.clear(); + mapTOFNsigmaReassociated.clear(); + mapTOFBetaReassociated.clear(); + + LOG(info) << "Total collisions: " << collisions.size(); + LOG(info) << "No MC collision: " << nNoMcColl; + LOG(info) << "Rejected by selection: " << nCollisSel; + LOG(info) << "Processed collisions: " << nProcessedCollisions; + LOG(info) << "Invalid track: " << nCheckTrack; + } + PROCESS_SWITCH(skimmerPrimaryElectronFromDalitzEE, processMC, "process reconstructed and MC info ", true); +}; diff --git a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEETmpTable.cxx b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEETmpTable.cxx new file mode 100644 index 00000000000..4587e8d6975 --- /dev/null +++ b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEETmpTable.cxx @@ -0,0 +1,28 @@ +// Copyright 2019-2020 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 skimmerPrimaryElectronFromDalitzEETmpTable.cxx +/// \brief write electrons for Dalitz into a temporary table that is then used for event skimming +/// \author joshua.konig@cern.ch + +#include "PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h" + +using namespace o2::framework; +using namespace o2::aod; +using namespace o2::framework::expressions; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + o2::pid::tof::TOFResponseImpl::metadataInfo.initMetadata(context); + + return WorkflowSpec{ + adaptAnalysisTask>(context, TaskName{"skimmer-primary-electron-from-dalitzee-tmptable"})}; +}