From 52f02f485e886af8f3643766cb1b9c1c4f05eb1a Mon Sep 17 00:00:00 2001 From: jryu Date: Wed, 22 Jul 2026 16:27:12 +0900 Subject: [PATCH] Add UPC jet QA task --- PWGJE/Tasks/CMakeLists.txt | 4 + PWGJE/Tasks/UPCJetQATables.h | 243 ++++++++++++ PWGJE/Tasks/jetUpcQa.cxx | 712 +++++++++++++++++++++++++++++++++++ PWGJE/Tasks/o2linter_config | 15 + 4 files changed, 974 insertions(+) create mode 100644 PWGJE/Tasks/UPCJetQATables.h create mode 100644 PWGJE/Tasks/jetUpcQa.cxx create mode 100644 PWGJE/Tasks/o2linter_config diff --git a/PWGJE/Tasks/CMakeLists.txt b/PWGJE/Tasks/CMakeLists.txt index 3fa812e1308..6a3e2c8bd9a 100644 --- a/PWGJE/Tasks/CMakeLists.txt +++ b/PWGJE/Tasks/CMakeLists.txt @@ -444,4 +444,8 @@ if(FastJet_FOUND) SOURCES jetHadronsPid.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(jet-upc-qa + SOURCES jetUpcQa.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils O2Physics::SGCutParHolder + COMPONENT_NAME Analysis) endif() diff --git a/PWGJE/Tasks/UPCJetQATables.h b/PWGJE/Tasks/UPCJetQATables.h new file mode 100644 index 00000000000..04206df1997 --- /dev/null +++ b/PWGJE/Tasks/UPCJetQATables.h @@ -0,0 +1,243 @@ +// 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. + +#ifndef PWGJE_TASKS_UPCJETQATABLES_H_ +#define PWGJE_TASKS_UPCJETQATABLES_H_ + +#include + +namespace o2::aod +{ + +// ============================================================ +// DATA tables +// ============================================================ +namespace upcjetevent +{ +DECLARE_SOA_COLUMN(GapSide, gapSide, int); +DECLARE_SOA_COLUMN(PosZ, posZ, float); +DECLARE_SOA_COLUMN(CentFT0M, centFT0M, float); +DECLARE_SOA_COLUMN(EnergyCommonZNA, energyCommonZNA, float); +DECLARE_SOA_COLUMN(EnergyCommonZNC, energyCommonZNC, float); +DECLARE_SOA_COLUMN(TimeZNA, timeZNA, float); +DECLARE_SOA_COLUMN(TimeZNC, timeZNC, float); +DECLARE_SOA_COLUMN(NeutronClass, neutronClass, int); +DECLARE_SOA_COLUMN(IsGapTagged, isGapTagged, bool); +DECLARE_SOA_COLUMN(IsZDCTagged, isZDCTagged, bool); +} // namespace upcjetevent + +DECLARE_SOA_TABLE(UpcJetEvents, "AOD", "UPCJETEVENT", + upcjetevent::GapSide, + upcjetevent::PosZ, + upcjetevent::CentFT0M, + upcjetevent::EnergyCommonZNA, + upcjetevent::EnergyCommonZNC, + upcjetevent::TimeZNA, + upcjetevent::TimeZNC, + upcjetevent::NeutronClass, + upcjetevent::IsGapTagged, + upcjetevent::IsZDCTagged); +using UpcJetEvent = UpcJetEvents::iterator; + +namespace upcjet +{ +DECLARE_SOA_INDEX_COLUMN(UpcJetEvent, upcJetEvent); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Area, area, float); +DECLARE_SOA_COLUMN(NConstituents, nConstituents, int); +} // namespace upcjet + +DECLARE_SOA_TABLE(UpcJets, "AOD", "UPCJET", + upcjet::UpcJetEventId, + upcjet::Pt, + upcjet::Eta, + upcjet::Phi, + upcjet::Area, + upcjet::NConstituents); +using UpcJet = UpcJets::iterator; + +namespace upctrack +{ +DECLARE_SOA_INDEX_COLUMN_FULL(UpcJetEvent, upcJetEvent, int32_t, UpcJetEvents, ""); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +} // namespace upctrack + +DECLARE_SOA_TABLE(UpcTracks, "AOD", "UPCTRACK", + upctrack::UpcJetEventId, + upctrack::Pt, + upctrack::Eta, + upctrack::Phi); +using UpcTrack = UpcTracks::iterator; + +namespace upcjettrack +{ +DECLARE_SOA_INDEX_COLUMN_FULL(UpcJet, upcJet, int32_t, UpcJets, ""); +DECLARE_SOA_INDEX_COLUMN_FULL(UpcJetEvent, upcJetEvent, int32_t, UpcJetEvents, ""); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +} // namespace upcjettrack + +DECLARE_SOA_TABLE(UpcJetTracks, "AOD", "UPCJETTRACK", + upcjettrack::UpcJetId, + upcjettrack::UpcJetEventId, + upcjettrack::Pt, + upcjettrack::Eta, + upcjettrack::Phi); +using UpcJetTrack = UpcJetTracks::iterator; + +// ============================================================ +// MCD tables +// ============================================================ +namespace upcjeteventmcd +{ +DECLARE_SOA_COLUMN(GapSide, gapSide, int); +DECLARE_SOA_COLUMN(PosZ, posZ, float); +DECLARE_SOA_COLUMN(CentFT0M, centFT0M, float); +DECLARE_SOA_COLUMN(EnergyCommonZNA, energyCommonZNA, float); +DECLARE_SOA_COLUMN(EnergyCommonZNC, energyCommonZNC, float); +DECLARE_SOA_COLUMN(TimeZNA, timeZNA, float); +DECLARE_SOA_COLUMN(TimeZNC, timeZNC, float); +DECLARE_SOA_COLUMN(NeutronClass, neutronClass, int); +DECLARE_SOA_COLUMN(IsGapTagged, isGapTagged, bool); +DECLARE_SOA_COLUMN(IsZDCTagged, isZDCTagged, bool); +} // namespace upcjeteventmcd + +DECLARE_SOA_TABLE(UpcJetEventsMCD, "AOD", "UPCJETEVENTMCD", + upcjeteventmcd::GapSide, + upcjeteventmcd::PosZ, + upcjeteventmcd::CentFT0M, + upcjeteventmcd::EnergyCommonZNA, + upcjeteventmcd::EnergyCommonZNC, + upcjeteventmcd::TimeZNA, + upcjeteventmcd::TimeZNC, + upcjeteventmcd::NeutronClass, + upcjeteventmcd::IsGapTagged, + upcjeteventmcd::IsZDCTagged); +using UpcJetEventMCD = UpcJetEventsMCD::iterator; + +namespace upctrackmcd +{ +DECLARE_SOA_INDEX_COLUMN_FULL(UpcJetEventMCD, upcJetEventMCD, int32_t, UpcJetEventsMCD, ""); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +} // namespace upctrackmcd + +DECLARE_SOA_TABLE(UpcTracksMCD, "AOD", "UPCTRACKMCD", + upctrackmcd::UpcJetEventMCDId, + upctrackmcd::Pt, + upctrackmcd::Eta, + upctrackmcd::Phi); +using UpcTrackMCD = UpcTracksMCD::iterator; + +namespace upcjetmcd +{ +DECLARE_SOA_INDEX_COLUMN_FULL(UpcJetEventMCD, upcJetEventMCD, int32_t, UpcJetEventsMCD, ""); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Area, area, float); +DECLARE_SOA_COLUMN(NConstituents, nConstituents, int); +} // namespace upcjetmcd + +DECLARE_SOA_TABLE(UpcJetsMCD, "AOD", "UPCJETMCD", + upcjetmcd::UpcJetEventMCDId, + upcjetmcd::Pt, + upcjetmcd::Eta, + upcjetmcd::Phi, + upcjetmcd::Area, + upcjetmcd::NConstituents); +using UpcJetMCD = UpcJetsMCD::iterator; + +namespace upcjettrackmcd +{ +DECLARE_SOA_INDEX_COLUMN_FULL(UpcJetMCD, upcJetMCD, int32_t, UpcJetsMCD, ""); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +} // namespace upcjettrackmcd + +DECLARE_SOA_TABLE(UpcJetTracksMCD, "AOD", "UPCJETTRACKMCD", + upcjettrackmcd::UpcJetMCDId, + upcjettrackmcd::Pt, + upcjettrackmcd::Eta, + upcjettrackmcd::Phi); +using UpcJetTrackMCD = UpcJetTracksMCD::iterator; + +// ============================================================ +// MCP tables +// ============================================================ +namespace upcjeteventmcp +{ +DECLARE_SOA_COLUMN(PosZ, posZ, float); +} // namespace upcjeteventmcp + +DECLARE_SOA_TABLE(UpcJetEventsMCP, "AOD", "UPCJETEVENTMCP", + upcjeteventmcp::PosZ); +using UpcJetEventMCP = UpcJetEventsMCP::iterator; + +namespace upctrackmcp +{ +DECLARE_SOA_INDEX_COLUMN_FULL(UpcJetEventMCP, upcJetEventMCP, int32_t, UpcJetEventsMCP, ""); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +} // namespace upctrackmcp + +DECLARE_SOA_TABLE(UpcTracksMCP, "AOD", "UPCTRACKMCP", + upctrackmcp::UpcJetEventMCPId, + upctrackmcp::Pt, + upctrackmcp::Eta, + upctrackmcp::Phi); +using UpcTrackMCP = UpcTracksMCP::iterator; + +namespace upcjetmcp +{ +DECLARE_SOA_INDEX_COLUMN_FULL(UpcJetEventMCP, upcJetEventMCP, int32_t, UpcJetEventsMCP, ""); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Area, area, float); +DECLARE_SOA_COLUMN(NConstituents, nConstituents, int); +} // namespace upcjetmcp + +DECLARE_SOA_TABLE(UpcJetsMCP, "AOD", "UPCJETMCP", + upcjetmcp::UpcJetEventMCPId, + upcjetmcp::Pt, + upcjetmcp::Eta, + upcjetmcp::Phi, + upcjetmcp::Area, + upcjetmcp::NConstituents); +using UpcJetMCP = UpcJetsMCP::iterator; + +namespace upcjettrackmcp +{ +DECLARE_SOA_INDEX_COLUMN_FULL(UpcJetMCP, upcJetMCP, int32_t, UpcJetsMCP, ""); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +} // namespace upcjettrackmcp + +DECLARE_SOA_TABLE(UpcJetTracksMCP, "AOD", "UPCJETTRACKMCP", + upcjettrackmcp::UpcJetMCPId, + upcjettrackmcp::Pt, + upcjettrackmcp::Eta, + upcjettrackmcp::Phi); +using UpcJetTrackMCP = UpcJetTracksMCP::iterator; + +} // namespace o2::aod + +#endif // PWGJE_TASKS_UPCJETQATABLES_H_ diff --git a/PWGJE/Tasks/jetUpcQa.cxx b/PWGJE/Tasks/jetUpcQa.cxx new file mode 100644 index 00000000000..f871030b6c7 --- /dev/null +++ b/PWGJE/Tasks/jetUpcQa.cxx @@ -0,0 +1,712 @@ +// 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. + +#include "PWGJE/Tasks/UPCJetQATables.h" +#include "PWGUD/Core/SGCutParHolder.h" +#include "PWGUD/Core/SGSelector.h" + +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +/// Stand-alone QA task for validating UPC event tagging for charged-jet studies. +/// The tagging and QA developed here are intended to guide a future integration +/// of the required UPC information into the PWGJE derived-data workflow. +struct JetUpcQa { + static constexpr int RequiredSingleGapA = 1; + static constexpr int RequiredSingleGapC = 2; + static constexpr int RequiredDoubleGap = 3; + + // Output tables + Produces upcJetEventTable; + Produces upcJetTable; + Produces upcJetTrackTable; + + Produces upcJetEventTableMCD; + Produces upcJetTableMCD; + Produces upcJetTrackTableMCD; + + Produces upcJetEventTableMCP; + Produces upcJetTableMCP; + Produces upcJetTrackTableMCP; + + Produces upcTrackTable; + Produces upcTrackTableMCD; + Produces upcTrackTableMCP; + + // Type aliases + using BCsWithSel = soa::Join; + using CollisionsData = soa::Join; + using TracksData = soa::Join; + using CollisionsMC = soa::Join; + using TracksMC = soa::Join; + + // Preslices + // Preslice tracksPerCollision = aod::track::collisionId; + // Preslice tracksPerCollisionMC = aod::track::collisionId; + + // SGSelector buffers + std::vector amplitudesFV0; + std::vector amplitudesFT0A; + std::vector amplitudesFT0C; + std::vector amplitudesFDDA; + std::vector amplitudesFDDC; + + // Configurables + Configurable cfgFT0AMax{"cfgFT0AMax", 100.f, "FT0A amplitude threshold"}; + Configurable cfgFT0CMax{"cfgFT0CMax", 50.f, "FT0C amplitude threshold"}; + Configurable cfgFV0AMax{"cfgFV0AMax", 100.f, "FV0A amplitude threshold"}; + Configurable cfgFDDAMax{"cfgFDDAMax", -1.f, "FDDA amplitude (-1 = ignore)"}; + Configurable cfgFDDCMax{"cfgFDDCMax", -1.f, "FDDC amplitude (-1 = ignore)"}; + Configurable cfgFITTime{"cfgFITTime", 4.f, "FIT time window (ns)"}; + Configurable cfgMinNBCs{"cfgMinNBCs", 7, "Minimum number of BCs"}; + Configurable cfgNDtcoll{"cfgNDtcoll", 1, "Time resolution multiplier for compatible BCs"}; + Configurable cfgMinNTracks{"cfgMinNTracks", 0, "Min tracks"}; + Configurable cfgMaxNTracks{"cfgMaxNTracks", 100, "Max tracks"}; + + Configurable cfgTrackPtMin{"cfgTrackPtMin", 0.15f, "Min track pT (GeV/c)"}; + Configurable cfgTrackEtaMax{"cfgTrackEtaMax", 0.9f, "Max track |eta|"}; + Configurable cfgTrackSelection{"cfgTrackSelection", "kinematicsOnly", "Reconstructed-track selection: kinematicsOnly, globalTracks, or udDefault"}; + Configurable cfgTrackDCAZMax{"cfgTrackDCAZMax", 2.f, "Maximum |DCAz| for udDefault track selection (cm)"}; + Configurable cfgTrackDCAXYMax{"cfgTrackDCAXYMax", 0.f, "Maximum |DCAxy| for udDefault (cm); 0 uses the pT-dependent UD cut"}; + Configurable cfgTrackTPCChi2NClMax{"cfgTrackTPCChi2NClMax", 4.f, "Maximum TPC chi2 per cluster for udDefault"}; + Configurable cfgTrackTPCNClsFindableMin{"cfgTrackTPCNClsFindableMin", 70, "Minimum findable TPC clusters for udDefault"}; + Configurable cfgTrackITSChi2NClMax{"cfgTrackITSChi2NClMax", 36.f, "Maximum ITS chi2 per cluster for udDefault"}; + Configurable cfgJetR{"cfgJetR", 0.4f, "Jet radius"}; + Configurable cfgJetPtMin{"cfgJetPtMin", 5.0f, "Min jet pT (GeV/c)"}; + Configurable cfgJetEtaMax{"cfgJetEtaMax", 0.5f, "Max jet |eta|"}; + Configurable cfgZDCEnergyCut{"cfgZDCEnergyCut", 10.f, "ZDC common energy threshold for Xn tagging"}; + Configurable cfgZDCTimeCut{"cfgZDCTimeCut", 2.f, "ZDC time window for Xn tagging (ns)"}; + Configurable cfgUseGapTagging{"cfgUseGapTagging", true, "Require SGSelector gap tag for event selection"}; + Configurable cfgUseZDCTagging{"cfgUseZDCTagging", false, "Require ZDC neutron tag for event selection"}; + Configurable cfgRequiredGapSide{"cfgRequiredGapSide", 0, "Required gap side when gap tagging is enabled (0 = any selected gap, 1 = SingleGapA, 2 = SingleGapC, 3 = DoubleGap)"}; + Configurable cfgRequiredNeutronClass{"cfgRequiredNeutronClass", 0, "Required neutron class when ZDC tagging is enabled (0 = any valid class, 1 = XnXn, 2 = Xn0n, 3 = 0nXn, 4 = 0n0n)"}; + + struct ZDCTagInfo { + float energyCommonZNA = -999.f; + float energyCommonZNC = -999.f; + float timeZNA = -999.f; + float timeZNC = -999.f; + int neutronClass = 0; + }; + + struct GapSelectionInfo { + int gapSide = o2::aod::sgselector::NoGap; + ZDCTagInfo zdc; + }; + + SGSelector sgSelector; + SGCutParHolder sgCuts; + + HistogramRegistry registry{"registry"}; + enum class TrackSelectionMode { + KinematicsOnly, + GlobalTracks, + UDDefault + }; + TrackSelectionMode trackSelectionMode = TrackSelectionMode::KinematicsOnly; + + // Init + void init(InitContext&) + { + if (cfgTrackSelection.value == "kinematicsOnly") { + trackSelectionMode = TrackSelectionMode::KinematicsOnly; + } else if (cfgTrackSelection.value == "globalTracks") { + trackSelectionMode = TrackSelectionMode::GlobalTracks; + } else if (cfgTrackSelection.value == "udDefault") { + trackSelectionMode = TrackSelectionMode::UDDefault; + } else { + LOGP(fatal, "Unknown cfgTrackSelection '{}'. Choose kinematicsOnly, globalTracks, or udDefault.", cfgTrackSelection.value); + } + + sgCuts.SetNDtcoll(cfgNDtcoll); + sgCuts.SetMinNBCs(cfgMinNBCs); + sgCuts.SetNTracks(cfgMinNTracks, cfgMaxNTracks); + sgCuts.SetMaxFITtime(cfgFITTime); + sgCuts.SetFITAmpLimits({cfgFV0AMax, cfgFT0AMax, cfgFT0CMax, cfgFDDAMax, cfgFDDCMax}); + + // Event-level QA only + registry.add("hGapSide", "Gap Side (Data);Gap Side;Counts", {HistType::kTH1F, {{6, -1.5, 4.5}}}); + registry.add("hGapSideMCD", "Gap Side (MCD);Gap Side;Counts", {HistType::kTH1F, {{6, -1.5, 4.5}}}); + + registry.add("hPosZ", "Vertex Z (Data);z (cm);Counts", {HistType::kTH1F, {{100, -15., 15.}}}); + registry.add("hPosZMCD", "Vertex Z (MCD);z (cm);Counts", {HistType::kTH1F, {{100, -15., 15.}}}); + registry.add("hPosZMCP", "Vertex Z (MCP);z (cm);Counts", {HistType::kTH1F, {{100, -15., 15.}}}); + + registry.add("hCentralityFT0M", "Centrality FT0M (Data);Cent;Counts", {HistType::kTH1F, {{100, 0., 100.}}}); + registry.add("hCentMCD", "Centrality FT0M (MCD);Cent;Counts", {HistType::kTH1F, {{100, 0., 100.}}}); + + registry.add("hMCDFoundBC", "MCD has_foundBC;0=false 1=true;Counts", {HistType::kTH1F, {{2, -0.5, 1.5}}}); + registry.add("hMCPNCharged", "MCP N charged primaries;N;Counts", {HistType::kTH1F, {{200, -0.5, 199.5}}}); + + registry.add("hZNAEnergy", "ZNA common energy (Data);E;Counts", {HistType::kTH1F, {{400, -10., 390.}}}); + registry.add("hZNCEnergy", "ZNC common energy (Data);E;Counts", {HistType::kTH1F, {{400, -10., 390.}}}); + registry.add("hZNATime", "ZNA time (Data);t (ns);Counts", {HistType::kTH1F, {{200, -20., 20.}}}); + registry.add("hZNCTime", "ZNC time (Data);t (ns);Counts", {HistType::kTH1F, {{200, -20., 20.}}}); + registry.add("hNeutronClass", "Neutron class (Data);class;Counts", {HistType::kTH1F, {{5, -0.5, 4.5}}}); + registry.add("hZNAEnergyMCD", "ZNA common energy (MCD);E;Counts", {HistType::kTH1F, {{400, -10., 390.}}}); + registry.add("hZNCEnergyMCD", "ZNC common energy (MCD);E;Counts", {HistType::kTH1F, {{400, -10., 390.}}}); + registry.add("hZNATimeMCD", "ZNA time (MCD);t (ns);Counts", {HistType::kTH1F, {{200, -20., 20.}}}); + registry.add("hZNCTimeMCD", "ZNC time (MCD);t (ns);Counts", {HistType::kTH1F, {{200, -20., 20.}}}); + registry.add("hNeutronClassMCD", "Neutron class (MCD);class;Counts", {HistType::kTH1F, {{5, -0.5, 4.5}}}); + registry.add("hSelectionBits", "Selection bits (Data);0 all 1 gap 2 zdc 3 accepted;Counts", {HistType::kTH1F, {{4, -0.5, 3.5}}}); + registry.add("hSelectionBitsMCD", "Selection bits (MCD);0 all 1 gap 2 zdc 3 accepted;Counts", {HistType::kTH1F, {{4, -0.5, 3.5}}}); + } + + template + bool isSelectedTrack(TTrack const& track) const + { + if (std::abs(track.eta()) > cfgTrackEtaMax.value || track.pt() < cfgTrackPtMin.value) { + return false; + } + if (trackSelectionMode == TrackSelectionMode::KinematicsOnly) { + return true; + } + if (trackSelectionMode == TrackSelectionMode::GlobalTracks) { + return track.isGlobalTrack(); + } + if (!track.isPVContributor()) { + return false; + } + if (std::abs(track.dcaZ()) > cfgTrackDCAZMax.value) { + return false; + } + float maxDCAxy = cfgTrackDCAXYMax.value; + if (maxDCAxy <= 0.f) { + maxDCAxy = 0.0105f + 0.035f / std::pow(track.pt(), 1.1f); + } + if (std::abs(track.dcaXY()) > maxDCAxy) { + return false; + } + if (track.tpcChi2NCl() > cfgTrackTPCChi2NClMax.value || track.tpcNClsFindable() < cfgTrackTPCNClsFindableMin.value) { + return false; + } + if (track.itsChi2NCl() > cfgTrackITSChi2NClMax.value) { + return false; + } + return true; + } + + // Helper: event tagging and selection + bool isSelectedGap(int gapSide) const + { + if (cfgRequiredGapSide.value == RequiredSingleGapA) { + return gapSide == o2::aod::sgselector::SingleGapA; + } + if (cfgRequiredGapSide.value == RequiredSingleGapC) { + return gapSide == o2::aod::sgselector::SingleGapC; + } + if (cfgRequiredGapSide.value == RequiredDoubleGap) { + return gapSide == o2::aod::sgselector::DoubleGap; + } + return gapSide == o2::aod::sgselector::SingleGapA || + gapSide == o2::aod::sgselector::SingleGapC || + gapSide == o2::aod::sgselector::DoubleGap; + } + + int getNeutronClass(ZDCTagInfo const& zdc) + { + if (zdc.energyCommonZNA < 0.f || zdc.energyCommonZNC < 0.f) { + return 0; + } + + bool aXn = zdc.energyCommonZNA > cfgZDCEnergyCut && std::abs(zdc.timeZNA) < cfgZDCTimeCut; + bool cXn = zdc.energyCommonZNC > cfgZDCEnergyCut && std::abs(zdc.timeZNC) < cfgZDCTimeCut; + bool a0n = zdc.energyCommonZNA <= cfgZDCEnergyCut; + bool c0n = zdc.energyCommonZNC <= cfgZDCEnergyCut; + + if (aXn && cXn) { + return 1; // XnXn + } + if (aXn && c0n) { + return 2; // Xn0n + } + if (a0n && cXn) { + return 3; // 0nXn + } + if (a0n && c0n) { + return 4; // 0n0n + } + return 0; + } + + bool isSelectedZDC(ZDCTagInfo const& zdc) const + { + if (cfgRequiredNeutronClass.value > 0) { + return zdc.neutronClass == cfgRequiredNeutronClass.value; + } + return zdc.neutronClass > 0; + } + + bool isAcceptedEvent(bool isGapTagged, bool isZDCTagged) const + { + return (!cfgUseGapTagging.value || isGapTagged) && + (!cfgUseZDCTagging.value || isZDCTagged); + } + + void fillSelectionBits(bool isMCD, bool isGapTagged, bool isZDCTagged, bool isAccepted) + { + if (isMCD) { + registry.fill(HIST("hSelectionBitsMCD"), 0); + if (isGapTagged) { + registry.fill(HIST("hSelectionBitsMCD"), 1); + } + if (isZDCTagged) { + registry.fill(HIST("hSelectionBitsMCD"), 2); + } + if (isAccepted) { + registry.fill(HIST("hSelectionBitsMCD"), 3); + } + return; + } + + registry.fill(HIST("hSelectionBits"), 0); + if (isGapTagged) { + registry.fill(HIST("hSelectionBits"), 1); + } + if (isZDCTagged) { + registry.fill(HIST("hSelectionBits"), 2); + } + if (isAccepted) { + registry.fill(HIST("hSelectionBits"), 3); + } + } + + template + ZDCTagInfo getZDCTagInfo(TBC const& bc) + { + ZDCTagInfo zdcInfo; + if (!bc.has_zdc()) { + return zdcInfo; + } + + auto const& zdc = bc.zdc(); + zdcInfo.energyCommonZNA = zdc.energyCommonZNA(); + zdcInfo.energyCommonZNC = zdc.energyCommonZNC(); + zdcInfo.timeZNA = zdc.timeZNA(); + zdcInfo.timeZNC = zdc.timeZNC(); + zdcInfo.neutronClass = getNeutronClass(zdcInfo); + return zdcInfo; + } + + template + GapSelectionInfo getGapSide(TCollision const& collision, TBCs const& bcs) + { + amplitudesFV0.clear(); + amplitudesFT0A.clear(); + amplitudesFT0C.clear(); + amplitudesFDDA.clear(); + amplitudesFDDC.clear(); + + GapSelectionInfo selection; + + if (!collision.has_foundBC()) { + return selection; + } + + auto const bc = collision.template foundBC_as(); + auto const bcRange = udhelpers::compatibleBCs(collision, sgCuts.NDtcoll(), bcs, sgCuts.minNBCs()); + + auto const result = sgSelector.IsSelected(sgCuts, collision, bcRange, bc, + &litudesFV0, &litudesFT0A, &litudesFT0C, + &litudesFDDA, &litudesFDDC); + + selection.gapSide = result.value; + if (result.bc) { + selection.zdc = getZDCTagInfo(*result.bc); + } + + if (!isSelectedGap(selection.gapSide)) { + amplitudesFV0.clear(); + amplitudesFT0A.clear(); + amplitudesFT0C.clear(); + amplitudesFDDA.clear(); + amplitudesFDDC.clear(); + } + + return selection; + } + + // Helper: jet finding for detector-level tracks + template + void runJetFindingDet(TTracks const& tracks, + int evtIdx, + TJetTable& jetTable, + TTrkTable& trkTable) + { + std::vector fjInput; + fjInput.reserve(tracks.size()); + + for (auto const& t : tracks) { + if (!isSelectedTrack(t)) { + continue; + } + + fastjet::PseudoJet p; + p.reset_PtYPhiM(t.pt(), t.eta(), t.phi(), o2::constants::physics::MassPiPlus); + fjInput.push_back(p); + } + + if (fjInput.empty()) { + return; + } + + fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, cfgJetR); + fastjet::AreaDefinition areaDef(fastjet::active_area_explicit_ghosts, + fastjet::GhostedAreaSpec(cfgTrackEtaMax + cfgJetR)); + fastjet::ClusterSequenceArea cs(fjInput, jetDef, areaDef); + auto jets = fastjet::sorted_by_pt(cs.inclusive_jets(cfgJetPtMin)); + + for (auto const& jet : jets) { + if (std::abs(jet.eta()) > cfgJetEtaMax) { + continue; + } + + auto constituents = jet.constituents(); + jetTable(evtIdx, jet.pt(), jet.eta(), jet.phi_std(), + jet.area(), static_cast(constituents.size())); + int jetIdx = jetTable.lastIndex(); + + for (auto const& c : constituents) { + trkTable(jetIdx, c.pt(), c.eta(), c.phi_std()); + } + } + } + + // Helper: jet finding for particle-level MC + template + void runJetFindingMCP(aod::McParticles const& particles, + int evtIdx, + TJetTable& jetTable, + TTrkTable& trkTable) + { + std::vector fjInput; + int nCharged = 0; + + for (auto const& p : particles) { + if (!p.isPhysicalPrimary()) { + continue; + } + if (std::abs(p.eta()) > cfgTrackEtaMax) { + continue; + } + if (p.pt() < cfgTrackPtMin) { + continue; + } + + auto pdg = std::abs(p.pdgCode()); + bool charged = (pdg == kPiPlus || pdg == kKPlus || pdg == kProton || pdg == kElectron || pdg == kMuonMinus); + if (!charged) { + continue; + } + + fastjet::PseudoJet fpp; + fpp.reset_PtYPhiM(p.pt(), p.eta(), p.phi(), o2::constants::physics::MassPiPlus); + fjInput.push_back(fpp); + ++nCharged; + } + + registry.fill(HIST("hMCPNCharged"), nCharged); + + if (fjInput.empty()) { + return; + } + + fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, cfgJetR); + fastjet::AreaDefinition areaDef(fastjet::active_area_explicit_ghosts, + fastjet::GhostedAreaSpec(cfgTrackEtaMax + cfgJetR)); + fastjet::ClusterSequenceArea cs(fjInput, jetDef, areaDef); + auto jets = fastjet::sorted_by_pt(cs.inclusive_jets(cfgJetPtMin)); + + for (auto const& jet : jets) { + if (std::abs(jet.eta()) > cfgJetEtaMax) { + continue; + } + + auto constituents = jet.constituents(); + jetTable(evtIdx, jet.pt(), jet.eta(), jet.phi_std(), + jet.area(), static_cast(constituents.size())); + int jetIdx = jetTable.lastIndex(); + + for (auto const& c : constituents) { + trkTable(jetIdx, c.pt(), c.eta(), c.phi_std()); + } + } + } + + template + void fillEventTracksDet(TTracks const& tracks, int evtIdx, TTrackTable& trackTable) + { + int nAll = 0; + int nFilled = 0; + for (auto const& t : tracks) { + ++nAll; + if (!isSelectedTrack(t)) + continue; + trackTable(evtIdx, t.pt(), t.eta(), t.phi()); + ++nFilled; + } + LOGP(info, "fillEventTracksDet: evtIdx={} nAll={} nFilled={}", evtIdx, nAll, nFilled); + } + + template + void fillEventTracksMCP(aod::McParticles const& particles, int evtIdx, TTrackTable& trackTable) + { + for (auto const& p : particles) { + if (!p.isPhysicalPrimary()) + continue; + if (std::abs(p.eta()) > cfgTrackEtaMax) + continue; + if (p.pt() < cfgTrackPtMin) + continue; + + auto pdg = std::abs(p.pdgCode()); + bool charged = (pdg == kPiPlus || pdg == kKPlus || pdg == kProton || + pdg == kElectron || pdg == kMuonMinus); + if (!charged) + continue; + + trackTable(evtIdx, p.pt(), p.eta(), p.phi()); + } + } + + // Process: Data + void processData(CollisionsData::iterator const& collision, + BCsWithSel const& bcs, + aod::Zdcs const&, + aod::FT0s const&, aod::FV0As const&, aod::FDDs const&, + TracksData const& tracks) + { + auto gapInfo = getGapSide(collision, bcs); + int gapSide = gapInfo.gapSide; + bool isGapTagged = isSelectedGap(gapSide); + bool isZDCTagged = isSelectedZDC(gapInfo.zdc); + bool isAccepted = isAcceptedEvent(isGapTagged, isZDCTagged); + registry.fill(HIST("hGapSide"), gapSide); + registry.fill(HIST("hZNAEnergy"), gapInfo.zdc.energyCommonZNA); + registry.fill(HIST("hZNCEnergy"), gapInfo.zdc.energyCommonZNC); + registry.fill(HIST("hZNATime"), gapInfo.zdc.timeZNA); + registry.fill(HIST("hZNCTime"), gapInfo.zdc.timeZNC); + registry.fill(HIST("hNeutronClass"), gapInfo.zdc.neutronClass); + fillSelectionBits(false, isGapTagged, isZDCTagged, isAccepted); + + if (!isAccepted) { + return; + } + + registry.fill(HIST("hPosZ"), collision.posZ()); + registry.fill(HIST("hCentralityFT0M"), collision.centFT0M()); + + upcJetEventTable(gapSide, + collision.posZ(), + collision.centFT0M(), + gapInfo.zdc.energyCommonZNA, + gapInfo.zdc.energyCommonZNC, + gapInfo.zdc.timeZNA, + gapInfo.zdc.timeZNC, + gapInfo.zdc.neutronClass, + isGapTagged, + isZDCTagged); + int evtIdx = upcJetEventTable.lastIndex(); + + std::vector fjInput; + fjInput.reserve(256); + + for (auto const& t : tracks) { + if (t.collisionId() != collision.globalIndex()) { + continue; + } + if (!isSelectedTrack(t)) { + continue; + } + + // event-level track table + upcTrackTable(evtIdx, t.pt(), t.eta(), t.phi()); + + // jet finding input + fastjet::PseudoJet p; + p.reset_PtYPhiM(t.pt(), t.eta(), t.phi(), o2::constants::physics::MassPiPlus); + fjInput.push_back(p); + } + + if (fjInput.empty()) { + return; + } + + fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, cfgJetR); + fastjet::AreaDefinition areaDef( + fastjet::active_area_explicit_ghosts, + fastjet::GhostedAreaSpec(cfgTrackEtaMax + cfgJetR)); + + fastjet::ClusterSequenceArea cs(fjInput, jetDef, areaDef); + auto jets = fastjet::sorted_by_pt(cs.inclusive_jets(cfgJetPtMin)); + + for (auto const& jet : jets) { + if (std::abs(jet.eta()) > cfgJetEtaMax) { + continue; + } + + auto constituents = jet.constituents(); + + upcJetTable(evtIdx, + jet.pt(), + jet.eta(), + jet.phi_std(), + jet.area(), + static_cast(constituents.size())); + + int jetIdx = upcJetTable.lastIndex(); + + for (auto const& c : constituents) { + upcJetTrackTable(jetIdx, evtIdx, c.pt(), c.eta(), c.phi_std()); + } + } + } + PROCESS_SWITCH(JetUpcQa, processData, "Process real data", false); + // Process: MCD + void processMCD(CollisionsMC::iterator const& collision, + BCsWithSel const& bcs, + aod::Zdcs const&, + aod::FT0s const&, aod::FV0As const&, aod::FDDs const&, + TracksMC const& tracks) + { + registry.fill(HIST("hMCDFoundBC"), collision.has_foundBC() ? 1 : 0); + + auto gapInfo = getGapSide(collision, bcs); + int gapSide = gapInfo.gapSide; + bool isGapTagged = isSelectedGap(gapSide); + bool isZDCTagged = isSelectedZDC(gapInfo.zdc); + bool isAccepted = isAcceptedEvent(isGapTagged, isZDCTagged); + registry.fill(HIST("hGapSideMCD"), gapSide); + registry.fill(HIST("hZNAEnergyMCD"), gapInfo.zdc.energyCommonZNA); + registry.fill(HIST("hZNCEnergyMCD"), gapInfo.zdc.energyCommonZNC); + registry.fill(HIST("hZNATimeMCD"), gapInfo.zdc.timeZNA); + registry.fill(HIST("hZNCTimeMCD"), gapInfo.zdc.timeZNC); + registry.fill(HIST("hNeutronClassMCD"), gapInfo.zdc.neutronClass); + fillSelectionBits(true, isGapTagged, isZDCTagged, isAccepted); + + if (!isAccepted) { + return; + } + + registry.fill(HIST("hPosZMCD"), collision.posZ()); + registry.fill(HIST("hCentMCD"), collision.centFT0M()); + + upcJetEventTableMCD(gapSide, + collision.posZ(), + collision.centFT0M(), + gapInfo.zdc.energyCommonZNA, + gapInfo.zdc.energyCommonZNC, + gapInfo.zdc.timeZNA, + gapInfo.zdc.timeZNC, + gapInfo.zdc.neutronClass, + isGapTagged, + isZDCTagged); + int evtIdx = upcJetEventTableMCD.lastIndex(); + + std::vector fjInput; + fjInput.reserve(256); + + for (auto const& t : tracks) { + if (t.collisionId() != collision.globalIndex()) { + continue; + } + if (!isSelectedTrack(t)) { + continue; + } + + upcTrackTableMCD(evtIdx, t.pt(), t.eta(), t.phi()); + + fastjet::PseudoJet p; + p.reset_PtYPhiM(t.pt(), t.eta(), t.phi(), o2::constants::physics::MassPiPlus); + fjInput.push_back(p); + } + + if (fjInput.empty()) { + return; + } + + fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, cfgJetR); + fastjet::AreaDefinition areaDef( + fastjet::active_area_explicit_ghosts, + fastjet::GhostedAreaSpec(cfgTrackEtaMax + cfgJetR)); + + fastjet::ClusterSequenceArea cs(fjInput, jetDef, areaDef); + auto jets = fastjet::sorted_by_pt(cs.inclusive_jets(cfgJetPtMin)); + + for (auto const& jet : jets) { + if (std::abs(jet.eta()) > cfgJetEtaMax) { + continue; + } + + auto constituents = jet.constituents(); + + upcJetTableMCD(evtIdx, + jet.pt(), + jet.eta(), + jet.phi_std(), + jet.area(), + static_cast(constituents.size())); + + int jetIdx = upcJetTableMCD.lastIndex(); + + for (auto const& c : constituents) { + upcJetTrackTableMCD(jetIdx, c.pt(), c.eta(), c.phi_std()); + } + } + } + PROCESS_SWITCH(JetUpcQa, processMCD, "Process MC detector level", true); + + // Process: MCP + void processMCP(aod::McCollision const& mcCollision, + aod::McParticles const& particles) + { + registry.fill(HIST("hPosZMCP"), mcCollision.posZ()); + + upcJetEventTableMCP(mcCollision.posZ()); + int evtIdx = upcJetEventTableMCP.lastIndex(); + fillEventTracksMCP(particles, evtIdx, upcTrackTableMCP); + runJetFindingMCP(particles, evtIdx, upcJetTableMCP, upcJetTrackTableMCP); + } + PROCESS_SWITCH(JetUpcQa, processMCP, "Process MC particle level", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGJE/Tasks/o2linter_config b/PWGJE/Tasks/o2linter_config new file mode 100644 index 00000000000..02ec0fc3e3b --- /dev/null +++ b/PWGJE/Tasks/o2linter_config @@ -0,0 +1,15 @@ +root/entity +external-pi +pi-multiple-fraction +doc/file +name/function-variable +name/macro +name/constexpr-constant +name/namespace +name/type +name/enum +name/class +name/struct +name/file-cpp +name/file-python +name/o2-workflow