Skip to content

Commit 0ee8d9f

Browse files
authored
[PWGCF] Added option of poissonian sampling of efficiency to corrected multiplicity estimate in filterCorrelations.cxx (#18672)
1 parent 86627a0 commit 0ee8d9f

1 file changed

Lines changed: 49 additions & 5 deletions

File tree

‎PWGCF/TableProducer/filterCorrelations.cxx‎

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
#include <experimental/type_traits> // required for is_detected
5656
#include <limits>
5757
#include <memory>
58+
#include <random>
5859
#include <string>
5960
#include <type_traits>
6061
#include <vector>
@@ -120,14 +121,16 @@ struct FilterCF {
120121
O2_DEFINE_CONFIGURABLE(cfgEfficiencyMultiplicity, std::string, "", "Multiplicity efficiency (RecoAll / MC): CCDB path or local ROOT file with a 4D ccdb_object (eta, pT, multiplicity, z-vtx); empty disables CFCollisionsExtra output");
121122
O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, int, 0, "0 = CCDB efficiency, 1 = local ROOT efficiency");
122123
O2_DEFINE_CONFIGURABLE(cfgMultiplicityTrackBitMask, uint16_t, 0, "Required track-type bits for corrected multiplicity; match cfgTrackBitMask used to produce the efficiency (0 = all stored tracks)");
124+
O2_DEFINE_CONFIGURABLE(cfgMultiplicityPoisson, bool, false, "Use reproducibly seeded Nvalid + Poisson(sum(1/eff - 1)) for multiplicity correction; skips eff > 1 tracks");
125+
O2_DEFINE_CONFIGURABLE(cfgMultiplicityPoissonSeed, uint32_t, 1, "Base seed for reproducible per-dataframe Poisson multiplicity sampling");
123126

124127
// Filters and input definitions
125128
Filter collisionZVtxFilter = nabs(aod::collision::posZ) < cfgCutVertex;
126129
Filter collisionVertexTypeFilter = (cfgCollisionFlags == 0) || ((aod::collision::flags & cfgCollisionFlags) == cfgCollisionFlags);
127130

128131
// TODO how to have this in the second task? For now they are copied
129132
Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPt);
130-
Filter trackSelection = (requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true);
133+
Filter trackSelection = (requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast<uint8_t>(true));
131134

132135
Filter mcCollisionFilter = nabs(aod::mccollision::posZ) < cfgCutVertex;
133136

@@ -160,6 +163,7 @@ struct FilterCF {
160163
// Own local histograms independently of their input file. CCDB owns its objects.
161164
std::unique_ptr<THn> localMultiplicityEfficiency;
162165
THn* mEfficiency = nullptr;
166+
std::mt19937_64 mMultiplicityGenerator;
163167
static constexpr int MultiplicityEfficiencyDimensions = 4;
164168

165169
// persistent caches
@@ -168,6 +172,9 @@ struct FilterCF {
168172

169173
void init(InitContext& initContext)
170174
{
175+
if (cfgMultiplicityPoisson && !doprocessDataframeSeed) {
176+
LOGF(fatal, "Poisson multiplicity sampling requires processDataframeSeed");
177+
}
171178
if (!cfgEfficiencyMultiplicity.value.empty()) {
172179
bool processTracksEnabled = false;
173180
if (!o2::common::core::getTaskOptionValue(initContext, "multiplicity-selector", "processTracks", processTracksEnabled, false)) {
@@ -394,6 +401,7 @@ struct FilterCF {
394401
auto* efficiency = loadMultiplicityEfficiency(timestamp);
395402
double correctedMultiplicity = 0.;
396403
size_t skippedTracks = 0;
404+
double totalMeanMissing = 0.;
397405
for (const auto& track : tracks) {
398406
// Match the tracks written by the corresponding data/MC producer path.
399407
if (!isTrackSelected(track, true)) {
@@ -403,14 +411,29 @@ struct FilterCF {
403411
// Keep the original estimator as the map coordinate, including for centrality.
404412
const std::array<double, MultiplicityEfficiencyDimensions> values{track.eta(), track.pt(), collision.multiplicity(), collision.posZ()};
405413
const double eff = efficiency->GetBinContent(efficiency->GetBin(values.data()));
406-
if (!std::isfinite(eff) || eff <= 0.) {
414+
if (!std::isfinite(eff) || eff <= 0. || (cfgMultiplicityPoisson && eff > 1.)) {
407415
++skippedTracks;
408416
continue;
409417
}
410-
correctedMultiplicity += 1. / eff;
418+
if (cfgMultiplicityPoisson) {
419+
correctedMultiplicity += 1.;
420+
totalMeanMissing += 1. / eff - 1.;
421+
} else {
422+
correctedMultiplicity += 1. / eff;
423+
}
424+
}
425+
if (cfgMultiplicityPoisson) {
426+
if (!std::isfinite(totalMeanMissing) || totalMeanMissing >= static_cast<double>(std::numeric_limits<uint64_t>::max())) {
427+
LOGF(fatal, "Poisson multiplicity mean exceeds the sampling range: %g", totalMeanMissing);
428+
return 0.f;
429+
}
430+
// Sample from sum of missing count (Poisson sum rule)
431+
if (totalMeanMissing > 0.) {
432+
correctedMultiplicity += std::poisson_distribution<uint64_t>(totalMeanMissing)(mMultiplicityGenerator);
433+
}
411434
}
412435
if (cfgVerbosity > 0 && skippedTracks > 0) {
413-
LOGF(warning, "Skipped %zu tracks with invalid efficiency while correcting collision %lld", skippedTracks, static_cast<int64_t>(collision.globalIndex()));
436+
LOGF(warning, "Skipped %zu tracks with invalid or unsupported efficiency while correcting collision %lld", skippedTracks, static_cast<int64_t>(collision.globalIndex()));
414437
}
415438
if (!std::isfinite(correctedMultiplicity) || correctedMultiplicity > std::numeric_limits<float>::max()) {
416439
LOGF(fatal, "Corrected multiplicity cannot be represented as a float: %g", correctedMultiplicity);
@@ -434,6 +457,27 @@ struct FilterCF {
434457
return true;
435458
}
436459

460+
// This process must run before the collision processes below.
461+
void processDataframeSeed(aod::Origins const& origins)
462+
{
463+
if (!cfgMultiplicityPoisson) {
464+
return;
465+
}
466+
if (origins.size() == 0) {
467+
LOGF(fatal, "Cannot seed Poisson multiplicity sampling without a dataframe ID");
468+
return;
469+
}
470+
const uint64_t dataframeId = origins.iteratorAt(0).dataframeID();
471+
std::seed_seq seed{cfgMultiplicityPoissonSeed.value, static_cast<uint32_t>(dataframeId),
472+
static_cast<uint32_t>(dataframeId >> std::numeric_limits<uint32_t>::digits)};
473+
std::array<uint32_t, 2> seedWords{};
474+
seed.generate(seedWords.begin(), seedWords.end());
475+
// Speed up seeding by creating one 64-bit seed from the dataframe information.
476+
const uint64_t dataframeSeed = (static_cast<uint64_t>(seedWords[0]) << std::numeric_limits<uint32_t>::digits) | seedWords[1];
477+
mMultiplicityGenerator.seed(dataframeSeed);
478+
}
479+
PROCESS_SWITCH(FilterCF, processDataframeSeed, "Seed Poisson multiplicity sampling for each dataframe", false);
480+
437481
template <class T>
438482
using HasMultTables = decltype(std::declval<T&>().multNTracksPV());
439483

@@ -815,7 +859,7 @@ struct MultiplicitySelector {
815859
O2_DEFINE_CONFIGURABLE(cfgCutEta, float, 0.8f, "Eta range for tracks");
816860

817861
Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPt);
818-
Filter trackSelection = (requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true);
862+
Filter trackSelection = (requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast<uint8_t>(true));
819863

820864
void init(InitContext&)
821865
{

0 commit comments

Comments
 (0)