Skip to content

Commit d33f68c

Browse files
committed
Add multiplicity corrected row to CFCollision table in CF derived data
1 parent 3894426 commit d33f68c

2 files changed

Lines changed: 105 additions & 5 deletions

File tree

PWGCF/DataModel/CorrelationsDerived.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,14 @@ using CFMultiplicity = CFMultiplicities::iterator;
6565

6666
namespace cfcollision
6767
{
68-
DECLARE_SOA_INDEX_COLUMN(CFMcCollision, cfMcCollision); //! Index to reduced MC collision
69-
DECLARE_SOA_COLUMN(Multiplicity, multiplicity, float); //! Centrality/multiplicity value
68+
DECLARE_SOA_INDEX_COLUMN(CFMcCollision, cfMcCollision); //! Index to reduced MC collision
69+
DECLARE_SOA_COLUMN(Multiplicity, multiplicity, float); //! Centrality/multiplicity value
70+
DECLARE_SOA_COLUMN(MultiplicityCorrected, multiplicityCorrected, float); //! Efficiency-corrected track count; original multiplicity when correction is disabled
7071
} // namespace cfcollision
7172
DECLARE_SOA_TABLE(CFCollisions, "AOD", "CFCOLLISION", //! Reduced collision table
7273
o2::soa::Index<>,
7374
bc::RunNumber, collision::PosZ,
74-
cfcollision::Multiplicity, timestamp::Timestamp);
75+
cfcollision::Multiplicity, timestamp::Timestamp, cfcollision::MultiplicityCorrected);
7576
DECLARE_SOA_TABLE(CFCollLabels, "AOD", "CFCOLLLABEL", //! Labels for reduced collision table
7677
cfcollision::CFMcCollisionId);
7778
using CFCollision = CFCollisions::iterator;

PWGCF/TableProducer/filterCorrelations.cxx

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "Common/DataModel/PIDResponseTPC.h"
2222
#include "Common/DataModel/TrackSelectionTables.h"
2323

24+
#include <CCDB/BasicCCDBManager.h>
2425
#include <Framework/AnalysisDataModel.h>
2526
#include <Framework/AnalysisHelpers.h>
2627
#include <Framework/AnalysisTask.h>
@@ -35,14 +36,21 @@
3536
#include <MathUtils/detail/TypeTruncation.h>
3637
#include <ReconstructionDataFormats/PID.h>
3738

39+
#include <TFile.h>
3840
#include <TH3.h>
41+
#include <THn.h>
3942
#include <TParticlePDG.h>
4043

4144
#include <Rtypes.h>
4245

4346
#include <algorithm>
47+
#include <array>
48+
#include <cmath>
4449
#include <cstdint>
4550
#include <experimental/type_traits> // required for is_detected
51+
#include <limits>
52+
#include <memory>
53+
#include <string>
4654
#include <type_traits>
4755
#include <vector>
4856

@@ -58,6 +66,7 @@ using namespace o2::math_utils::detail;
5866

5967
struct FilterCF {
6068
Service<o2::framework::O2DatabasePDG> pdg;
69+
Service<o2::ccdb::BasicCCDBManager> ccdb;
6170

6271
enum TrackSelectionCuts1 : uint8_t {
6372
kTrackSelected = BIT(0),
@@ -103,6 +112,10 @@ struct FilterCF {
103112
O2_DEFINE_CONFIGURABLE(chi2peritscluster, float, 36, "maximum Chi2 / cluster for the ITS track segment")
104113
O2_DEFINE_CONFIGURABLE(cfgEstimatorBitMask, uint16_t, 0, "BitMask for multiplicity estimators to be included in the CFMultSet tables.");
105114

115+
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 copies the original multiplicity")
116+
O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, int, 0, "0 = CCDB efficiency, 1 = local ROOT efficiency")
117+
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)")
118+
106119
// Filters and input definitions
107120
Filter collisionZVtxFilter = nabs(aod::collision::posZ) < cfgCutVertex;
108121
Filter collisionVertexTypeFilter = (cfgCollisionFlags == 0) || ((aod::collision::flags & cfgCollisionFlags) == cfgCollisionFlags);
@@ -135,12 +148,36 @@ struct FilterCF {
135148
Produces<aod::CFMultSets> outputMultSets;
136149
std::vector<float> multiplicities{};
137150

151+
// Own local histograms independently of their input file. CCDB owns its objects.
152+
std::unique_ptr<THn> localMultiplicityEfficiency;
153+
138154
// persistent caches
139155
std::vector<bool> mcReconstructedCache;
140156
std::vector<int> mcParticleLabelsCache;
141157

142158
void init(InitContext&)
143159
{
160+
if (!cfgEfficiencyMultiplicity.value.empty()) {
161+
if (cfgLocalEfficiency != 0 && cfgLocalEfficiency != 1) {
162+
LOGF(fatal, "cfgLocalEfficiency must be 0 (CCDB) or 1 (local ROOT file)");
163+
}
164+
if (cfgMultiplicityTrackBitMask > std::numeric_limits<uint8_t>::max()) {
165+
LOGF(fatal, "cfgMultiplicityTrackBitMask must fit the 8-bit track type");
166+
}
167+
if (cfgLocalEfficiency == 1) {
168+
std::unique_ptr<TFile> file(TFile::Open(cfgEfficiencyMultiplicity.value.c_str(), "READ"));
169+
if (!file || file->IsZombie()) {
170+
LOGF(fatal, "Could not open multiplicity efficiency file %s", cfgEfficiencyMultiplicity.value.c_str());
171+
}
172+
auto* efficiency = dynamic_cast<THn*>(file->Get("ccdb_object"));
173+
validateMultiplicityEfficiency(efficiency);
174+
localMultiplicityEfficiency.reset(static_cast<THn*>(efficiency->Clone()));
175+
} else {
176+
ccdb->setURL("http://alice-ccdb.cern.ch");
177+
ccdb->setCaching(true);
178+
ccdb->setLocalObjectValidityChecking();
179+
}
180+
}
144181
if (doprocessTrackQA) {
145182
registrytrackQA.add("zvtx", "Z Vertex position; posz (cm); Events", HistType::kTH1F, {{100, -12, 12}});
146183
registrytrackQA.add("eta", "eta distribution; eta; arb. units", HistType::kTH1F, {{100, -2, 2}});
@@ -280,6 +317,68 @@ struct FilterCF {
280317
return dcaXyConst + dcaXySlope / pt; // a + b/pT
281318
}
282319

320+
void validateMultiplicityEfficiency(const THn* efficiency) const
321+
{
322+
if (!efficiency || efficiency->GetNdimensions() != 4) {
323+
LOGF(fatal, "Multiplicity efficiency from %s must be a 4D THn with axes (eta, pT, multiplicity, z-vtx)", cfgEfficiencyMultiplicity.value.c_str());
324+
}
325+
}
326+
327+
THn* loadMultiplicityEfficiency(uint64_t timestamp)
328+
{
329+
if (cfgLocalEfficiency == 1) {
330+
return localMultiplicityEfficiency.get();
331+
}
332+
// Query each collision so the manager can refresh its cache at validity boundaries.
333+
auto* efficiency = ccdb->getForTimeStamp<THnT<float>>(cfgEfficiencyMultiplicity.value, timestamp);
334+
validateMultiplicityEfficiency(efficiency);
335+
return efficiency;
336+
}
337+
338+
template <bool applyDCA, typename TCollision, typename TTracks>
339+
float getCorrectedMultiplicity(const TCollision& collision, const TTracks& tracks, uint64_t timestamp)
340+
{
341+
if (cfgEfficiencyMultiplicity.value.empty()) {
342+
return collision.multiplicity();
343+
}
344+
345+
auto* efficiency = loadMultiplicityEfficiency(timestamp);
346+
double correctedMultiplicity = 0.;
347+
for (const auto& track : tracks) {
348+
// Match the tracks written by the corresponding data/MC producer path.
349+
if constexpr (applyDCA) {
350+
if (std::abs(track.dcaXY()) > getMaxDCAxy(track.pt()) || std::abs(track.dcaZ()) > dcazmax) {
351+
continue;
352+
}
353+
}
354+
const auto mask = static_cast<uint8_t>(cfgMultiplicityTrackBitMask.value);
355+
if (mask != 0 && (getTrackType(track) & mask) != mask) {
356+
continue;
357+
}
358+
359+
// The map contains RecoAll / MC, not inverse-efficiency weights.
360+
// Keep the original estimator as the map coordinate, including for centrality.
361+
const std::array<double, 4> values{track.eta(), track.pt(), collision.multiplicity(), collision.posZ()};
362+
std::array<int, 4> bins{};
363+
for (int axis = 0; axis < 4; ++axis) {
364+
auto* efficiencyAxis = efficiency->GetAxis(axis);
365+
bins[axis] = efficiencyAxis->FindFixBin(values[axis]);
366+
if (!std::isfinite(values[axis]) || bins[axis] < 1 || bins[axis] > efficiencyAxis->GetNbins()) {
367+
LOGF(fatal, "Multiplicity efficiency from %s does not cover axis %d value %g", cfgEfficiencyMultiplicity.value.c_str(), axis, values[axis]);
368+
}
369+
}
370+
const double eff = efficiency->GetBinContent(bins.data());
371+
if (!std::isfinite(eff) || eff <= 0.) {
372+
LOGF(fatal, "Invalid multiplicity efficiency %g from %s at bins (%d, %d, %d, %d)", eff, cfgEfficiencyMultiplicity.value.c_str(), bins[0], bins[1], bins[2], bins[3]);
373+
}
374+
correctedMultiplicity += 1. / eff;
375+
}
376+
if (!std::isfinite(correctedMultiplicity) || correctedMultiplicity > std::numeric_limits<float>::max()) {
377+
LOGF(fatal, "Corrected multiplicity cannot be represented as a float: %g", correctedMultiplicity);
378+
}
379+
return static_cast<float>(correctedMultiplicity);
380+
}
381+
283382
template <class T>
284383
using HasMultTables = decltype(std::declval<T&>().multNTracksPV());
285384

@@ -298,7 +397,7 @@ struct FilterCF {
298397
}
299398

300399
auto bc = collision.template bc_as<aod::BCsWithTimestamps>();
301-
outputCollisions(bc.runNumber(), collision.posZ(), collision.multiplicity(), bc.timestamp());
400+
outputCollisions(bc.runNumber(), collision.posZ(), collision.multiplicity(), bc.timestamp(), getCorrectedMultiplicity<true>(collision, tracks, bc.timestamp()));
302401

303402
if constexpr (std::experimental::is_detected<HasMultTables, C1>::value) {
304403
multiplicities.clear();
@@ -476,7 +575,7 @@ struct FilterCF {
476575

477576
auto bc = collision.template bc_as<aod::BCsWithTimestamps>();
478577
// NOTE works only when we store all MC collisions (as we do here)
479-
outputCollisions(bc.runNumber(), collision.posZ(), collision.multiplicity(), bc.timestamp());
578+
outputCollisions(bc.runNumber(), collision.posZ(), collision.multiplicity(), bc.timestamp(), getCorrectedMultiplicity<false>(collision, groupedTracks, bc.timestamp()));
480579
outputMcCollisionLabels(collision.mcCollisionId());
481580

482581
if constexpr (std::experimental::is_detected<HasMultTables, C1>::value) {

0 commit comments

Comments
 (0)