Skip to content

Commit 04260f6

Browse files
minjungkim12claude
andcommitted
[PWGHF] Refactor UPC code: move utilities to utilsUpcHf.h
- Add new utility header PWGHF/Utils/utilsUpcHf.h with: - GapType enum for UPC gap classification - determineGapType() function with configurable thresholds - Helper functions: isSingleSidedGap(), gapTypeToInt(), getGapTypeName() - UpcQaHistoConfig struct for consistent histogram binning - Refactor taskD0.cxx and taskDplus.cxx to use shared utilities - Remove duplicate GapType enum and determineGapType() from both tasks - Add gap type axis to THnSparse histograms for UPC analysis - Improve code maintainability and reusability across HF UPC tasks 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent c009341 commit 04260f6

File tree

3 files changed

+182
-67
lines changed

3 files changed

+182
-67
lines changed

PWGHF/D2H/Tasks/taskD0.cxx

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "PWGHF/DataModel/CandidateReconstructionTables.h"
2424
#include "PWGHF/DataModel/CandidateSelectionTables.h"
2525
#include "PWGHF/Utils/utilsEvSelHf.h"
26+
#include "PWGHF/Utils/utilsUpcHf.h"
2627
#include "PWGUD/Core/UPCHelpers.h"
2728

2829
#include "Common/CCDB/ctpRateFetcher.h"
@@ -59,12 +60,7 @@ using namespace o2::framework::expressions;
5960
using namespace o2::hf_centrality;
6061
using namespace o2::hf_occupancy;
6162
using namespace o2::hf_evsel;
62-
63-
enum class GapType {
64-
GapA = 0,
65-
GapC = 1,
66-
DoubleGap = 2,
67-
};
63+
using namespace o2::analysis::hf_upc;
6864

6965
/// D0 analysis task
7066
namespace
@@ -156,6 +152,7 @@ struct HfTaskD0 {
156152
ConfigurableAxis thnConfigAxisMinItsNCls{"thnConfigAxisMinItsNCls", {5, 3, 8}, "axis for minimum ITS NCls of candidate prongs"};
157153
ConfigurableAxis thnConfigAxisMinTpcNCrossedRows{"thnConfigAxisMinTpcNCrossedRows", {10, 70, 180}, "axis for minimum TPC NCls crossed rows of candidate prongs"};
158154
ConfigurableAxis thnConfigAxisIR{"thnConfigAxisIR", {5000, 0, 500}, "Interaction rate (kHz)"};
155+
ConfigurableAxis thnConfigAxisGapType{"thnConfigAxisGapType", {3, -0.5, 2.5}, "axis for UPC gap type (0=GapA, 1=GapC, 2=DoubleGap)"};
159156

160157
HistogramRegistry registry{
161158
"registry",
@@ -300,6 +297,7 @@ struct HfTaskD0 {
300297
const AxisSpec thnAxisMinItsNCls{thnConfigAxisMinItsNCls, "Minimum ITS cluster found"};
301298
const AxisSpec thnAxisMinTpcNCrossedRows{thnConfigAxisMinTpcNCrossedRows, "Minimum TPC crossed rows"};
302299
const AxisSpec thnAxisIR{thnConfigAxisIR, "Interaction rate"};
300+
const AxisSpec thnAxisGapType{thnConfigAxisGapType, "Gap type"};
303301

304302
if (doprocessMcWithDCAFitterN || doprocessMcWithDCAFitterNCent || doprocessMcWithKFParticle || doprocessMcWithDCAFitterNMl || doprocessMcWithDCAFitterNMlCent || doprocessMcWithKFParticleMl) {
305303
std::vector<AxisSpec> axesAcc = {thnAxisGenPtD, thnAxisGenPtB, thnAxisY, thnAxisOrigin, thnAxisNumPvContr};
@@ -333,6 +331,9 @@ struct HfTaskD0 {
333331
axes.push_back(thnAxisMinItsNCls);
334332
axes.push_back(thnAxisMinTpcNCrossedRows);
335333
}
334+
if (doprocessDataWithDCAFitterNMlWithUpc) {
335+
axes.push_back(thnAxisGapType);
336+
}
336337
if (applyMl) {
337338
const AxisSpec thnAxisBkgScore{thnConfigAxisBkgScore, "BDT score bkg."};
338339
const AxisSpec thnAxisNonPromptScore{thnConfigAxisNonPromptScore, "BDT score non-prompt."};
@@ -543,20 +544,6 @@ struct HfTaskD0 {
543544
}
544545
}
545546

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-
560547
template <bool fillMl, typename CollType, typename CandType, typename BCsType>
561548
void runAnalysisPerCollisionDataWithUpc(CollType const& collisions,
562549
CandType const& candidates,
@@ -581,10 +568,11 @@ struct HfTaskD0 {
581568
auto zdc = bc.zdc();
582569
qaRegistry.fill(HIST("Data/fitInfo/ampFT0A_vs_ampFT0C"), fitInfo.ampFT0A, fitInfo.ampFT0C);
583570
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));
571+
gap = hf_upc::determineGapType(fitInfo.ampFT0A, fitInfo.ampFT0C, zdc.energyCommonZNA(), zdc.energyCommonZNC());
572+
qaRegistry.fill(HIST("Data/hUpcGapAfterSelection"), hf_upc::gapTypeToInt(gap));
586573
}
587-
if (gap == GapType::GapA || gap == GapType::GapC) {
574+
if (hf_upc::isSingleSidedGap(gap)) {
575+
int const gapTypeInt = hf_upc::gapTypeToInt(gap);
588576
const auto thisCollId = collision.globalIndex();
589577
const auto& groupedD0Candidates = candidates.sliceBy(candD0PerCollision, thisCollId);
590578

@@ -613,21 +601,21 @@ struct HfTaskD0 {
613601

614602
if constexpr (fillMl) {
615603
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);
604+
registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, hfHelper.yD0(candidate), SigD0, static_cast<float>(gapTypeInt));
605+
registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, hfHelper.yD0(candidate), candidate.isSelD0bar() ? ReflectedD0 : PureSigD0, static_cast<float>(gapTypeInt));
618606
}
619607
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);
608+
registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0bar, ptCandidate, hfHelper.yD0(candidate), SigD0bar, static_cast<float>(gapTypeInt));
609+
registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0bar, ptCandidate, hfHelper.yD0(candidate), candidate.isSelD0() ? ReflectedD0bar : PureSigD0bar, static_cast<float>(gapTypeInt));
622610
}
623611
} else {
624612
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);
613+
registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, hfHelper.yD0(candidate), SigD0, static_cast<float>(gapTypeInt));
614+
registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, hfHelper.yD0(candidate), candidate.isSelD0bar() ? ReflectedD0 : PureSigD0, static_cast<float>(gapTypeInt));
627615
}
628616
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);
617+
registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, hfHelper.yD0(candidate), SigD0bar, static_cast<float>(gapTypeInt));
618+
registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, hfHelper.yD0(candidate), candidate.isSelD0() ? ReflectedD0bar : PureSigD0bar, static_cast<float>(gapTypeInt));
631619
}
632620
}
633621
}

PWGHF/D2H/Tasks/taskDplus.cxx

Lines changed: 40 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "PWGHF/DataModel/CandidateSelectionTables.h"
2727
#include "PWGHF/Utils/utilsAnalysis.h"
2828
#include "PWGHF/Utils/utilsEvSelHf.h"
29+
#include "PWGHF/Utils/utilsUpcHf.h"
2930
#include "PWGUD/Core/UPCHelpers.h"
3031

3132
#include "Common/Core/RecoDecay.h"
@@ -62,12 +63,7 @@ using namespace o2::framework::expressions;
6263
using namespace o2::hf_centrality;
6364
using namespace o2::hf_occupancy;
6465
using namespace o2::hf_evsel;
65-
66-
enum class GapType {
67-
GapA = 0,
68-
GapC = 1,
69-
DoubleGap = 2,
70-
};
66+
using namespace o2::analysis::hf_upc;
7167

7268
/// D± analysis task
7369
struct HfTaskDplus {
@@ -128,6 +124,7 @@ struct HfTaskDplus {
128124
ConfigurableAxis thnConfigAxisMlScore0{"thnConfigAxisMlScore0", {100, 0., 1.}, "axis for ML output score 0"};
129125
ConfigurableAxis thnConfigAxisMlScore1{"thnConfigAxisMlScore1", {100, 0., 1.}, "axis for ML output score 1"};
130126
ConfigurableAxis thnConfigAxisMlScore2{"thnConfigAxisMlScore2", {100, 0., 1.}, "axis for ML output score 2"};
127+
ConfigurableAxis thnConfigAxisGapType{"thnConfigAxisGapType", {3, -0.5, 2.5}, "axis for UPC gap type (0=GapA, 1=GapC, 2=DoubleGap)"};
131128

132129
HistogramRegistry registry{
133130
"registry",
@@ -161,6 +158,7 @@ struct HfTaskDplus {
161158
AxisSpec const thnAxisCent{thnConfigAxisCent, "Centrality"};
162159
AxisSpec const thnAxisOccupancy{thnConfigAxisOccupancy, "Occupancy"};
163160
AxisSpec const thnAxisPvContributors{thnConfigAxisPvContributors, "PV contributors"};
161+
AxisSpec const thnAxisGapType{thnConfigAxisGapType, "Gap type"};
164162

165163
registry.add("hMass", "3-prong candidates;inv. mass (#pi K #pi) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{350, 1.7, 2.05}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}});
166164
registry.add("hEta", "3-prong candidates;candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}});
@@ -200,10 +198,10 @@ struct HfTaskDplus {
200198
registry.add("hPtVsYGenPrompt", "MC particles (matched, prompt);#it{p}_{T}^{gen.}; #it{y}", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {100, -5., 5.}}});
201199
registry.add("hPtVsYGenNonPrompt", "MC particles (matched, non-prompt);#it{p}_{T}^{gen.}; #it{y}", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {100, -5., 5.}}});
202200

203-
if (doprocessDataWithMl || doprocessData) {
201+
if (doprocessDataWithMl || doprocessData || doprocessDataWithMlWithUpc) {
204202
std::vector<AxisSpec> axes = {thnAxisMass, thnAxisPt};
205203

206-
if (doprocessDataWithMl) {
204+
if (doprocessDataWithMl || doprocessDataWithMlWithUpc) {
207205
axes.push_back(thnAxisMlScore0);
208206
axes.push_back(thnAxisMlScore1);
209207
axes.push_back(thnAxisMlScore2);
@@ -214,6 +212,9 @@ struct HfTaskDplus {
214212
if (storeOccupancy) {
215213
axes.push_back(thnAxisOccupancy);
216214
}
215+
if (doprocessDataWithMlWithUpc) {
216+
axes.push_back(thnAxisGapType);
217+
}
217218

218219
registry.add("hSparseMass", "THn for Dplus", HistType::kTHnSparseF, axes);
219220
}
@@ -313,13 +314,15 @@ struct HfTaskDplus {
313314
/// \param centrality collision centrality
314315
/// \param occupancy collision occupancy
315316
/// \param numPvContributors contributors to the PV
317+
/// \param gapType UPC gap type (-1 for non-UPC)
316318
template <bool IsMc, bool IsMatched, typename T1>
317319
void fillSparseML(const T1& candidate,
318320
float ptbhad,
319321
int flagBHad,
320322
float centrality,
321323
float occupancy,
322-
float numPvContributors)
324+
float numPvContributors,
325+
int gapType = -1)
323326
{
324327
std::vector<float> outputMl = {-999., -999., -999.};
325328
for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) {
@@ -384,16 +387,30 @@ struct HfTaskDplus {
384387
}
385388
}
386389
} else { // Data
387-
if (storeCentrality && storeOccupancy) {
388-
registry.fill(HIST("hSparseMass"), hfHelper.invMassDplusToPiKPi(candidate), candidate.pt(), outputMl[0], outputMl[1], outputMl[2], centrality, occupancy);
389-
} else if (storeCentrality && !storeOccupancy) {
390-
registry.fill(HIST("hSparseMass"), hfHelper.invMassDplusToPiKPi(candidate), candidate.pt(), outputMl[0], outputMl[1], outputMl[2], centrality);
391-
} else if (!storeCentrality && storeOccupancy) {
392-
registry.fill(HIST("hSparseMass"), hfHelper.invMassDplusToPiKPi(candidate), candidate.pt(), outputMl[0], outputMl[1], outputMl[2], occupancy);
393-
} else if (!storeCentrality && !storeOccupancy && storePvContributors) {
394-
registry.fill(HIST("hSparseMass"), hfHelper.invMassDplusToPiKPi(candidate), candidate.pt(), outputMl[0], outputMl[1], outputMl[2], numPvContributors);
390+
if (gapType >= 0) {
391+
// UPC mode: always include gap type
392+
if (storeCentrality && storeOccupancy) {
393+
registry.fill(HIST("hSparseMass"), hfHelper.invMassDplusToPiKPi(candidate), candidate.pt(), outputMl[0], outputMl[1], outputMl[2], centrality, occupancy, static_cast<float>(gapType));
394+
} else if (storeCentrality && !storeOccupancy) {
395+
registry.fill(HIST("hSparseMass"), hfHelper.invMassDplusToPiKPi(candidate), candidate.pt(), outputMl[0], outputMl[1], outputMl[2], centrality, static_cast<float>(gapType));
396+
} else if (!storeCentrality && storeOccupancy) {
397+
registry.fill(HIST("hSparseMass"), hfHelper.invMassDplusToPiKPi(candidate), candidate.pt(), outputMl[0], outputMl[1], outputMl[2], occupancy, static_cast<float>(gapType));
398+
} else {
399+
registry.fill(HIST("hSparseMass"), hfHelper.invMassDplusToPiKPi(candidate), candidate.pt(), outputMl[0], outputMl[1], outputMl[2], static_cast<float>(gapType));
400+
}
395401
} else {
396-
registry.fill(HIST("hSparseMass"), hfHelper.invMassDplusToPiKPi(candidate), candidate.pt(), outputMl[0], outputMl[1], outputMl[2]);
402+
// Non-UPC mode: original behavior
403+
if (storeCentrality && storeOccupancy) {
404+
registry.fill(HIST("hSparseMass"), hfHelper.invMassDplusToPiKPi(candidate), candidate.pt(), outputMl[0], outputMl[1], outputMl[2], centrality, occupancy);
405+
} else if (storeCentrality && !storeOccupancy) {
406+
registry.fill(HIST("hSparseMass"), hfHelper.invMassDplusToPiKPi(candidate), candidate.pt(), outputMl[0], outputMl[1], outputMl[2], centrality);
407+
} else if (!storeCentrality && storeOccupancy) {
408+
registry.fill(HIST("hSparseMass"), hfHelper.invMassDplusToPiKPi(candidate), candidate.pt(), outputMl[0], outputMl[1], outputMl[2], occupancy);
409+
} else if (!storeCentrality && !storeOccupancy && storePvContributors) {
410+
registry.fill(HIST("hSparseMass"), hfHelper.invMassDplusToPiKPi(candidate), candidate.pt(), outputMl[0], outputMl[1], outputMl[2], numPvContributors);
411+
} else {
412+
registry.fill(HIST("hSparseMass"), hfHelper.invMassDplusToPiKPi(candidate), candidate.pt(), outputMl[0], outputMl[1], outputMl[2]);
413+
}
397414
}
398415
}
399416
}
@@ -680,20 +697,6 @@ struct HfTaskDplus {
680697
}
681698
}
682699

683-
GapType determineGapType(float FT0A, float FT0C, float ZNA, float ZNC)
684-
{
685-
constexpr float FT0AThreshold = 100.0;
686-
constexpr float FT0CThreshold = 50.0;
687-
constexpr float ZDCThreshold = 1.0;
688-
if (FT0A < FT0AThreshold && FT0C > FT0CThreshold && ZNA < ZDCThreshold && ZNC > ZDCThreshold) {
689-
return GapType::GapA;
690-
}
691-
if (FT0A > FT0AThreshold && FT0C < FT0CThreshold && ZNA > ZDCThreshold && ZNC < ZDCThreshold) {
692-
return GapType::GapC;
693-
}
694-
return GapType::DoubleGap;
695-
}
696-
697700
template <bool fillMl, typename CollType, typename CandType, typename BCsType>
698701
void runAnalysisPerCollisionDataWithUpc(CollType const& collisions,
699702
CandType const& candidates,
@@ -719,10 +722,10 @@ struct HfTaskDplus {
719722
auto zdc = bc.zdc();
720723
qaRegistry.fill(HIST("Data/fitInfo/ampFT0A_vs_ampFT0C"), fitInfo.ampFT0A, fitInfo.ampFT0C);
721724
qaRegistry.fill(HIST("Data/zdc/energyZNA_vs_energyZNC"), zdc.energyCommonZNA(), zdc.energyCommonZNC());
722-
gap = determineGapType(fitInfo.ampFT0A, fitInfo.ampFT0C, zdc.energyCommonZNA(), zdc.energyCommonZNC());
723-
qaRegistry.fill(HIST("Data/hUpcGapAfterSelection"), static_cast<int>(gap));
725+
gap = hf_upc::determineGapType(fitInfo.ampFT0A, fitInfo.ampFT0C, zdc.energyCommonZNA(), zdc.energyCommonZNC());
726+
qaRegistry.fill(HIST("Data/hUpcGapAfterSelection"), hf_upc::gapTypeToInt(gap));
724727
}
725-
if (gap == GapType::GapA || gap == GapType::GapC) {
728+
if (hf_upc::isSingleSidedGap(gap)) {
726729
// Use the candidates from this collision
727730
const auto thisCollId = collision.globalIndex();
728731
const auto& groupedDplusCandidates = candidates.sliceBy(candDplusPerCollision, thisCollId);
@@ -731,14 +734,15 @@ struct HfTaskDplus {
731734
float numPvContr{-1.f};
732735
float ptBhad{-1.f};
733736
int const flagBHad{-1};
737+
int const gapTypeInt = hf_upc::gapTypeToInt(gap);
734738

735739
for (const auto& candidate : groupedDplusCandidates) {
736740
if ((yCandRecoMax >= 0. && std::abs(hfHelper.yDplus(candidate)) > yCandRecoMax)) {
737741
continue;
738742
}
739743
fillHisto(candidate);
740744
if constexpr (fillMl) {
741-
fillSparseML<false, false>(candidate, ptBhad, flagBHad, cent, occ, numPvContr);
745+
fillSparseML<false, false>(candidate, ptBhad, flagBHad, cent, occ, numPvContr, gapTypeInt);
742746
}
743747
}
744748
}

0 commit comments

Comments
 (0)