Skip to content

Commit ae6c411

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 13a68cd commit ae6c411

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"
@@ -22,6 +23,7 @@
2223
#include "PWGHF/DataModel/CandidateReconstructionTables.h"
2324
#include "PWGHF/DataModel/CandidateSelectionTables.h"
2425
#include "PWGHF/Utils/utilsEvSelHf.h"
26+
#include "PWGUD/Core/UPCHelpers.h"
2527

2628
#include "Common/CCDB/ctpRateFetcher.h"
2729
#include "Common/Core/RecoDecay.h"
@@ -56,6 +58,13 @@ using namespace o2::framework;
5658
using namespace o2::framework::expressions;
5759
using namespace o2::hf_centrality;
5860
using namespace o2::hf_occupancy;
61+
using namespace o2::hf_evsel;
62+
63+
enum class GapType {
64+
GapA = 0,
65+
GapC = 1,
66+
DoubleGap = 2,
67+
};
5968

6069
/// D0 analysis task
6170
namespace
@@ -87,8 +96,12 @@ struct HfTaskD0 {
8796
// ML inference
8897
Configurable<bool> applyMl{"applyMl", false, "Flag to apply ML selections"};
8998
Configurable<std::string> ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"};
99+
Configurable<std::string> ccdbPathGrp{"ccdbPathGrp", "GLO/GRP/GRP", "Path of the grp file (Run 2)"};
100+
Configurable<std::string> ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object (Run 3)"};
90101
Configurable<std::string> irSource{"irSource", "ZNC hadronic", "Estimator of the interaction rate (Recommended: pp --> T0VTX, Pb-Pb --> ZNC hadronic)"};
91102

103+
HfEventSelection hfEvSel; // event selection and monitoring
104+
92105
HfHelper hfHelper;
93106
ctpRateFetcher mRateFetcher;
94107

@@ -110,6 +123,8 @@ struct HfTaskD0 {
110123
using CollisionsWithMcLabels = soa::Join<aod::Collisions, aod::McCollisionLabels, aod::EvSels>;
111124
using CollisionsWithMcLabelsCent = soa::Join<aod::Collisions, aod::McCollisionLabels, aod::EvSels, aod::CentFT0Ms, aod::CentFT0Cs>;
112125
using TracksSelQuality = soa::Join<aod::TracksExtra, aod::TracksWMc>;
126+
127+
Preslice<aod::HfCand2Prong> candD0PerCollision = aod::hf_cand::collisionId;
113128
PresliceUnsorted<CollisionsWithMcLabels> colPerMcCollision = aod::mccollisionlabel::mcCollisionId;
114129
PresliceUnsorted<CollisionsWithMcLabelsCent> colPerMcCollisionCent = aod::mccollisionlabel::mcCollisionId;
115130

@@ -218,9 +233,11 @@ struct HfTaskD0 {
218233
{"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.}}}},
219234
{"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.}}}}}};
220235

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

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

9821114
WorkflowSpec defineDataProcessing(ConfigContext const& cfgc)

0 commit comments

Comments
 (0)