Skip to content

Commit 13a68cd

Browse files
minjungkim12claude
andcommitted
[PWGHF] Add UPC process function and QA hists to taskDplus
This commit adds support for analyzing Ultra-Peripheral Collisions (UPC) in the D± → π± K∓ π± analysis, following the implementation in taskLc. 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 processDataWithMlWithUpc() process function - Added candDplusPerCollision 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. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 8215385 commit 13a68cd

File tree

2 files changed

+113
-2
lines changed

2 files changed

+113
-2
lines changed

PWGHF/D2H/Tasks/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ o2physics_add_dpl_workflow(task-directed-flow-charm-hadrons
8181

8282
o2physics_add_dpl_workflow(task-dplus
8383
SOURCES taskDplus.cxx
84-
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore
84+
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::SGCutParHolder O2Physics::EventFilteringUtils
8585
COMPONENT_NAME Analysis)
8686

8787
o2physics_add_dpl_workflow(task-ds

PWGHF/D2H/Tasks/taskDplus.cxx

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
/// \author Fabio Catalano <fabio.catalano@cern.ch>, Politecnico and INFN Torino
1717
/// \author Vít Kučera <vit.kucera@cern.ch>, CERN
1818
/// \author Luca Aglietta <luca.aglietta@cern.ch>, University and INFN Torino
19+
/// \author Minjung Kim <minjung.kim@cern.ch>, Inha University
1920

2021
#include "PWGHF/Core/CentralityEstimation.h"
2122
#include "PWGHF/Core/DecayChannels.h"
@@ -25,11 +26,13 @@
2526
#include "PWGHF/DataModel/CandidateSelectionTables.h"
2627
#include "PWGHF/Utils/utilsAnalysis.h"
2728
#include "PWGHF/Utils/utilsEvSelHf.h"
29+
#include "PWGUD/Core/UPCHelpers.h"
2830

2931
#include "Common/Core/RecoDecay.h"
3032
#include "Common/DataModel/Centrality.h"
3133
#include "Common/DataModel/EventSelection.h"
3234

35+
#include <CCDB/BasicCCDBManager.h>
3336
#include <CommonConstants/PhysicsConstants.h>
3437
#include <Framework/ASoA.h>
3538
#include <Framework/AnalysisDataModel.h>
@@ -49,6 +52,7 @@
4952
#include <cstdint>
5053
#include <cstdlib>
5154
#include <numeric>
55+
#include <string>
5256
#include <vector>
5357

5458
using namespace o2;
@@ -57,6 +61,13 @@ using namespace o2::framework;
5761
using namespace o2::framework::expressions;
5862
using namespace o2::hf_centrality;
5963
using namespace o2::hf_occupancy;
64+
using namespace o2::hf_evsel;
65+
66+
enum class GapType {
67+
GapA = 0,
68+
GapC = 1,
69+
DoubleGap = 2,
70+
};
6071

6172
/// D± analysis task
6273
struct HfTaskDplus {
@@ -72,8 +83,14 @@ struct HfTaskDplus {
7283
Configurable<bool> storeOccupancy{"storeOccupancy", false, "Flag to store occupancy information"};
7384
Configurable<bool> storePvContributors{"storePvContributors", false, "Flag to store number of PV contributors information"};
7485
Configurable<bool> fillMcBkgHistos{"fillMcBkgHistos", false, "Flag to fill and store histograms for MC background"};
86+
Configurable<std::string> ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"};
87+
Configurable<std::string> ccdbPathGrp{"ccdbPathGrp", "GLO/GRP/GRP", "Path of the grp file (Run 2)"};
88+
Configurable<std::string> ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object (Run 3)"};
89+
90+
HfEventSelection hfEvSel; // event selection and monitoring
7591

7692
HfHelper hfHelper;
93+
Service<o2::ccdb::BasicCCDBManager> ccdb;
7794

7895
using CandDplusData = soa::Filtered<soa::Join<aod::HfCand3Prong, aod::HfSelDplusToPiKPi>>;
7996
using CandDplusDataWithMl = soa::Filtered<soa::Join<aod::HfCand3Prong, aod::HfSelDplusToPiKPi, aod::HfMlDplusToPiKPi>>;
@@ -86,6 +103,7 @@ struct HfTaskDplus {
86103

87104
Filter filterDplusFlag = (o2::aod::hf_track_index::hfflag & static_cast<uint8_t>(BIT(aod::hf_cand_3prong::DecayType::DplusToPiKPi))) != static_cast<uint8_t>(0);
88105

106+
Preslice<aod::HfCand3Prong> candDplusPerCollision = aod::hf_cand::collisionId;
89107
Preslice<aod::McParticles> mcParticlesPerMcCollision = aod::mcparticle::mcCollisionId;
90108
PresliceUnsorted<aod::McCollisionLabels> recoColPerMcCollision = aod::mccollisionlabel::mcCollisionId;
91109

@@ -123,9 +141,11 @@ struct HfTaskDplus {
123141
{"hEtaRecBg", "3-prong candidates (unmatched);#it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}},
124142
{"hEtaGen", "MC particles (matched);#it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}}}};
125143

144+
HistogramRegistry qaRegistry{"QAHistos", {}, OutputObjHandlingPolicy::AnalysisObject};
145+
126146
void init(InitContext&)
127147
{
128-
std::array<bool, 4> doprocess{doprocessData, doprocessDataWithMl, doprocessMc, doprocessMcWithMl};
148+
std::array<bool, 5> doprocess{doprocessData, doprocessDataWithMl, doprocessMc, doprocessMcWithMl, doprocessDataWithMlWithUpc};
129149
if ((std::accumulate(doprocess.begin(), doprocess.end(), 0)) != 1) {
130150
LOGP(fatal, "Only one process function should be enabled! Please check your configuration!");
131151
}
@@ -240,6 +260,19 @@ struct HfTaskDplus {
240260
registry.add("hSparseMassGenPrompt", "THn for gen Prompt Dplus", HistType::kTHnSparseF, axesGenPrompt);
241261
registry.add("hSparseMassGenFD", "THn for gen FD Dplus", HistType::kTHnSparseF, axesGenFD);
242262
}
263+
264+
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}}});
265+
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}}});
266+
qaRegistry.add("Data/hUpcGapAfterSelection", "UPC gap type after selection;Gap side;Counts", {HistType::kTH1F, {{3, -0.5, 2.5}}});
267+
qaRegistry.get<TH1>(HIST("Data/hUpcGapAfterSelection"))->GetXaxis()->SetBinLabel(static_cast<int>(GapType::GapA) + 1, "A");
268+
qaRegistry.get<TH1>(HIST("Data/hUpcGapAfterSelection"))->GetXaxis()->SetBinLabel(static_cast<int>(GapType::GapC) + 1, "C");
269+
qaRegistry.get<TH1>(HIST("Data/hUpcGapAfterSelection"))->GetXaxis()->SetBinLabel(static_cast<int>(GapType::DoubleGap) + 1, "Double");
270+
271+
hfEvSel.addHistograms(qaRegistry); // collision monitoring
272+
273+
ccdb->setURL(ccdbUrl);
274+
ccdb->setCaching(true);
275+
ccdb->setLocalObjectValidityChecking();
243276
}
244277

245278
// Fill histograms of quantities for the reconstructed Dplus candidates
@@ -647,6 +680,71 @@ struct HfTaskDplus {
647680
}
648681
}
649682

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+
697+
template <bool fillMl, typename CollType, typename CandType, typename BCsType>
698+
void runAnalysisPerCollisionDataWithUpc(CollType const& collisions,
699+
CandType const& candidates,
700+
BCsType const& bcs,
701+
aod::FT0s const& ft0s,
702+
aod::FV0As const& fv0as,
703+
aod::FDDs const& fdds)
704+
{
705+
for (const auto& collision : collisions) {
706+
uint32_t rejectionMask{0}; // 32 bits, in case new ev. selections will be added
707+
float centrality{-1.f};
708+
rejectionMask = hfEvSel.getHfCollisionRejectionMaskWithUpc<true, CentralityEstimator::None, BCsType>(collision, centrality, ccdb, qaRegistry, bcs);
709+
if (rejectionMask != 0) {
710+
/// at least one event selection not satisfied --> reject the candidate
711+
continue;
712+
}
713+
auto bc = collision.template bc_as<BCsType>();
714+
upchelpers::FITInfo fitInfo{};
715+
udhelpers::getFITinfo(fitInfo, bc, bcs, ft0s, fv0as, fdds);
716+
717+
GapType gap = GapType::DoubleGap;
718+
if (bc.has_zdc()) {
719+
auto zdc = bc.zdc();
720+
qaRegistry.fill(HIST("Data/fitInfo/ampFT0A_vs_ampFT0C"), fitInfo.ampFT0A, fitInfo.ampFT0C);
721+
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));
724+
}
725+
if (gap == GapType::GapA || gap == GapType::GapC) {
726+
// Use the candidates from this collision
727+
const auto thisCollId = collision.globalIndex();
728+
const auto& groupedDplusCandidates = candidates.sliceBy(candDplusPerCollision, thisCollId);
729+
float cent{-1.f};
730+
float occ{-1.f};
731+
float numPvContr{-1.f};
732+
float ptBhad{-1.f};
733+
int const flagBHad{-1};
734+
735+
for (const auto& candidate : groupedDplusCandidates) {
736+
if ((yCandRecoMax >= 0. && std::abs(hfHelper.yDplus(candidate)) > yCandRecoMax)) {
737+
continue;
738+
}
739+
fillHisto(candidate);
740+
if constexpr (fillMl) {
741+
fillSparseML<false, false>(candidate, ptBhad, flagBHad, cent, occ, numPvContr);
742+
}
743+
}
744+
}
745+
}
746+
}
747+
650748
// process functions
651749
void processData(CandDplusData const& candidates, CollisionsCent const& collisions)
652750
{
@@ -679,6 +777,19 @@ struct HfTaskDplus {
679777
runAnalysisMcGen<true>(mcGenCollisions, mcRecoCollisions, mcGenParticles);
680778
}
681779
PROCESS_SWITCH(HfTaskDplus, processMcWithMl, "Process MC with ML", false);
780+
781+
void processDataWithMlWithUpc(soa::Join<aod::Collisions, aod::EvSels> const& collisions,
782+
aod::BcFullInfos const& bcs,
783+
CandDplusDataWithMl const& selectedDplusCandidatesMl,
784+
aod::Tracks const&,
785+
aod::FT0s const& ft0s,
786+
aod::FV0As const& fv0as,
787+
aod::FDDs const& fdds,
788+
aod::Zdcs const& /*zdcs*/)
789+
{
790+
runAnalysisPerCollisionDataWithUpc<true>(collisions, selectedDplusCandidatesMl, bcs, ft0s, fv0as, fdds);
791+
}
792+
PROCESS_SWITCH(HfTaskDplus, processDataWithMlWithUpc, "Process real data with the ML method with UPC", false);
682793
};
683794

684795
WorkflowSpec defineDataProcessing(ConfigContext const& cfgc)

0 commit comments

Comments
 (0)