Skip to content

Commit d77c23c

Browse files
author
Henrik Fribert
committed
Add back tracker PID tables
1 parent 85cee1b commit d77c23c

1 file changed

Lines changed: 127 additions & 57 deletions

File tree

‎ALICE3/TableProducer/OTF/onTheFlyTrackerPid.cxx‎

Lines changed: 127 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
/// \since May 22, 2025
2222
///
2323

24+
#include "ALICE3/DataModel/OTFPIDTrk.h"
25+
2426
#include <CCDB/BasicCCDBManager.h>
2527
#include <CCDB/CcdbApi.h>
2628
#include <Framework/AnalysisDataModel.h>
@@ -122,15 +124,22 @@ class TrackDedxLut
122124
allLoaded = false;
123125
continue;
124126
}
125-
// cumulative distributions, only for the filled bins
127+
// cumulative distributions and raw moments, only for the filled bins
126128
auto& offset = mOffset[pdg];
127129
auto& cdf = mCdf[pdg];
130+
auto& mean = mMean[pdg];
131+
auto& sigma = mSigma[pdg];
128132
offset.assign(nBins, -1);
133+
mean.assign(nBins, -1.f);
134+
sigma.assign(nBins, -1.f);
129135
for (size_t b = 0; b < nBins; ++b) {
130136
const float* hb = h->data() + b * nDedx;
131-
double sum = 0.;
137+
double sum = 0., m1 = 0., m2 = 0.;
132138
for (size_t k = 0; k < nDedx; ++k) {
139+
const double x = 0.5 * (mDedxEdges[k] + mDedxEdges[k + 1]);
133140
sum += hb[k];
141+
m1 += hb[k] * x;
142+
m2 += hb[k] * x * x;
134143
}
135144
if (sum <= 0.) {
136145
continue;
@@ -141,6 +150,9 @@ class TrackDedxLut
141150
cum += hb[k];
142151
cdf.push_back(cum / sum);
143152
}
153+
const double mu = m1 / sum;
154+
mean[b] = mu;
155+
sigma[b] = std::sqrt(std::max(0., m2 / sum - mu * mu));
144156
}
145157
}
146158
LOG(info) << "Loaded dE/dx LUT " << filename << " (" << mEtaEdges.size() - 1 << " |eta| x " << mRigEdges.size() - 1
@@ -150,34 +162,67 @@ class TrackDedxLut
150162

151163
/// Sampled dE/dx for uniform random number u, or -1 if there is no LUT entry
152164
float sample(int pdg, float eta, float rigidity, double u) const
165+
{
166+
int bin = -1, binNext = -1;
167+
float weight = 0.f;
168+
if (!locate(pdg, eta, rigidity, bin, binNext, weight)) {
169+
return -1.f;
170+
}
171+
const auto& offset = mOffset.at(pdg);
172+
const float* cdf = mCdf.at(pdg).data();
173+
float dedx = inverseCdf(cdf + offset[bin], u);
174+
if (binNext >= 0) {
175+
dedx += weight * (inverseCdf(cdf + offset[binNext], u) - dedx);
176+
}
177+
return dedx;
178+
}
179+
180+
bool getMeanSigma(int pdg, float eta, float rigidity, float& mean, float& sigma) const
181+
{
182+
int bin = -1, binNext = -1;
183+
float weight = 0.f;
184+
if (!locate(pdg, eta, rigidity, bin, binNext, weight)) {
185+
return false;
186+
}
187+
const auto& m = mMean.at(pdg);
188+
const auto& s = mSigma.at(pdg);
189+
mean = m[bin];
190+
sigma = s[bin];
191+
if (binNext >= 0) {
192+
mean += weight * (m[binNext] - mean);
193+
sigma += weight * (s[binNext] - sigma);
194+
}
195+
return sigma > 0.f;
196+
}
197+
198+
private:
199+
/// LUT bin of the track and the neighbouring rigidity bin on its side (-1 if empty)
200+
bool locate(int pdg, float eta, float rigidity, int& bin, int& binNext, float& weight) const
153201
{
154202
const auto it = mOffset.find(pdg);
155203
const float absEta = std::abs(eta);
156204
if (it == mOffset.end() || mEtaEdges.empty() || absEta < mEtaEdges.front() || absEta >= mEtaEdges.back() ||
157205
rigidity < mRigEdges.front() || rigidity >= mRigEdges.back()) {
158-
return -1.f;
206+
return false;
159207
}
160208
const int nRig = mRigEdges.size() - 1;
161209
const int iEta = std::upper_bound(mEtaEdges.begin(), mEtaEdges.end(), absEta) - mEtaEdges.begin() - 1;
162210
const int iRig = std::upper_bound(mRigEdges.begin(), mRigEdges.end(), rigidity) - mRigEdges.begin() - 1;
163-
const int offset = it->second[iEta * nRig + iRig];
164-
if (offset < 0) {
165-
return -1.f;
211+
bin = iEta * nRig + iRig;
212+
if (it->second[bin] < 0) {
213+
return false;
166214
}
167-
const float* cdf = mCdf.at(pdg).data();
168-
float dedx = inverseCdf(cdf + offset, u);
169-
170-
// neighbouring bin on the side of the track, no interpolation if it is empty
215+
binNext = -1;
216+
weight = 0.f;
171217
const double logRig = std::log(rigidity);
172218
const int iNext = logRig < mLogRigCentres[iRig] ? iRig - 1 : iRig + 1;
173219
if (iNext >= 0 && iNext < nRig && it->second[iEta * nRig + iNext] >= 0) {
174-
const float w = (logRig - mLogRigCentres[iRig]) / (mLogRigCentres[iNext] - mLogRigCentres[iRig]);
175-
dedx += w * (inverseCdf(cdf + it->second[iEta * nRig + iNext], u) - dedx);
220+
binNext = iEta * nRig + iNext;
221+
weight = (logRig - mLogRigCentres[iRig]) / (mLogRigCentres[iNext] - mLogRigCentres[iRig]);
176222
}
177-
return dedx;
223+
return true;
178224
}
179225

180-
private:
181226
/// dE/dx at which the cumulative distribution reaches u, uniform within the dE/dx bin
182227
float inverseCdf(const float* cdf, double u) const
183228
{
@@ -203,20 +248,26 @@ class TrackDedxLut
203248
std::vector<double> mDedxEdges;
204249
std::unordered_map<int, std::vector<int>> mOffset; // |pdg| -> start of the cdf of bin etaBin * nRig + rigBin, -1 if empty
205250
std::unordered_map<int, std::vector<float>> mCdf; // |pdg| -> cumulative dE/dx distributions of the filled bins
251+
std::unordered_map<int, std::vector<float>> mMean; // |pdg| -> mean dE/dx per bin, -1 if empty
252+
std::unordered_map<int, std::vector<float>> mSigma;
206253
};
207254

208255
struct OnTheFlyTrackerPid {
209256
static constexpr int NumSpecies = 9;
210257
static constexpr std::array<int, NumSpecies> PdgCodes = {11, 13, 211, 321, 2212, 1000010020, 1000010030, 1000020030, 1000020040};
211258
static constexpr std::array<const char*, NumSpecies> SpeciesNames = {"Elec", "Muon", "Pion", "Kaon", "Prot", "Deut", "Trit", "He3", "Al"};
212259
static constexpr std::array<const char*, NumSpecies> SpeciesLabels = {"#it{e}", "#it{#mu}", "#it{#pi}", "#it{K}", "#it{p}", "#it{d}", "#it{t}", "#it{^{3}He}", "#it{^{4}He}"};
260+
static constexpr float NoNsigma = 999.f;
213261

214262
std::array<std::shared_ptr<TH2>, NumSpecies> h2dToTvsPperParticle;
215263
std::array<std::shared_ptr<TH2>, NumSpecies> h2dToTvsPperParticleLin;
216264
std::array<std::shared_ptr<TH3>, NumSpecies> h3dToTvsPEtaPerParticle;
217265
std::array<std::shared_ptr<TH2>, NumSpecies> h2dTracks;
218266
std::array<std::shared_ptr<TH2>, NumSpecies> h2dTracksWithDedx;
219267

268+
Produces<aod::UpgradeTrkPidSignals> tableUpgradeTrkPidSignals;
269+
Produces<aod::UpgradeTrkPids> tableUpgradeTrkPids;
270+
220271
Service<o2::framework::O2DatabasePDG> pdgDatabase{};
221272
Service<o2::ccdb::BasicCCDBManager> ccdb{};
222273
TrackDedxLut mTrackLut;
@@ -226,7 +277,7 @@ struct OnTheFlyTrackerPid {
226277

227278
Configurable<std::string> trackLut{"trackLut", "", "Per-track dE/dx LUT from MakeToTLUT.C (local file or ccdb:<path>)"};
228279
Configurable<int> seed{"seed", 0, "Seed for the dE/dx sampling (0: different in every job)"};
229-
Configurable<bool> primariesOnly{"primariesOnly", false, "Only physical primaries (otherwise all tracks, including decay products)"};
280+
Configurable<bool> primariesOnly{"primariesOnly", false, "Fill the histograms only with physical primaries (the tables are filled for all tracks)"};
230281
Configurable<bool> useTrueKinematics{"useTrueKinematics", false, "Fill the histograms vs the true instead of the reconstructed p/z and |eta| (the dE/dx is always sampled at the true ones)"};
231282

232283
// dE/dx axis, logarithmic
@@ -315,52 +366,71 @@ struct OnTheFlyTrackerPid {
315366
}
316367
}
317368

369+
/// dE/dx of the track (-1 if there is none) and its nsigma for every species
370+
template <typename TTrack>
371+
float processTrack(TTrack const& track, std::array<float, NumSpecies>& nSigma)
372+
{
373+
if (!track.has_mcParticle()) {
374+
return -1.f;
375+
}
376+
const auto& mcParticle = track.mcParticle();
377+
const int pdg = std::abs(mcParticle.pdgCode());
378+
const auto itSpecies = std::find(PdgCodes.begin(), PdgCodes.end(), pdg);
379+
if (itSpecies == PdgCodes.end()) {
380+
return -1.f;
381+
}
382+
const auto* pdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode());
383+
if (!pdgInfo || pdgInfo->Charge() == 0) {
384+
return -1.f;
385+
}
386+
const int iSpecies = std::distance(PdgCodes.begin(), itSpecies);
387+
const float absCharge = std::abs(pdgInfo->Charge()) / 3.f;
388+
const float rigidityTrue = mcParticle.p() / absCharge;
389+
const float dedx = mTrackLut.sample(pdg, mcParticle.eta(), rigidityTrue, mRandom.Rndm());
390+
391+
if (dedx > 0.f) {
392+
for (int i = 0; i < NumSpecies; ++i) {
393+
float mean = 0.f, sigma = 0.f;
394+
if (mTrackLut.getMeanSigma(PdgCodes[i], track.eta(), track.p(), mean, sigma)) {
395+
nSigma[i] = (dedx - mean) / sigma;
396+
}
397+
}
398+
}
399+
400+
if (primariesOnly.value && !mcParticle.isPhysicalPrimary()) {
401+
return dedx;
402+
}
403+
const float rigidity = useTrueKinematics.value ? rigidityTrue : track.p();
404+
const float absEta = std::abs(useTrueKinematics.value ? mcParticle.eta() : track.eta());
405+
const float p = rigidity * absCharge;
406+
h2dTracks[iSpecies]->Fill(rigidity, absEta);
407+
if (dedx < 0.f) {
408+
return dedx;
409+
}
410+
h2dTracksWithDedx[iSpecies]->Fill(rigidity, absEta);
411+
h3dToTvsPEtaPerParticle[iSpecies]->Fill(rigidity, absEta, dedx);
412+
if (enableLogPBins.value) {
413+
histos.fill(HIST("hToTvsMom"), p, dedx);
414+
histos.fill(HIST("hToTvsP"), rigidity, dedx);
415+
h2dToTvsPperParticle[iSpecies]->Fill(rigidity, dedx);
416+
}
417+
if (enableLinearPBins.value) {
418+
histos.fill(HIST("hToTvsMomLin"), p, dedx);
419+
histos.fill(HIST("hToTvsPLin"), rigidity, dedx);
420+
h2dToTvsPperParticleLin[iSpecies]->Fill(rigidity, dedx);
421+
}
422+
return dedx;
423+
}
424+
318425
void process(soa::Join<aod::Tracks, aod::McTrackLabels> const& tracks,
319426
aod::McParticles const&)
320427
{
428+
std::array<float, NumSpecies> nSigma{};
321429
for (const auto& track : tracks) {
322-
if (!track.has_mcParticle()) {
323-
continue;
324-
}
325-
const auto& mcParticle = track.mcParticle();
326-
if (primariesOnly.value && !mcParticle.isPhysicalPrimary()) {
327-
continue;
328-
}
329-
const int pdg = std::abs(mcParticle.pdgCode());
330-
const auto itSpecies = std::find(PdgCodes.begin(), PdgCodes.end(), pdg);
331-
if (itSpecies == PdgCodes.end()) {
332-
continue;
333-
}
334-
const auto* pdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode());
335-
if (!pdgInfo || pdgInfo->Charge() == 0) {
336-
continue;
337-
}
338-
const int iSpecies = std::distance(PdgCodes.begin(), itSpecies);
339-
const float absCharge = std::abs(pdgInfo->Charge()) / 3.f;
340-
const float rigidityTrue = mcParticle.p() / absCharge;
341-
342-
// the OTF tracks carry p/z
343-
const float rigidity = useTrueKinematics.value ? rigidityTrue : track.p();
344-
const float absEta = std::abs(useTrueKinematics.value ? mcParticle.eta() : track.eta());
345-
const float p = rigidity * absCharge;
346-
h2dTracks[iSpecies]->Fill(rigidity, absEta);
347-
348-
const float dedx = mTrackLut.sample(pdg, mcParticle.eta(), rigidityTrue, mRandom.Rndm());
349-
if (dedx < 0.f) {
350-
continue;
351-
}
352-
h2dTracksWithDedx[iSpecies]->Fill(rigidity, absEta);
353-
h3dToTvsPEtaPerParticle[iSpecies]->Fill(rigidity, absEta, dedx);
354-
if (enableLogPBins.value) {
355-
histos.fill(HIST("hToTvsMom"), p, dedx);
356-
histos.fill(HIST("hToTvsP"), rigidity, dedx);
357-
h2dToTvsPperParticle[iSpecies]->Fill(rigidity, dedx);
358-
}
359-
if (enableLinearPBins.value) {
360-
histos.fill(HIST("hToTvsMomLin"), p, dedx);
361-
histos.fill(HIST("hToTvsPLin"), rigidity, dedx);
362-
h2dToTvsPperParticleLin[iSpecies]->Fill(rigidity, dedx);
363-
}
430+
nSigma.fill(NoNsigma);
431+
const float dedx = processTrack(track, nSigma);
432+
tableUpgradeTrkPidSignals(dedx);
433+
tableUpgradeTrkPids(nSigma[0], nSigma[1], nSigma[2], nSigma[3], nSigma[4], nSigma[5], nSigma[6], nSigma[7], nSigma[8]);
364434
}
365435
}
366436
};

0 commit comments

Comments
 (0)