Skip to content

Commit 2b70ea4

Browse files
authored
[PWGCF] correlations task 2-prong track efficiency (#10307)
1 parent de2f0af commit 2b70ea4

File tree

5 files changed

+148
-33
lines changed

5 files changed

+148
-33
lines changed

PWGCF/DataModel/CorrelationsDerived.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ using CFTrackWithLabel = CFTracksWithLabel::iterator;
8585
//------transient CF-filter to CF-2prong-filter
8686
DECLARE_SOA_TABLE(CFCollRefs, "AOD", "CFCOLLREF", o2::soa::Index<>, track::CollisionId); //! Transient cf collision index table
8787

88+
// Reco
89+
8890
using CFCollRef = CFCollRefs::iterator;
8991

9092
namespace cftrackref
@@ -94,6 +96,16 @@ DECLARE_SOA_INDEX_COLUMN(Track, track);
9496
DECLARE_SOA_TABLE(CFTrackRefs, "AOD", "CFTRACKREF", o2::soa::Index<>, track::CollisionId, cftrackref::TrackId); //! Transient cf track index table
9597

9698
using CFTrackRef = CFTrackRefs::iterator;
99+
100+
// MC
101+
102+
namespace cfmcparticleref
103+
{
104+
DECLARE_SOA_INDEX_COLUMN(McParticle, mcParticle);
105+
} // namespace cfmcparticleref
106+
DECLARE_SOA_TABLE(CFMcParticleRefs, "AOD", "CFMCPARTICLEREF", o2::soa::Index<>, mcparticle::McCollisionId, cfmcparticleref::McParticleId); //! Transient cf track index table
107+
108+
using CFMcParticleRef = CFMcParticleRefs::iterator;
97109
//------
98110

99111
namespace cf2prongtrack
@@ -133,6 +145,17 @@ DECLARE_SOA_TABLE(CF2ProngTrackmls, "AOD", "CF2PRONGTRACKML", //! Reduced track
133145
using CF2ProngTrackml = CF2ProngTrackmls::iterator;
134146
//------
135147

148+
namespace cf2prongmcpart
149+
{
150+
DECLARE_SOA_INDEX_COLUMN_FULL(CFParticleDaugh0, cfParticleDaugh0, int, CFMcParticles, "_0"); //! Index to prong 1 CFMcParticle
151+
DECLARE_SOA_INDEX_COLUMN_FULL(CFParticleDaugh1, cfParticleDaugh1, int, CFMcParticles, "_1"); //! Index to prong 2 CFMcParticle
152+
} // namespace cf2prongmcpart
153+
DECLARE_SOA_TABLE(CF2ProngMcParts, "AOD", "CF2PRONGMCPART", //! Table for the daughter particles of a 2-prong particle, to be joined with CFMcParticles
154+
o2::soa::Index<>,
155+
cf2prongmcpart::CFParticleDaugh0Id,
156+
cf2prongmcpart::CFParticleDaugh1Id)
157+
using CF2ProngMcPart = CF2ProngMcParts::iterator;
158+
136159
} // namespace o2::aod
137160

138161
#endif // PWGCF_DATAMODEL_CORRELATIONSDERIVED_H_

PWGCF/JCorran/Tasks/jflucWeightsLoader.cxx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ struct JflucWeightsLoader {
5050

5151
THnF* ph = 0;
5252
TFile* pf = 0;
53-
THnF* pheff = 0;
53+
THnD* pheff = 0;
5454
TFile* pfeff = 0;
5555
int runNumber = 0;
5656
int timestamp = 0;
@@ -120,7 +120,7 @@ struct JflucWeightsLoader {
120120
LOGF(fatal, "Efficiency correction weights file not found: %s", cfgPathEffWeights.value.substr(8).c_str());
121121
}
122122
//
123-
if (!(pheff = pfeff->Get<THnF>("ccdb_object"))) {
123+
if (!(pheff = pfeff->Get<THnD>("ccdb_object"))) {
124124
LOGF(warning, "Efficiency correction histogram not found.");
125125
} else {
126126
LOGF(info, "Loaded efficiency correction histogram locally.");

PWGCF/TableProducer/filter2Prong.cxx

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ struct Filter2Prong {
4040
Produces<aod::CF2ProngTracks> output2ProngTracks;
4141
Produces<aod::CF2ProngTrackmls> output2ProngTrackmls;
4242

43+
Produces<aod::CF2ProngMcParts> output2ProngMcParts;
44+
4345
std::vector<float> mlvecd{};
4446
std::vector<float> mlvecdbar{};
4547

@@ -81,13 +83,11 @@ struct Filter2Prong {
8183
prongCFId[0], prongCFId[1], c.pt(), c.eta(), c.phi(), hfHelper.invMassD0ToPiK(c), aod::cf2prongtrack::D0ToPiK);
8284
if constexpr (std::experimental::is_detected<HasMLProb, typename HFCandidatesType::iterator>::value) {
8385
mlvecd.clear();
84-
for (float val : c.mlProbD0()) {
86+
for (float val : c.mlProbD0())
8587
mlvecd.push_back(val);
86-
}
8788
mlvecdbar.clear();
88-
for (float val : c.mlProbD0bar()) {
89+
for (float val : c.mlProbD0bar())
8990
mlvecdbar.push_back(val);
90-
}
9191
output2ProngTrackmls(cfcollisions.begin().globalIndex(), mlvecd, mlvecdbar);
9292
}
9393
}
@@ -97,30 +97,51 @@ struct Filter2Prong {
9797
prongCFId[0], prongCFId[1], c.pt(), c.eta(), c.phi(), hfHelper.invMassD0barToKPi(c), aod::cf2prongtrack::D0barToKPi);
9898
if constexpr (std::experimental::is_detected<HasMLProb, typename HFCandidatesType::iterator>::value) {
9999
mlvecd.clear();
100-
for (float val : c.mlProbD0()) {
100+
for (float val : c.mlProbD0())
101101
mlvecd.push_back(val);
102-
}
103102
mlvecdbar.clear();
104-
for (float val : c.mlProbD0bar()) {
103+
for (float val : c.mlProbD0bar())
105104
mlvecdbar.push_back(val);
106-
}
107105
output2ProngTrackmls(cfcollisions.begin().globalIndex(), mlvecd, mlvecdbar);
108106
}
109107
}
110108
}
111109
}
112110

113-
void processDataML(aod::Collisions::iterator const& cols, aod::BCsWithTimestamps const& bcs, aod::CFCollRefs const& cfcollisions, aod::CFTrackRefs const& cftracks, HFCandidatesML const& candidates)
111+
void processDataML(aod::Collisions::iterator const& col, aod::BCsWithTimestamps const& bcs, aod::CFCollRefs const& cfcollisions, aod::CFTrackRefs const& cftracks, HFCandidatesML const& candidates)
114112
{
115-
processDataT(cols, bcs, cfcollisions, cftracks, candidates);
113+
processDataT(col, bcs, cfcollisions, cftracks, candidates);
116114
}
117115
PROCESS_SWITCH(Filter2Prong, processDataML, "Process data D0 candidates with ML", false);
118116

119-
void processData(aod::Collisions::iterator const& cols, aod::BCsWithTimestamps const& bcs, aod::CFCollRefs const& cfcollisions, aod::CFTrackRefs const& cftracks, HFCandidates const& candidates)
117+
void processData(aod::Collisions::iterator const& col, aod::BCsWithTimestamps const& bcs, aod::CFCollRefs const& cfcollisions, aod::CFTrackRefs const& cftracks, HFCandidates const& candidates)
120118
{
121-
processDataT(cols, bcs, cfcollisions, cftracks, candidates);
119+
processDataT(col, bcs, cfcollisions, cftracks, candidates);
122120
}
123121
PROCESS_SWITCH(Filter2Prong, processData, "Process data D0 candidates", true);
122+
123+
void processMC(aod::McCollisions::iterator const&, aod::CFMcParticleRefs const& cfmcparticles, aod::McParticles const& mcparticles)
124+
{
125+
// The main filter outputs the primary MC particles. Here we just resolve the daughter indices that are needed for the efficiency matching.
126+
for (auto& r : cfmcparticles) {
127+
const auto& mcParticle = mcparticles.iteratorAt(r.mcParticleId());
128+
if (mcParticle.daughtersIds().size() != 2) {
129+
output2ProngMcParts(-1, -1);
130+
continue;
131+
}
132+
int prongCFId[2] = {-1, -1};
133+
for (uint i = 0; i < 2; ++i) {
134+
for (auto& cfmcpart : cfmcparticles) {
135+
if (mcParticle.daughtersIds()[i] == cfmcpart.mcParticleId()) {
136+
prongCFId[i] = cfmcpart.globalIndex();
137+
break;
138+
}
139+
}
140+
}
141+
output2ProngMcParts(prongCFId[0], prongCFId[1]);
142+
}
143+
}
144+
PROCESS_SWITCH(Filter2Prong, processMC, "Process MC 2-prong daughters", false);
124145
}; // struct
125146

126147
WorkflowSpec defineDataProcessing(ConfigContext const& cfgc)

PWGCF/TableProducer/filterCorrelations.cxx

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
// In applying this license CERN does not waive the privileges and immunities
99
// granted to it by virtue of its status as an Intergovernmental Organization
1010
// or submit itself to any jurisdiction.
11+
#include <vector>
12+
1113
#include "Framework/runDataProcessing.h"
1214
#include "Framework/AnalysisTask.h"
1315
#include "Framework/AnalysisDataModel.h"
@@ -62,9 +64,10 @@ struct FilterCF {
6264
O2_DEFINE_CONFIGURABLE(cfgMinOcc, int, 0, "minimum occupancy selection")
6365
O2_DEFINE_CONFIGURABLE(cfgMaxOcc, int, 3000, "maximum occupancy selection")
6466
O2_DEFINE_CONFIGURABLE(cfgCollisionFlags, uint16_t, aod::collision::CollisionFlagsRun2::Run2VertexerTracks, "Request collision flags if non-zero (0 = off, 1 = Run2VertexerTracks)")
65-
O2_DEFINE_CONFIGURABLE(cfgTransientTables, bool, false, "Output transient tables for collision and track IDs")
67+
O2_DEFINE_CONFIGURABLE(cfgTransientTables, bool, false, "Output transient tables for collision and track IDs to enable successive filtering tasks")
6668
O2_DEFINE_CONFIGURABLE(cfgTrackSelection, int, 0, "Type of track selection (0 = Run 2/3 without systematics | 1 = Run 3 with systematics)")
6769
O2_DEFINE_CONFIGURABLE(cfgMinMultiplicity, float, -1, "Minimum multiplicity considered for filtering (if value positive)")
70+
O2_DEFINE_CONFIGURABLE(cfgMcSpecialPDGs, std::vector<int>, {}, "Special MC PDG codes to include in the MC primary particle output (additional to charged particles). Empty = charged particles only.") // needed for some neutral particles
6871

6972
// Filters and input definitions
7073
Filter collisionZVtxFilter = nabs(aod::collision::posZ) < cfgCutVertex;
@@ -90,6 +93,11 @@ struct FilterCF {
9093

9194
Produces<aod::CFCollRefs> outputCollRefs;
9295
Produces<aod::CFTrackRefs> outputTrackRefs;
96+
Produces<aod::CFMcParticleRefs> outputMcParticleRefs;
97+
98+
// persistent caches
99+
std::vector<bool> mcReconstructedCache;
100+
std::vector<int> mcParticleLabelsCache;
93101

94102
template <typename TCollision>
95103
bool keepCollision(TCollision& collision)
@@ -182,11 +190,13 @@ struct FilterCF {
182190
soa::Filtered<soa::Join<aod::Tracks, aod::TracksExtra, aod::McTrackLabels, aod::TrackSelection>> const& tracks,
183191
aod::BCsWithTimestamps const&)
184192
{
185-
bool* reconstructed = new bool[allParticles.size()];
186-
int* mcParticleLabels = new int[allParticles.size()];
193+
mcReconstructedCache.reserve(allParticles.size());
194+
mcParticleLabelsCache.reserve(allParticles.size());
195+
mcReconstructedCache.clear();
196+
mcParticleLabelsCache.clear();
187197
for (int i = 0; i < allParticles.size(); i++) {
188-
reconstructed[i] = false;
189-
mcParticleLabels[i] = -1;
198+
mcReconstructedCache.push_back(false);
199+
mcParticleLabelsCache.push_back(-1);
190200
}
191201

192202
// PASS 1 on collisions: check which particles are kept
@@ -202,7 +212,7 @@ struct FilterCF {
202212

203213
for (auto& track : groupedTracks) {
204214
if (track.has_mcParticle()) {
205-
reconstructed[track.mcParticleId()] = true;
215+
mcReconstructedCache[track.mcParticleId()] = true;
206216
}
207217
}
208218
}
@@ -222,25 +232,29 @@ struct FilterCF {
222232
if (pdgparticle != nullptr) {
223233
sign = (pdgparticle->Charge() > 0) ? 1.0 : ((pdgparticle->Charge() < 0) ? -1.0 : 0.0);
224234
}
235+
236+
bool special = !cfgMcSpecialPDGs->empty() && std::find(cfgMcSpecialPDGs->begin(), cfgMcSpecialPDGs->end(), particle.pdgCode()) != cfgMcSpecialPDGs->end();
225237
bool primary = particle.isPhysicalPrimary() && sign != 0 && std::abs(particle.eta()) < cfgCutMCEta && particle.pt() > cfgCutMCPt;
226238
if (primary) {
227239
multiplicity++;
228240
}
229-
if (reconstructed[particle.globalIndex()] || primary) {
241+
if (mcReconstructedCache[particle.globalIndex()] || primary || special) {
230242
// keep particle
231243

232244
// use highest bit to flag if it is reconstructed
233245
uint8_t flags = particle.flags() & ~aod::cfmcparticle::kReconstructed; // clear bit in case of clashes in the future
234-
if (reconstructed[particle.globalIndex()]) {
246+
if (mcReconstructedCache[particle.globalIndex()]) {
235247
flags |= aod::cfmcparticle::kReconstructed;
236248
}
237249

238250
// NOTE using "outputMcCollisions.lastIndex()+1" here to allow filling of outputMcCollisions *after* the loop
239251
outputMcParticles(outputMcCollisions.lastIndex() + 1, truncateFloatFraction(particle.pt(), FLOAT_PRECISION), truncateFloatFraction(particle.eta(), FLOAT_PRECISION),
240252
truncateFloatFraction(particle.phi(), FLOAT_PRECISION), sign, particle.pdgCode(), flags);
253+
if (cfgTransientTables)
254+
outputMcParticleRefs(outputMcCollisions.lastIndex() + 1, particle.globalIndex());
241255

242256
// relabeling array
243-
mcParticleLabels[particle.globalIndex()] = outputMcParticles.lastIndex();
257+
mcParticleLabelsCache[particle.globalIndex()] = outputMcParticles.lastIndex();
244258
}
245259
}
246260
outputMcCollisions(mcCollision.posZ(), multiplicity);
@@ -261,26 +275,27 @@ struct FilterCF {
261275
// NOTE works only when we store all MC collisions (as we do here)
262276
outputCollisions(bc.runNumber(), collision.posZ(), collision.multiplicity(), bc.timestamp());
263277
outputMcCollisionLabels(collision.mcCollisionId());
278+
if (cfgTransientTables)
279+
outputCollRefs(collision.globalIndex());
264280

265281
for (auto& track : groupedTracks) {
266282
int mcParticleId = track.mcParticleId();
267283
if (mcParticleId >= 0) {
268-
mcParticleId = mcParticleLabels[track.mcParticleId()];
284+
mcParticleId = mcParticleLabelsCache[track.mcParticleId()];
269285
if (mcParticleId < 0) {
270-
LOGP(fatal, "processMC: Track {} is referring to a MC particle which we do not store {} {} (reco flag {})", track.index(), track.mcParticleId(), mcParticleId, reconstructed[track.mcParticleId()]);
286+
LOGP(fatal, "processMC: Track {} is referring to a MC particle which we do not store {} {} (reco flag {})", track.index(), track.mcParticleId(), mcParticleId, static_cast<bool>(mcReconstructedCache[track.mcParticleId()]));
271287
}
272288
}
273289
outputTracks(outputCollisions.lastIndex(),
274290
truncateFloatFraction(track.pt()), truncateFloatFraction(track.eta()), truncateFloatFraction(track.phi()), track.sign(), getTrackType(track));
275291
outputTrackLabels(mcParticleId);
292+
if (cfgTransientTables)
293+
outputTrackRefs(collision.globalIndex(), track.globalIndex());
276294

277295
yields->Fill(collision.multiplicity(), track.pt(), track.eta());
278296
etaphi->Fill(collision.multiplicity(), track.eta(), track.phi());
279297
}
280298
}
281-
282-
delete[] reconstructed;
283-
delete[] mcParticleLabels;
284299
}
285300
PROCESS_SWITCH(FilterCF, processMC, "Process MC", false);
286301

PWGCF/Tasks/correlations.cxx

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,9 @@ struct CorrelationTask {
133133
OutputObj<CorrelationContainer> same{"sameEvent"};
134134
OutputObj<CorrelationContainer> mixed{"mixedEvent"};
135135

136+
// persistent caches
136137
std::vector<float> efficiencyAssociatedCache;
138+
std::vector<int> p2indexCache;
137139

138140
struct Config {
139141
bool mPairCuts = false;
@@ -215,7 +217,13 @@ struct CorrelationTask {
215217
same->setTrackEtaCut(cfgCutEta);
216218
mixed->setTrackEtaCut(cfgCutEta);
217219

218-
efficiencyAssociatedCache.reserve(512);
220+
if (!cfgEfficiencyAssociated.value.empty())
221+
efficiencyAssociatedCache.reserve(512);
222+
if (doprocessMCEfficiency2Prong) {
223+
p2indexCache.reserve(16);
224+
if (cfgMcTriggerPDGs->empty())
225+
LOGF(fatal, "At least one PDG code in {} is to be selected to process 2-prong efficiency.", cfgMcTriggerPDGs.name);
226+
}
219227

220228
// o2-ccdb-upload -p Users/jgrosseo/correlations/LHC15o -f /tmp/correction_2011_global.root -k correction
221229

@@ -949,11 +957,8 @@ struct CorrelationTask {
949957
case 2212: // proton
950958
case -2212:
951959
return 2;
952-
case 421: // D0
953-
case -421:
960+
default: // NOTE. The efficiency histogram is hardcoded to contain 4 species. Anything special will have the last slot.
954961
return 3;
955-
default:
956-
return 4;
957962
}
958963
}
959964

@@ -1004,6 +1009,57 @@ struct CorrelationTask {
10041009
}
10051010
PROCESS_SWITCH(CorrelationTask, processMCEfficiency, "MC: Extract efficiencies", false);
10061011

1012+
Preslice<aod::CF2ProngTracks> perCollision2Prong = aod::cftrack::cfCollisionId;
1013+
void processMCEfficiency2Prong(soa::Filtered<aod::CFMcCollisions>::iterator const& mcCollision, soa::Join<aod::CFMcParticles, aod::CF2ProngMcParts> const& mcParticles, soa::SmallGroups<aod::CFCollisionsWithLabel> const& collisions, aod::CFTracksWithLabel const&, aod::CF2ProngTracks const& p2tracks)
1014+
{
1015+
auto multiplicity = mcCollision.multiplicity();
1016+
if (cfgCentBinsForMC > 0) {
1017+
if (collisions.size() == 0) {
1018+
return;
1019+
}
1020+
for (const auto& collision : collisions) {
1021+
multiplicity = collision.multiplicity();
1022+
}
1023+
}
1024+
// Primaries
1025+
p2indexCache.clear();
1026+
for (const auto& mcParticle : mcParticles) {
1027+
if (mcParticle.isPhysicalPrimary() && std::find(cfgMcTriggerPDGs->begin(), cfgMcTriggerPDGs->end(), mcParticle.pdgCode()) != cfgMcTriggerPDGs->end()) {
1028+
same->getTrackHistEfficiency()->Fill(CorrelationContainer::MC, mcParticle.eta(), mcParticle.pt(), getSpecies(mcParticle.pdgCode()), multiplicity, mcCollision.posZ());
1029+
if (mcParticle.cfParticleDaugh0Id() < 0 || mcParticle.cfParticleDaugh1Id() < 0)
1030+
continue;
1031+
p2indexCache.push_back(mcParticle.globalIndex());
1032+
}
1033+
}
1034+
for (const auto& collision : collisions) {
1035+
auto grouped2ProngTracks = p2tracks.sliceBy(perCollision2Prong, collision.globalIndex());
1036+
1037+
for (const auto& p2track : grouped2ProngTracks) {
1038+
// Check if the mc particles of the prongs are found.
1039+
const auto& p0 = p2track.cfTrackProng0_as<aod::CFTracksWithLabel>();
1040+
const auto& p1 = p2track.cfTrackProng1_as<aod::CFTracksWithLabel>();
1041+
if (p0.has_cfMCParticle() && p1.has_cfMCParticle()) {
1042+
// find the 2-prong MC particle by the daughter MC particle IDs
1043+
auto m = std::find_if(p2indexCache.begin(), p2indexCache.end(), [&](const auto& t) -> bool {
1044+
const auto& mcParticle = mcParticles.iteratorAt(t);
1045+
return p0.cfMCParticleId() == mcParticle.cfParticleDaugh0Id() && p1.cfMCParticleId() == mcParticle.cfParticleDaugh1Id();
1046+
});
1047+
if (m == p2indexCache.end())
1048+
continue;
1049+
const auto& mcParticle = mcParticles.iteratorAt(*m);
1050+
if (mcParticle.isPhysicalPrimary()) {
1051+
same->getTrackHistEfficiency()->Fill(CorrelationContainer::RecoPrimaries, mcParticle.eta(), mcParticle.pt(), getSpecies(mcParticle.pdgCode()), multiplicity, mcCollision.posZ());
1052+
}
1053+
same->getTrackHistEfficiency()->Fill(CorrelationContainer::RecoAll, mcParticle.eta(), mcParticle.pt(), getSpecies(mcParticle.pdgCode()), multiplicity, mcCollision.posZ());
1054+
} else {
1055+
// fake track
1056+
same->getTrackHistEfficiency()->Fill(CorrelationContainer::Fake, p2track.eta(), p2track.pt(), 0, multiplicity, mcCollision.posZ());
1057+
}
1058+
}
1059+
}
1060+
}
1061+
PROCESS_SWITCH(CorrelationTask, processMCEfficiency2Prong, "MC: Extract efficiencies for 2-prong particles", false);
1062+
10071063
// NOTE SmallGroups includes soa::Filtered always
10081064
void processMCSameDerived(soa::Filtered<aod::CFMcCollisions>::iterator const& mcCollision, soa::Filtered<aod::CFMcParticles> const& mcParticles, soa::SmallGroups<aod::CFCollisionsWithLabel> const& collisions)
10091065
{

0 commit comments

Comments
 (0)