Skip to content

Commit 7a97c33

Browse files
committed
Add efficiency and acceptance methods for Run2/3
1 parent dc6f0e6 commit 7a97c33

1 file changed

Lines changed: 220 additions & 10 deletions

File tree

‎PWGCF/GenericFramework/Tasks/energyFlowGfw.cxx‎

Lines changed: 220 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,15 @@
3636
#include <Framework/HistogramRegistry.h>
3737
#include <Framework/HistogramSpec.h>
3838
#include <Framework/InitContext.h>
39+
#include <Framework/O2DatabasePDGPlugin.h>
3940
#include <Framework/runDataProcessing.h>
4041

4142
#include <TFile.h>
4243
#include <TH1.h>
4344
#include <THn.h>
4445
#include <TNamed.h>
4546
#include <TObjArray.h>
47+
#include <TPDGCode.h>
4648
#include <TRandom3.h>
4749

4850
#include <algorithm>
@@ -78,7 +80,7 @@ struct EnergyFlowGfw {
7880

7981
enum ActivityAxisMode : int {
8082
CentralityAxis = 0,
81-
NGlobalTracksAxis,
83+
NchAxis,
8284
ZdcSpectatorAxis
8385
};
8486

@@ -99,6 +101,20 @@ struct EnergyFlowGfw {
99101
HasAllTiming
100102
};
101103

104+
enum EfficiencyStep : uint8_t {
105+
EfficiencyGenerated = 0,
106+
EfficiencyReconstructed,
107+
EfficiencyStepCount
108+
};
109+
110+
enum EfficiencySpecies : uint8_t {
111+
EfficiencyCharged = 0,
112+
EfficiencyPion,
113+
EfficiencyKaon,
114+
EfficiencyProton,
115+
EfficiencySpeciesCount
116+
};
117+
102118
struct : ConfigurableGroup {
103119
Configurable<float> vertexZ{"vertexZ", 10.f, "Accepted |z-vertex| (cm)"};
104120
Configurable<std::pair<float, float>> ptCuts{"ptCuts", {0.2f, 5.f}, "Track pT cuts (GeV/c)"};
@@ -118,6 +134,8 @@ struct EnergyFlowGfw {
118134
Configurable<int> cfgBootstrapSamples{"cfgBootstrapSamples", 10, "Number of FlowContainer bootstrap subsamples; zero disables bootstrapping"};
119135
Configurable<int> cfgActivityAxis{"cfgActivityAxis", CentralityAxis, "FlowContainer x-axis: 0=centrality, 1=number of filtered global tracks, 2=corrected ZDC spectator estimate"};
120136
Configurable<float> cfgMaxCentrality{"cfgMaxCentrality", 100.f, "Maximum accepted centrality percentile"};
137+
Configurable<bool> cfgFillEfficiency{"fillEfficiency", false, "Fill MC tracking efficiency; ignored by data processes"};
138+
Configurable<bool> cfgFillAcceptance{"fillAcceptance", false, "Fill GFW acceptance weights instead of running the reconstructed analysis"};
121139

122140
Configurable<float> cfgSpectatorEnergyPerNucleon{"cfgSpectatorEnergyPerNucleon", 1.f, "ZDC energy corresponding to one spectator nucleon"};
123141
Configurable<float> cfgMaxTotalSpectators{"cfgMaxTotalSpectators", 500.f, "Maximum accepted spectator count from ZNA+ZNC+ZPA+ZPC"};
@@ -152,16 +170,26 @@ struct EnergyFlowGfw {
152170
((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast<uint8_t>(true)));
153171

154172
using CollisionsRun2 = soa::Filtered<soa::Join<aod::Collisions, aod::EvSels, aod::CentRun2V0Ms>>;
173+
using CollisionsRun2MC = soa::Join<aod::Collisions, aod::EvSels, aod::CentRun2V0Ms, aod::McCollisionLabels>;
155174
using CollisionsRun3 = soa::Filtered<soa::Join<aod::Collisions, aod::EvSels, aod::CentFT0Cs>>;
175+
using CollisionsRun3MC = soa::Join<aod::Collisions, aod::EvSels, aod::CentFT0Cs, aod::McCollisionLabels>;
156176
using Tracks = soa::Filtered<soa::Join<aod::Tracks, aod::TracksExtra, aod::TrackSelection,
157177
aod::pidTPCFullPi, aod::pidTPCFullKa, aod::pidTPCFullPr,
158-
aod::pidTOFbeta, aod::pidTOFFullPi, aod::pidTOFFullKa, aod::pidTOFFullPr>>;
178+
aod::pidTOFFullPi, aod::pidTOFFullKa, aod::pidTOFFullPr>>;
179+
using TracksMC = soa::Filtered<soa::Join<aod::Tracks, aod::TracksExtra, aod::TrackSelection,
180+
aod::pidTPCFullPi, aod::pidTPCFullKa, aod::pidTPCFullPr,
181+
aod::pidTOFFullPi, aod::pidTOFFullKa, aod::pidTOFFullPr,
182+
aod::McTrackLabels>>;
159183
using BCsRun2 = soa::Join<aod::BCs, aod::Timestamps, aod::BcSels, aod::Run2MatchedToBCSparse>;
160184
using BCsRun3 = soa::Join<aod::BCs, aod::Timestamps, aod::BcSels, aod::Run3MatchedToBCSparse>;
161185

186+
Preslice<TracksMC> tracksPerCollision = aod::track::collisionId;
187+
162188
OutputObj<FlowContainer> flowContainer{"FlowContainer"};
189+
OutputObj<GFWWeights> acceptanceWeights{GFWWeights("weights")};
163190
HistogramRegistry registry{"registry"};
164191
Service<o2::ccdb::BasicCCDBManager> ccdb{};
192+
Service<o2::framework::O2DatabasePDG> pdgDB{};
165193

166194
std::unique_ptr<GFW> gfw{std::make_unique<GFW>()};
167195
std::unique_ptr<TRandom3> random{std::make_unique<TRandom3>(0)};
@@ -176,8 +204,9 @@ struct EnergyFlowGfw {
176204

177205
void init(InitContext const&)
178206
{
179-
if (doprocessRun2 && doprocessRun3) {
180-
LOGF(fatal, "Enable only one input format: Run 2 AOD or Run 3 AOD");
207+
const int enabledProcesses = static_cast<int>(doprocessRun2) + static_cast<int>(doprocessRun2MC) + static_cast<int>(doprocessRun3) + static_cast<int>(doprocessRun3MC);
208+
if (enabledProcesses > 1) {
209+
LOGF(fatal, "Enable only one input process: Run 2 data, Run 2 MC, Run 3 data, or Run 3 MC");
181210
}
182211
if (cuts.ptCuts->first >= cuts.ptCuts->second || cuts.etaMax <= 0.f) {
183212
LOGF(fatal, "Require ptMin < ptMax and etaMax > 0");
@@ -194,8 +223,11 @@ struct EnergyFlowGfw {
194223
if (cfgActivityAxis < CentralityAxis || cfgActivityAxis > ZdcSpectatorAxis) {
195224
LOGF(fatal, "cfgActivityAxis must be 0 (centrality), 1 (filtered global tracks), or 2 (corrected ZDC spectators)");
196225
}
197-
if (cfgMaxCentrality < 0.f || cfgMaxCentrality > 100.f || cfgSpectatorEnergyPerNucleon <= 0.f || cfgMaxTotalSpectators < 0.f || cfgZdcResponseZNA <= 0.f || cfgZdcResponseZNC <= 0.f || cfgZdcResponseZPA <= 0.f || cfgZdcResponseZPC <= 0.f || cfgZdcMaxAbsTimeForQa <= 0.f) {
198-
LOGF(fatal, "The maximum centrality must be in [0, 100], the spectator energy scale, ZDC response factors, and ZDC QA time range must be positive, and the spectator threshold non-negative");
226+
if (!std::isfinite(cfgMaxCentrality.value) || cfgMaxCentrality < 0.f || cfgSpectatorEnergyPerNucleon <= 0.f || cfgMaxTotalSpectators < 0.f || cfgZdcResponseZNA <= 0.f || cfgZdcResponseZNC <= 0.f || cfgZdcResponseZPA <= 0.f || cfgZdcResponseZPC <= 0.f || cfgZdcMaxAbsTimeForQa <= 0.f) {
227+
LOGF(fatal, "The maximum centrality must be finite and non-negative, the spectator energy scale, ZDC response factors, and ZDC QA time range must be positive, and the spectator threshold non-negative");
228+
}
229+
if (cfgMaxCentrality > 100.f) {
230+
LOGF(warning, "cfgMaxCentrality is %.1f (> 100); dummy centrality values will be accepted. Use the Nch or spectator activity axis when calibrated centrality is unavailable", cfgMaxCentrality.value);
199231
}
200232

201233
const int numberOfRegions = cfgRegions->GetSize();
@@ -247,7 +279,7 @@ struct EnergyFlowGfw {
247279
gfw->CreateRegions();
248280
flowContainer.setObject(new FlowContainer("FlowContainer"));
249281
const AxisSpec activityAxis = [this]() {
250-
if (cfgActivityAxis.value == NGlobalTracksAxis) {
282+
if (cfgActivityAxis.value == NchAxis) {
251283
return AxisSpec{axisNGlobalTracks, "N_{global tracks}"};
252284
}
253285
if (cfgActivityAxis.value == ZdcSpectatorAxis) {
@@ -260,6 +292,9 @@ struct EnergyFlowGfw {
260292
} else {
261293
flowContainer->Initialize(profileNames.get(), activityAxis, cfgBootstrapSamples);
262294
}
295+
if (cfgFillAcceptance) {
296+
acceptanceWeights->init(true, false);
297+
}
263298

264299
const AxisSpec etaAxis{32, -cuts.etaMax.value, cuts.etaMax.value, "#eta"};
265300
registry.add("event/centrality", "Accepted events;centrality;events", HistType::kTH1F, {axisCentrality});
@@ -271,6 +306,19 @@ struct EnergyFlowGfw {
271306
registry.add("track/energy", "Energy entering the GFW;E_{T} (GeV);tracks", HistType::kTH1F, {axisEnergy});
272307
registry.add("track/pid", "PID mass assignment;species;tracks", HistType::kTH1F, {{4, -0.5, 3.5}});
273308

309+
if (cfgFillEfficiency && (doprocessRun2MC || doprocessRun3MC)) {
310+
const AxisSpec efficiencySpeciesAxis{EfficiencySpeciesCount, -0.5, static_cast<float>(EfficiencySpeciesCount) - 0.5, "particle"};
311+
const AxisSpec efficiencyStepAxis{EfficiencyStepCount, -0.5, static_cast<float>(EfficiencyStepCount) - 0.5, "generated/reconstructed"};
312+
registry.add("Efficiency/efficiencyHist", "MC tracking efficiency;#it{p}_{T}^{MC};centrality (%);particle;step", HistType::kTHnSparseF, {axisPt, axisCentrality, efficiencySpeciesAxis, efficiencyStepAxis});
313+
auto efficiencyHistogram = registry.get<THnSparse>(HIST("Efficiency/efficiencyHist"));
314+
efficiencyHistogram->GetAxis(2)->SetBinLabel(EfficiencyCharged + 1, "charged");
315+
efficiencyHistogram->GetAxis(2)->SetBinLabel(EfficiencyPion + 1, "pion");
316+
efficiencyHistogram->GetAxis(2)->SetBinLabel(EfficiencyKaon + 1, "kaon");
317+
efficiencyHistogram->GetAxis(2)->SetBinLabel(EfficiencyProton + 1, "proton");
318+
efficiencyHistogram->GetAxis(3)->SetBinLabel(EfficiencyGenerated + 1, "generated");
319+
efficiencyHistogram->GetAxis(3)->SetBinLabel(EfficiencyReconstructed + 1, "reconstructed");
320+
}
321+
274322
registry.add("zdc/qaSelection", "ZDC QA availability;condition;events", HistType::kTH1F, {{6, -0.5, 5.5}});
275323
registry.add("zdc/energyZNA", "Triggered events with matched ZDC;E_{ZNA};events", HistType::kTH1F, {axisZdcNeutronEnergy});
276324
registry.add("zdc/energyZNC", "Triggered events with matched ZDC;E_{ZNC};events", HistType::kTH1F, {axisZdcNeutronEnergy});
@@ -685,11 +733,79 @@ struct EnergyFlowGfw {
685733
return std::hypot(track.pt(), mass / std::cosh(track.eta()));
686734
}
687735

736+
template <typename TParticle>
737+
bool isEfficiencyParticle(TParticle const& particle) const
738+
{
739+
if (!particle.isPhysicalPrimary() || std::abs(particle.eta()) >= cuts.etaMax.value || particle.pt() <= cuts.ptCuts.value.first || particle.pt() >= cuts.ptCuts.value.second) {
740+
return false;
741+
}
742+
const auto* pdgParticle = pdgDB->GetParticle(particle.pdgCode());
743+
return pdgParticle && pdgParticle->Charge() != 0.;
744+
}
745+
746+
template <typename TParticle>
747+
int efficiencySpecies(TParticle const& particle) const
748+
{
749+
const int absolutePdg = std::abs(particle.pdgCode());
750+
if (absolutePdg == PDG_t::kPiPlus) {
751+
return EfficiencyPion;
752+
}
753+
if (absolutePdg == PDG_t::kKPlus) {
754+
return EfficiencyKaon;
755+
}
756+
if (absolutePdg == PDG_t::kProton) {
757+
return EfficiencyProton;
758+
}
759+
return EfficiencyCharged;
760+
}
761+
762+
template <typename TParticle>
763+
void fillEfficiencyParticle(TParticle const& particle, float centrality, EfficiencyStep step)
764+
{
765+
if (!isEfficiencyParticle(particle)) {
766+
return;
767+
}
768+
registry.fill(HIST("Efficiency/efficiencyHist"), particle.pt(), centrality, EfficiencyCharged, step);
769+
const int species = efficiencySpecies(particle);
770+
if (species != EfficiencyCharged) {
771+
registry.fill(HIST("Efficiency/efficiencyHist"), particle.pt(), centrality, species, step);
772+
}
773+
}
774+
775+
template <typename TParticles, typename TTracks>
776+
void fillTrackingEfficiency(int mcCollisionIndex, TParticles const& particles, TTracks const& tracks, float centrality)
777+
{
778+
for (const auto& particle : particles) {
779+
if (particle.mcCollisionId() != mcCollisionIndex) {
780+
continue;
781+
}
782+
fillEfficiencyParticle(particle, centrality, EfficiencyGenerated);
783+
}
784+
for (const auto& track : tracks) {
785+
if (!track.has_mcParticle()) {
786+
continue;
787+
}
788+
const auto& particle = track.mcParticle();
789+
if (particle.mcCollisionId() != mcCollisionIndex) {
790+
continue;
791+
}
792+
fillEfficiencyParticle(particle, centrality, EfficiencyReconstructed);
793+
}
794+
}
795+
796+
template <typename TCollision, typename TTracks>
797+
void fillAcceptanceWeights(TCollision const& collision, TTracks const& tracks, float centrality)
798+
{
799+
for (const auto& track : tracks) {
800+
acceptanceWeights->fill(track.phi(), track.eta(), collision.posZ(), track.pt(), centrality, 0);
801+
}
802+
}
803+
688804
template <typename TCollision, typename TTracks>
689805
void processCollision(TCollision const& collision, TTracks const& tracks, float centrality, float spectators)
690806
{
691807
float activity = centrality;
692-
if (cfgActivityAxis.value == NGlobalTracksAxis) {
808+
if (cfgActivityAxis.value == NchAxis) {
693809
activity = static_cast<float>(tracks.size());
694810
} else if (cfgActivityAxis.value == ZdcSpectatorAxis) {
695811
activity = spectators;
@@ -755,28 +871,122 @@ struct EnergyFlowGfw {
755871
if (!eventSelected<BCsRun2>(collision, collision.alias_bit(kINT7) && collision.sel7(), static_cast<float>(tracks.size()), collision.centRun2V0M(), spectators)) {
756872
return;
757873
}
874+
if (cfgFillAcceptance) {
875+
fillAcceptanceWeights(collision, tracks, collision.centRun2V0M());
876+
return;
877+
}
758878
const auto& bc = collision.foundBC_as<BCsRun2>();
759879
loadEfficiency(bc.timestamp());
760880
loadAcceptance(bc.timestamp(), bc.runNumber());
761881
processCollision(collision, tracks, collision.centRun2V0M(), spectators);
762882
}
763883
PROCESS_SWITCH(EnergyFlowGfw, processRun2, "Process Run 2 AOD data", false);
764884

885+
void processRun2MC(aod::McCollisions::iterator const& mcCollision,
886+
soa::SmallGroups<CollisionsRun2MC> const& collisions,
887+
BCsRun2 const&,
888+
aod::Zdcs const&,
889+
TracksMC const& tracks,
890+
aod::McParticles const& mcParticles)
891+
{
892+
if (collisions.size() != 1) {
893+
return;
894+
}
895+
896+
for (const auto& collision : collisions) {
897+
if (std::abs(collision.posZ()) >= cuts.vertexZ.value) {
898+
return;
899+
}
900+
const auto groupedTracks = tracks.sliceBy(tracksPerCollision, collision.globalIndex());
901+
float spectators = -1.f;
902+
if (!eventSelected<BCsRun2>(collision, collision.alias_bit(kINT7) && collision.sel7(), static_cast<float>(groupedTracks.size()), collision.centRun2V0M(), spectators)) {
903+
return;
904+
}
905+
906+
bool filledCorrections = false;
907+
if (cfgFillEfficiency) {
908+
fillTrackingEfficiency(mcCollision.globalIndex(), mcParticles, groupedTracks, collision.centRun2V0M());
909+
filledCorrections = true;
910+
}
911+
if (cfgFillAcceptance) {
912+
fillAcceptanceWeights(collision, groupedTracks, collision.centRun2V0M());
913+
filledCorrections = true;
914+
}
915+
if (filledCorrections) {
916+
return;
917+
}
918+
919+
const auto& bc = collision.foundBC_as<BCsRun2>();
920+
loadEfficiency(bc.timestamp());
921+
loadAcceptance(bc.timestamp(), bc.runNumber());
922+
processCollision(collision, groupedTracks, collision.centRun2V0M(), spectators);
923+
return;
924+
}
925+
}
926+
PROCESS_SWITCH(EnergyFlowGfw, processRun2MC, "Process matched Run 2 MC for efficiency, acceptance, or reconstructed closure", false);
927+
765928
void processRun3(CollisionsRun3::iterator const& collision, Tracks const& tracks, BCsRun3 const&, aod::Zdcs const&)
766929
{
767930
float spectators = -1.f;
768931
if (!eventSelected<BCsRun3>(collision, collision.sel8(), static_cast<float>(tracks.size()), collision.centFT0C(), spectators)) {
769932
return;
770933
}
934+
if (cfgFillAcceptance) {
935+
fillAcceptanceWeights(collision, tracks, collision.centFT0C());
936+
return;
937+
}
771938
const auto& bc = collision.foundBC_as<BCsRun3>();
772939
loadEfficiency(bc.timestamp());
773940
loadAcceptance(bc.timestamp(), bc.runNumber());
774941
processCollision(collision, tracks, collision.centFT0C(), spectators);
775942
}
776943
PROCESS_SWITCH(EnergyFlowGfw, processRun3, "Process Run 3 AOD data", true);
944+
945+
void processRun3MC(aod::McCollisions::iterator const& mcCollision,
946+
soa::SmallGroups<CollisionsRun3MC> const& collisions,
947+
BCsRun3 const&,
948+
aod::Zdcs const&,
949+
TracksMC const& tracks,
950+
aod::McParticles const& mcParticles)
951+
{
952+
if (collisions.size() != 1) {
953+
return;
954+
}
955+
956+
for (const auto& collision : collisions) {
957+
if (std::abs(collision.posZ()) >= cuts.vertexZ.value) {
958+
return;
959+
}
960+
const auto groupedTracks = tracks.sliceBy(tracksPerCollision, collision.globalIndex());
961+
float spectators = -1.f;
962+
if (!eventSelected<BCsRun3>(collision, collision.sel8(), static_cast<float>(groupedTracks.size()), collision.centFT0C(), spectators)) {
963+
return;
964+
}
965+
966+
bool filledCorrections = false;
967+
if (cfgFillEfficiency) {
968+
fillTrackingEfficiency(mcCollision.globalIndex(), mcParticles, groupedTracks, collision.centFT0C());
969+
filledCorrections = true;
970+
}
971+
if (cfgFillAcceptance) {
972+
fillAcceptanceWeights(collision, groupedTracks, collision.centFT0C());
973+
filledCorrections = true;
974+
}
975+
if (filledCorrections) {
976+
return;
977+
}
978+
979+
const auto& bc = collision.foundBC_as<BCsRun3>();
980+
loadEfficiency(bc.timestamp());
981+
loadAcceptance(bc.timestamp(), bc.runNumber());
982+
processCollision(collision, groupedTracks, collision.centFT0C(), spectators);
983+
return;
984+
}
985+
}
986+
PROCESS_SWITCH(EnergyFlowGfw, processRun3MC, "Process matched Run 3 MC for efficiency, acceptance, or reconstructed closure", false);
777987
};
778988

779-
WorkflowSpec defineDataProcessing(ConfigContext const& cfgc)
989+
WorkflowSpec defineDataProcessing(ConfigContext const& context)
780990
{
781-
return WorkflowSpec{adaptAnalysisTask<EnergyFlowGfw>(cfgc)};
991+
return WorkflowSpec{adaptAnalysisTask<EnergyFlowGfw>(context)};
782992
}

0 commit comments

Comments
 (0)