Skip to content

Commit d785d18

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 bfc91e5 commit d785d18

File tree

2 files changed

+114
-2
lines changed

2 files changed

+114
-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: 113 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"
@@ -26,11 +27,13 @@
2627
#include "PWGHF/DataModel/TrackIndexSkimmingTables.h"
2728
#include "PWGHF/Utils/utilsAnalysis.h"
2829
#include "PWGHF/Utils/utilsEvSelHf.h"
30+
#include "PWGUD/Core/UPCHelpers.h"
2931

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

36+
#include <CCDB/BasicCCDBManager.h>
3437
#include <CommonConstants/PhysicsConstants.h>
3538
#include <Framework/ASoA.h>
3639
#include <Framework/AnalysisDataModel.h>
@@ -50,6 +53,7 @@
5053
#include <cstdint>
5154
#include <cstdlib>
5255
#include <numeric>
56+
#include <string>
5357
#include <vector>
5458

5559
using namespace o2;
@@ -58,6 +62,13 @@ using namespace o2::framework;
5862
using namespace o2::framework::expressions;
5963
using namespace o2::hf_centrality;
6064
using namespace o2::hf_occupancy;
65+
using namespace o2::hf_evsel;
66+
67+
enum class GapType {
68+
GapA = 0,
69+
GapC = 1,
70+
DoubleGap = 2,
71+
};
6172

6273
/// D± analysis task
6374
struct HfTaskDplus {
@@ -73,6 +84,13 @@ struct HfTaskDplus {
7384
Configurable<bool> storeOccupancy{"storeOccupancy", false, "Flag to store occupancy information"};
7485
Configurable<bool> storePvContributors{"storePvContributors", false, "Flag to store number of PV contributors information"};
7586
Configurable<bool> fillMcBkgHistos{"fillMcBkgHistos", false, "Flag to fill and store histograms for MC background"};
87+
Configurable<std::string> ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"};
88+
Configurable<std::string> ccdbPathGrp{"ccdbPathGrp", "GLO/GRP/GRP", "Path of the grp file (Run 2)"};
89+
Configurable<std::string> ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object (Run 3)"};
90+
91+
HfEventSelection hfEvSel; // event selection and monitoring
92+
93+
Service<o2::ccdb::BasicCCDBManager> ccdb;
7694

7795
using CandDplusData = soa::Filtered<soa::Join<aod::HfCand3Prong, aod::HfSelDplusToPiKPi>>;
7896
using CandDplusDataWithMl = soa::Filtered<soa::Join<aod::HfCand3Prong, aod::HfSelDplusToPiKPi, aod::HfMlDplusToPiKPi>>;
@@ -85,6 +103,7 @@ struct HfTaskDplus {
85103

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

106+
Preslice<aod::HfCand3Prong> candDplusPerCollision = aod::hf_cand::collisionId;
88107
Preslice<aod::McParticles> mcParticlesPerMcCollision = aod::mcparticle::mcCollisionId;
89108
PresliceUnsorted<aod::McCollisionLabels> recoColPerMcCollision = aod::mccollisionlabel::mcCollisionId;
90109

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

144+
HistogramRegistry qaRegistry{"QAHistos", {}, OutputObjHandlingPolicy::AnalysisObject};
145+
125146
void init(InitContext&)
126147
{
127-
std::array<bool, 4> doprocess{doprocessData, doprocessDataWithMl, doprocessMc, doprocessMcWithMl};
148+
std::array<bool, 5> doprocess{doprocessData, doprocessDataWithMl, doprocessMc, doprocessMcWithMl, doprocessDataWithMlWithUpc};
128149
if ((std::accumulate(doprocess.begin(), doprocess.end(), 0)) != 1) {
129150
LOGP(fatal, "Only one process function should be enabled! Please check your configuration!");
130151
}
@@ -239,6 +260,19 @@ struct HfTaskDplus {
239260
registry.add("hSparseMassGenPrompt", "THn for gen Prompt Dplus", HistType::kTHnSparseF, axesGenPrompt);
240261
registry.add("hSparseMassGenFD", "THn for gen FD Dplus", HistType::kTHnSparseF, axesGenFD);
241262
}
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();
242276
}
243277

244278
// Fill histograms of quantities for the reconstructed Dplus candidates
@@ -646,6 +680,71 @@ struct HfTaskDplus {
646680
}
647681
}
648682

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+
649748
// process functions
650749
void processData(CandDplusData const& candidates, CollisionsCent const& collisions)
651750
{
@@ -678,6 +777,19 @@ struct HfTaskDplus {
678777
runAnalysisMcGen<true>(mcGenCollisions, mcRecoCollisions, mcGenParticles);
679778
}
680779
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);
681793
};
682794

683795
WorkflowSpec defineDataProcessing(ConfigContext const& cfgc)

0 commit comments

Comments
 (0)