Skip to content

Commit db90b42

Browse files
minjungkim12claude
andcommitted
[PWGHF] Add UPC process function and QA hists to taskD0
This commit adds support for analyzing Ultra-Peripheral Collisions (UPC) in the D0 → K- π+ analysis, following the implementation in taskLc and taskDplus. Changes include: - Added UPC-related library dependencies in CMakeLists.txt - Added CCDB manager and UPC helper includes - Implemented GapType enum for gap classification - Added QA histograms for FT0 and ZDC detectors - Implemented determineGapType() function for event classification - Added runAnalysisPerCollisionDataWithUpc() template function - Added processDataWithDCAFitterNMlWithUpc() process function - Added candD0PerCollision Preslice for efficient candidate slicing The UPC analysis uses FT0 and ZDC detector information to classify events into Gap A, Gap C, or Double Gap categories based on detector thresholds, enabling studies of diffractive processes in D0 production. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent d785d18 commit db90b42

File tree

2 files changed

+134
-2
lines changed

2 files changed

+134
-2
lines changed

PWGHF/D2H/Tasks/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ o2physics_add_dpl_workflow(task-charm-reso-to-d-trk-reduced
7171

7272
o2physics_add_dpl_workflow(task-d0
7373
SOURCES taskD0.cxx
74-
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB
74+
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::SGCutParHolder O2Physics::EventFilteringUtils
7575
COMPONENT_NAME Analysis)
7676

7777
o2physics_add_dpl_workflow(task-directed-flow-charm-hadrons

PWGHF/D2H/Tasks/taskD0.cxx

Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
///
1515
/// \author Gian Michele Innocenti <gian.michele.innocenti@cern.ch>, CERN
1616
/// \author Vít Kučera <vit.kucera@cern.ch>, CERN
17+
/// \author Minjung Kim <minjung.kim@cern.ch>, CERN
1718

1819
#include "PWGHF/Core/CentralityEstimation.h"
1920
#include "PWGHF/Core/DecayChannels.h"
@@ -24,6 +25,7 @@
2425
#include "PWGHF/DataModel/CandidateSelectionTables.h"
2526
#include "PWGHF/DataModel/TrackIndexSkimmingTables.h"
2627
#include "PWGHF/Utils/utilsEvSelHf.h"
28+
#include "PWGUD/Core/UPCHelpers.h"
2729

2830
#include "Common/CCDB/ctpRateFetcher.h"
2931
#include "Common/Core/RecoDecay.h"
@@ -58,6 +60,13 @@ using namespace o2::framework;
5860
using namespace o2::framework::expressions;
5961
using namespace o2::hf_centrality;
6062
using namespace o2::hf_occupancy;
63+
using namespace o2::hf_evsel;
64+
65+
enum class GapType {
66+
GapA = 0,
67+
GapC = 1,
68+
DoubleGap = 2,
69+
};
6170

6271
/// D0 analysis task
6372
namespace
@@ -89,8 +98,12 @@ struct HfTaskD0 {
8998
// ML inference
9099
Configurable<bool> applyMl{"applyMl", false, "Flag to apply ML selections"};
91100
Configurable<std::string> ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"};
101+
Configurable<std::string> ccdbPathGrp{"ccdbPathGrp", "GLO/GRP/GRP", "Path of the grp file (Run 2)"};
102+
Configurable<std::string> ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object (Run 3)"};
92103
Configurable<std::string> irSource{"irSource", "ZNC hadronic", "Estimator of the interaction rate (Recommended: pp --> T0VTX, Pb-Pb --> ZNC hadronic)"};
93104

105+
HfEventSelection hfEvSel; // event selection and monitoring
106+
94107
ctpRateFetcher mRateFetcher;
95108

96109
SliceCache cache;
@@ -111,6 +124,8 @@ struct HfTaskD0 {
111124
using CollisionsWithMcLabels = soa::Join<aod::Collisions, aod::McCollisionLabels, aod::EvSels>;
112125
using CollisionsWithMcLabelsCent = soa::Join<aod::Collisions, aod::McCollisionLabels, aod::EvSels, aod::CentFT0Ms, aod::CentFT0Cs>;
113126
using TracksSelQuality = soa::Join<aod::TracksExtra, aod::TracksWMc>;
127+
128+
Preslice<aod::HfCand2Prong> candD0PerCollision = aod::hf_cand::collisionId;
114129
PresliceUnsorted<CollisionsWithMcLabels> colPerMcCollision = aod::mccollisionlabel::mcCollisionId;
115130
PresliceUnsorted<CollisionsWithMcLabelsCent> colPerMcCollisionCent = aod::mccollisionlabel::mcCollisionId;
116131

@@ -219,9 +234,11 @@ struct HfTaskD0 {
219234
{"hMassReflBkgD0bar", "2-prong candidates (matched);#it{m}_{inv} (GeV/#it{c}^{2}); #it{p}_{T}; #it{y}", {HistType::kTH3F, {{120, 1.5848, 2.1848}, {150, 0., 30.}, {20, -5., 5.}}}},
220235
{"hMassSigBkgD0bar", "2-prong candidates (not checked);#it{m}_{inv} (GeV/#it{c}^{2}); #it{p}_{T}; #it{y}", {HistType::kTH3F, {{120, 1.5848, 2.1848}, {150, 0., 30.}, {20, -5., 5.}}}}}};
221236

237+
HistogramRegistry qaRegistry{"QAHistos", {}, OutputObjHandlingPolicy::AnalysisObject};
238+
222239
void init(InitContext&)
223240
{
224-
std::array<bool, 12> doprocess{doprocessDataWithDCAFitterN, doprocessDataWithDCAFitterNCent, doprocessDataWithKFParticle, doprocessMcWithDCAFitterN, doprocessMcWithDCAFitterNCent, doprocessMcWithKFParticle, doprocessDataWithDCAFitterNMl, doprocessDataWithDCAFitterNMlCent, doprocessDataWithKFParticleMl, doprocessMcWithDCAFitterNMl, doprocessMcWithDCAFitterNMlCent, doprocessMcWithKFParticleMl};
241+
std::array<bool, 13> doprocess{doprocessDataWithDCAFitterN, doprocessDataWithDCAFitterNCent, doprocessDataWithKFParticle, doprocessMcWithDCAFitterN, doprocessMcWithDCAFitterNCent, doprocessMcWithKFParticle, doprocessDataWithDCAFitterNMl, doprocessDataWithDCAFitterNMlCent, doprocessDataWithKFParticleMl, doprocessMcWithDCAFitterNMl, doprocessMcWithDCAFitterNMlCent, doprocessMcWithKFParticleMl, doprocessDataWithDCAFitterNMlWithUpc};
225242
if ((std::accumulate(doprocess.begin(), doprocess.end(), 0)) == 0) {
226243
LOGP(fatal, "At least one process function should be enabled at a time.");
227244
}
@@ -333,6 +350,15 @@ struct HfTaskD0 {
333350
registry.get<THnSparse>(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"))->Sumw2();
334351
}
335352

353+
qaRegistry.add("Data/fitInfo/ampFT0A_vs_ampFT0C", "FT0-A vs FT0-C amplitude;FT0-A amplitude (a.u.);FT0-C amplitude (a.u.)", {HistType::kTH2F, {{2500, 0., 250}, {2500, 0., 250}}});
354+
qaRegistry.add("Data/zdc/energyZNA_vs_energyZNC", "ZNA vs ZNC common energy;E_{ZNA}^{common} (a.u.);E_{ZNC}^{common} (a.u.)", {HistType::kTH2F, {{200, 0., 20}, {200, 0., 20}}});
355+
qaRegistry.add("Data/hUpcGapAfterSelection", "UPC gap type after selection;Gap side;Counts", {HistType::kTH1F, {{3, -0.5, 2.5}}});
356+
qaRegistry.get<TH1>(HIST("Data/hUpcGapAfterSelection"))->GetXaxis()->SetBinLabel(static_cast<int>(GapType::GapA) + 1, "A");
357+
qaRegistry.get<TH1>(HIST("Data/hUpcGapAfterSelection"))->GetXaxis()->SetBinLabel(static_cast<int>(GapType::GapC) + 1, "C");
358+
qaRegistry.get<TH1>(HIST("Data/hUpcGapAfterSelection"))->GetXaxis()->SetBinLabel(static_cast<int>(GapType::DoubleGap) + 1, "Double");
359+
360+
hfEvSel.addHistograms(qaRegistry);
361+
336362
ccdb->setURL(ccdbUrl);
337363
ccdb->setCaching(true);
338364
ccdb->setLocalObjectValidityChecking();
@@ -517,6 +543,99 @@ struct HfTaskD0 {
517543
}
518544
}
519545
}
546+
547+
GapType determineGapType(float FT0A, float FT0C, float ZNA, float ZNC)
548+
{
549+
constexpr float FT0AThreshold = 100.0;
550+
constexpr float FT0CThreshold = 50.0;
551+
constexpr float ZDCThreshold = 1.0;
552+
if (FT0A < FT0AThreshold && FT0C > FT0CThreshold && ZNA < ZDCThreshold && ZNC > ZDCThreshold) {
553+
return GapType::GapA;
554+
}
555+
if (FT0A > FT0AThreshold && FT0C < FT0CThreshold && ZNA > ZDCThreshold && ZNC < ZDCThreshold) {
556+
return GapType::GapC;
557+
}
558+
return GapType::DoubleGap;
559+
}
560+
561+
template <bool fillMl, typename CollType, typename CandType, typename BCsType>
562+
void runAnalysisPerCollisionDataWithUpc(CollType const& collisions,
563+
CandType const& candidates,
564+
BCsType const& bcs,
565+
aod::FT0s const& ft0s,
566+
aod::FV0As const& fv0as,
567+
aod::FDDs const& fdds)
568+
{
569+
for (const auto& collision : collisions) {
570+
uint32_t rejectionMask{0};
571+
float centrality{-1.f};
572+
rejectionMask = hfEvSel.getHfCollisionRejectionMaskWithUpc<true, CentralityEstimator::None, BCsType>(collision, centrality, ccdb, qaRegistry, bcs);
573+
if (rejectionMask != 0) {
574+
continue;
575+
}
576+
auto bc = collision.template bc_as<BCsType>();
577+
upchelpers::FITInfo fitInfo{};
578+
udhelpers::getFITinfo(fitInfo, bc, bcs, ft0s, fv0as, fdds);
579+
580+
GapType gap = GapType::DoubleGap;
581+
if (bc.has_zdc()) {
582+
auto zdc = bc.zdc();
583+
qaRegistry.fill(HIST("Data/fitInfo/ampFT0A_vs_ampFT0C"), fitInfo.ampFT0A, fitInfo.ampFT0C);
584+
qaRegistry.fill(HIST("Data/zdc/energyZNA_vs_energyZNC"), zdc.energyCommonZNA(), zdc.energyCommonZNC());
585+
gap = determineGapType(fitInfo.ampFT0A, fitInfo.ampFT0C, zdc.energyCommonZNA(), zdc.energyCommonZNC());
586+
qaRegistry.fill(HIST("Data/hUpcGapAfterSelection"), static_cast<int>(gap));
587+
}
588+
if (gap == GapType::GapA || gap == GapType::GapC) {
589+
const auto thisCollId = collision.globalIndex();
590+
const auto& groupedD0Candidates = candidates.sliceBy(candD0PerCollision, thisCollId);
591+
592+
for (const auto& candidate : groupedD0Candidates) {
593+
if (!(candidate.hfflag() & 1 << aod::hf_cand_2prong::DecayType::D0ToPiK)) {
594+
continue;
595+
}
596+
if (yCandRecoMax >= 0. && std::abs(HfHelper::yD0(candidate)) > yCandRecoMax) {
597+
continue;
598+
}
599+
600+
float massD0 = HfHelper::invMassD0ToPiK(candidate);
601+
float massD0bar = HfHelper::invMassD0barToKPi(candidate);
602+
auto ptCandidate = candidate.pt();
603+
604+
if (candidate.isSelD0() >= selectionFlagD0) {
605+
registry.fill(HIST("hMass"), massD0, ptCandidate);
606+
registry.fill(HIST("hMassFinerBinning"), massD0, ptCandidate);
607+
registry.fill(HIST("hMassVsPhi"), massD0, ptCandidate, candidate.phi());
608+
}
609+
if (candidate.isSelD0bar() >= selectionFlagD0bar) {
610+
registry.fill(HIST("hMass"), massD0bar, ptCandidate);
611+
registry.fill(HIST("hMassFinerBinning"), massD0bar, ptCandidate);
612+
registry.fill(HIST("hMassVsPhi"), massD0bar, ptCandidate, candidate.phi());
613+
}
614+
615+
if constexpr (fillMl) {
616+
if (candidate.isSelD0() >= selectionFlagD0) {
617+
registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, HfHelper::yD0(candidate), SigD0);
618+
registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, HfHelper::yD0(candidate), candidate.isSelD0bar() ? ReflectedD0 : PureSigD0);
619+
}
620+
if (candidate.isSelD0bar() >= selectionFlagD0bar) {
621+
registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0bar, ptCandidate, HfHelper::yD0(candidate), SigD0bar);
622+
registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0bar, ptCandidate, HfHelper::yD0(candidate), candidate.isSelD0() ? ReflectedD0bar : PureSigD0bar);
623+
}
624+
} else {
625+
if (candidate.isSelD0() >= selectionFlagD0) {
626+
registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, HfHelper::yD0(candidate), SigD0);
627+
registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, HfHelper::yD0(candidate), candidate.isSelD0bar() ? ReflectedD0 : PureSigD0);
628+
}
629+
if (candidate.isSelD0bar() >= selectionFlagD0bar) {
630+
registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, HfHelper::yD0(candidate), SigD0bar);
631+
registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, HfHelper::yD0(candidate), candidate.isSelD0() ? ReflectedD0bar : PureSigD0bar);
632+
}
633+
}
634+
}
635+
}
636+
}
637+
}
638+
520639
void processDataWithDCAFitterN(D0Candidates const&, Collisions const& collisions, aod::TracksWExtra const& tracks, aod::BcFullInfos const& bcs)
521640
{
522641
processData<aod::hf_cand::VertexerType::DCAFitter, false>(selectedD0Candidates, collisions, tracks, bcs);
@@ -978,6 +1097,19 @@ struct HfTaskD0 {
9781097
}
9791098
PROCESS_SWITCH(HfTaskD0, processMcWithKFParticleMl, "Process MC with KFParticle and ML selections", false);
9801099
// TODO: add the processMcWithKFParticleMlCent
1100+
1101+
void processDataWithDCAFitterNMlWithUpc(soa::Join<aod::Collisions, aod::EvSels> const& collisions,
1102+
aod::BcFullInfos const& bcs,
1103+
D0CandidatesMl const&,
1104+
aod::TracksWExtra const& tracks,
1105+
aod::FT0s const& ft0s,
1106+
aod::FV0As const& fv0as,
1107+
aod::FDDs const& fdds,
1108+
aod::Zdcs const& /*zdcs*/)
1109+
{
1110+
runAnalysisPerCollisionDataWithUpc<true>(collisions, selectedD0CandidatesMl, bcs, ft0s, fv0as, fdds);
1111+
}
1112+
PROCESS_SWITCH(HfTaskD0, processDataWithDCAFitterNMlWithUpc, "Process real data with DCAFitterN and ML with UPC", false);
9811113
};
9821114

9831115
WorkflowSpec defineDataProcessing(ConfigContext const& cfgc)

0 commit comments

Comments
 (0)