Skip to content

Commit 7e78d8c

Browse files
committed
[PWGCF] Add Nch-dependent rebinning of radial-flow correction maps
1 parent db83481 commit 7e78d8c

1 file changed

Lines changed: 245 additions & 31 deletions

File tree

PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx

Lines changed: 245 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ struct RadialFlowDecorr {
186186

187187
Configurable<int> cfgSys{"cfgSys", 1, "Which collision system? 1-->PbPb, 2-->NeNe, 3-->OO, 4-->pp"};
188188
Configurable<int> cfgSystType{"cfgSystType", 0, "Systematic variation: 0=Base,1=systDCA,2=systEff,3=systFlat,4=systNEta,5=systNITS,6=systNTPC,7=systPileup,8=systVz,9=systEtaBinning"};
189-
Configurable<int> cfgNBootstrap{"cfgNBootstrap", 30, "Number of Poisson bootstrap samples (base data run only)"};
189+
Configurable<int> cfgNBootstrap{"cfgNBootstrap", 16, "Number of Poisson bootstrap samples (base data run only)"};
190190
Configurable<int> cfgBootstrapSeed{"cfgBootstrapSeed", 0, "TRandom3 seed for bootstrap (0 = machine-random per job)"};
191191

192192
Configurable<bool> cfgFlat{"cfgFlat", false, "Whether to use flattening weights"};
@@ -462,23 +462,68 @@ struct RadialFlowDecorr {
462462
}
463463

464464
// Inclusive efficiency/fake lookup (no species dependence).
465-
float getEfficiency(float mult, float pt, float eta, int effidx, bool useEff) const
465+
float getEfficiency(float mult, float pt, float eta,
466+
int effidx, bool useEff) const
466467
{
467468
if (!useEff) {
468469
return (effidx == 0) ? 1.0f : 0.0f;
469470
}
471+
472+
const float invalid =
473+
std::numeric_limits<float>::quiet_NaN();
474+
470475
TH3F* h = (effidx == 0) ? state.hEff : state.hFake;
471-
if (!h) {
472-
return -1;
476+
477+
if (!h || !std::isfinite(mult) ||
478+
!std::isfinite(pt) || !std::isfinite(eta)) {
479+
return invalid;
480+
}
481+
482+
int ibx = h->GetXaxis()->FindFixBin(mult);
483+
484+
// Reject Nch underflow, clamp overflow.
485+
if (ibx < 1) {
486+
return invalid;
487+
}
488+
489+
ibx = std::min(ibx, h->GetNbinsX());
490+
491+
int iby = h->GetYaxis()->FindFixBin(pt);
492+
int ibz = h->GetZaxis()->FindFixBin(eta);
493+
494+
// Do not extrapolate in pT or eta.
495+
if (iby < 1 || iby > h->GetNbinsY() ||
496+
ibz < 1 || ibz > h->GetNbinsZ()) {
497+
return invalid;
498+
}
499+
500+
const double val = h->GetBinContent(ibx, iby, ibz);
501+
const double err = h->GetBinError(ibx, iby, ibz);
502+
503+
// A bin without a positive error is not a valid measurement.
504+
if (!std::isfinite(val) ||
505+
!std::isfinite(err) || err <= 0.0) {
506+
return invalid;
473507
}
474-
int ibx = h->GetXaxis()->FindBin(mult);
475-
int iby = h->GetYaxis()->FindBin(pt);
476-
int ibz = h->GetZaxis()->FindBin(eta);
477-
float val = h->GetBinContent(ibx, iby, ibz);
478-
if (effidx == 0) {
479-
return (val > 0.f) ? val : 1.0f;
508+
509+
// Physical-domain checks.
510+
if (val < 0.0 || val > 1.0 ||
511+
(effidx == 0 && val == 0.0)) {
512+
return invalid;
480513
}
481-
return val;
514+
515+
return static_cast<float>(val);
516+
}
517+
518+
// Reject missing or invalid efficiency/fake corrections consistently in all passes.
519+
bool getValidEffFake(float mult, float pt, float eta, bool useEff,
520+
float& eff, float& fake) const
521+
{
522+
eff = getEfficiency(mult, pt, eta, 0, useEff);
523+
fake = getEfficiency(mult, pt, eta, 1, useEff);
524+
return std::isfinite(eff) && std::isfinite(fake) &&
525+
eff > KFloatEpsilon && eff <= 1.f &&
526+
fake >= 0.f && fake < 1.f;
482527
}
483528

484529
float getFlatteningWeight(float vz, float chg, float pt, float eta, float phi, bool useFlat) const
@@ -499,6 +544,139 @@ struct RadialFlowDecorr {
499544
return h->GetBinContent(bins.data());
500545
}
501546

547+
TH3F* rebinNchMap(const TH3F* h, int sysConfig,
548+
const char* newName) const
549+
{
550+
if (!h) {
551+
return nullptr;
552+
}
553+
554+
// PbPb: 100 tracks; NeNe, OO, pp: 10 tracks
555+
const int trackStep = (sysConfig == 1) ? 100 : 10;
556+
557+
const int nx = h->GetNbinsX();
558+
const int ny = h->GetNbinsY();
559+
const int nz = h->GetNbinsZ();
560+
561+
const double originalWidth = h->GetXaxis()->GetBinWidth(1);
562+
563+
const int groupSize =
564+
static_cast<int>(std::lround(trackStep / originalWidth));
565+
566+
// Ensure the input binning allows exact 10/100-track groups
567+
if (groupSize < 1 ||
568+
std::abs(groupSize * originalWidth - trackStep) > 1.e-6) {
569+
LOGF(fatal, "Nch bin width is incompatible with %d-track rebinning",
570+
trackStep);
571+
return nullptr;
572+
}
573+
574+
for (int ix = 1; ix <= nx; ++ix) {
575+
if (std::abs(h->GetXaxis()->GetBinWidth(ix) -
576+
originalWidth) > 1.e-6) {
577+
LOGF(fatal, "Input Nch axis must have uniform bin widths");
578+
return nullptr;
579+
}
580+
}
581+
582+
// Construct the new Nch bin edges
583+
std::vector<double> xEdges;
584+
xEdges.push_back(h->GetXaxis()->GetBinLowEdge(1));
585+
586+
for (int ix = 1; ix <= nx; ix += groupSize) {
587+
const int last = std::min(ix + groupSize - 1, nx);
588+
xEdges.push_back(h->GetXaxis()->GetBinUpEdge(last));
589+
}
590+
591+
// Preserve the original pT and eta axes exactly
592+
auto copyEdges = [](const TAxis* axis) {
593+
std::vector<double> edges;
594+
595+
for (int i = 1; i <= axis->GetNbins(); ++i) {
596+
edges.push_back(axis->GetBinLowEdge(i));
597+
}
598+
599+
edges.push_back(axis->GetBinUpEdge(axis->GetNbins()));
600+
return edges;
601+
};
602+
603+
const auto yEdges = copyEdges(h->GetYaxis());
604+
const auto zEdges = copyEdges(h->GetZaxis());
605+
606+
const int nNewX = static_cast<int>(xEdges.size()) - 1;
607+
608+
auto* rebinned = new TH3F(
609+
newName, h->GetTitle(),
610+
nNewX, xEdges.data(),
611+
ny, yEdges.data(),
612+
nz, zEdges.data());
613+
614+
rebinned->SetDirectory(nullptr);
615+
rebinned->Sumw2();
616+
617+
// Rebin independently for each (pT, eta) slice
618+
for (int iy = 1; iy <= ny; ++iy) {
619+
for (int iz = 1; iz <= nz; ++iz) {
620+
621+
int lastValid = 0;
622+
double lastValue = 0.;
623+
double lastError = 0.;
624+
625+
for (int ib = 1; ib <= nNewX; ++ib) {
626+
627+
const int first = (ib - 1) * groupSize + 1;
628+
const int last = std::min(first + groupSize - 1, nx);
629+
630+
double sumW = 0.;
631+
double sumVW = 0.;
632+
633+
for (int ix = first; ix <= last; ++ix) {
634+
635+
const double v = h->GetBinContent(ix, iy, iz);
636+
const double e = h->GetBinError(ix, iy, iz);
637+
638+
// Inverse-variance weighting requires a positive error
639+
if (!std::isfinite(v) ||
640+
!std::isfinite(e) || e <= 0.) {
641+
continue;
642+
}
643+
644+
const double weight = 1. / (e * e);
645+
646+
sumW += weight;
647+
sumVW += v * weight;
648+
}
649+
650+
if (sumW > 0.) {
651+
652+
const double avg = sumVW / sumW;
653+
const double err = std::sqrt(1. / sumW);
654+
655+
rebinned->SetBinContent(ib, iy, iz, avg);
656+
rebinned->SetBinError(ib, iy, iz, err);
657+
658+
lastValid = ib;
659+
lastValue = avg;
660+
lastError = err;
661+
}
662+
}
663+
664+
// Extend the final valid value into trailing empty bins.
665+
// This also allows safe overflow clamping at readout.
666+
for (int ib = lastValid + 1; ib <= nNewX; ++ib) {
667+
if (lastValid == 0) {
668+
break;
669+
}
670+
671+
rebinned->SetBinContent(ib, iy, iz, lastValue);
672+
rebinned->SetBinError(ib, iy, iz, lastError);
673+
}
674+
}
675+
}
676+
677+
return rebinned;
678+
}
679+
502680
std::vector<o2::detectors::AlignParam>* offsetFT0 = nullptr;
503681
uint64_t mLastTimestamp = 0;
504682
double getEtaFT0(uint64_t globalChno, int i)
@@ -937,6 +1115,7 @@ struct RadialFlowDecorr {
9371115
// ===========================================================================
9381116
void init(InitContext&)
9391117
{
1118+
TH1::SetDefaultSumw2(kTRUE);
9401119
// Nch axes by system
9411120
if (cfgSys == kPbPb) {
9421121
nChAxis = {cfgNchPbMax / 2, KBinOffset, cfgNchPbMax + KBinOffset, "Nch", "PV-contributor track multiplicity"};
@@ -1082,26 +1261,50 @@ struct RadialFlowDecorr {
10821261
return;
10831262
}
10841263

1264+
// 1. Process Efficiency Map
10851265
auto* hNum = dynamic_cast<TH3F*>(lst->FindObject("h3_RecoMatchedToPrimary"));
10861266
auto* hDen = dynamic_cast<TH3F*>(lst->FindObject("h3_AllPrimary"));
10871267
if (hNum && hDen) {
10881268
state.hEff = dynamic_cast<TH3F*>(hNum->Clone("hEff"));
10891269
state.hEff->SetDirectory(nullptr);
1270+
state.hEff->Sumw2();
10901271
state.hEff->Divide(hDen);
1272+
1273+
TH3F* rebinnedEff = rebinNchMap(state.hEff, cfgSys.value, "hEffRebinned");
1274+
if (!rebinnedEff) {
1275+
LOGF(fatal, "Failed to rebin efficiency map");
1276+
return;
1277+
}
1278+
1279+
delete state.hEff;
1280+
state.hEff = rebinnedEff;
1281+
10911282
} else {
1092-
LOGF(error, "Missing CCDB objects for efficiency (h3_RecoMatchedToPrimary / h3_AllPrimary).");
1283+
LOGF(fatal, "Missing CCDB objects for efficiency (h3_RecoMatchedToPrimary / h3_AllPrimary).");
10931284
}
10941285

1286+
// 2. Process Fake Map
10951287
auto* hNumS = dynamic_cast<TH3F*>(lst->FindObject("h3_RecoUnMatchedToPrimary_Secondary"));
10961288
auto* hNumF = dynamic_cast<TH3F*>(lst->FindObject("h3_RecoUnMatchedToPrimary_Fake"));
10971289
auto* hDenF = dynamic_cast<TH3F*>(lst->FindObject("h3_AllReco"));
10981290
if (hNumS && hNumF && hDenF) {
10991291
state.hFake = dynamic_cast<TH3F*>(hNumS->Clone("hFake"));
1100-
state.hFake->Add(hNumF);
11011292
state.hFake->SetDirectory(nullptr);
1293+
state.hFake->Sumw2();
1294+
state.hFake->Add(hNumF); // Secondary + fake: add exactly once.
11021295
state.hFake->Divide(hDenF);
1296+
1297+
TH3F* rebinnedFake = rebinNchMap(state.hFake, cfgSys.value, "hFakeRebinned");
1298+
if (!rebinnedFake) {
1299+
LOGF(fatal, "Failed to rebin fake map");
1300+
return;
1301+
}
1302+
1303+
delete state.hFake;
1304+
state.hFake = rebinnedFake;
1305+
11031306
} else {
1104-
LOGF(error, "Missing CCDB objects for fakes.");
1307+
LOGF(fatal, "Missing CCDB objects for fakes.");
11051308
}
11061309
}
11071310

@@ -1333,9 +1536,11 @@ struct RadialFlowDecorr {
13331536
histos.fill(HIST("hEta"), eta);
13341537
histos.fill(HIST("hPhi"), phi);
13351538

1336-
float eff = getEfficiency(multPV, pt, eta, 0, cfgEff);
1337-
float fake = getEfficiency(multPV, pt, eta, 1, cfgEff);
1338-
float w = (eff > KFloatEpsilon) ? (1.0f - fake) / eff : 0.0f;
1539+
float eff = 1.f, fake = 0.f;
1540+
if (!getValidEffFake(multPV, pt, eta, cfgEff, eff, fake)) {
1541+
continue;
1542+
}
1543+
float w = (1.0f - fake) / eff;
13391544
if (std::isfinite(w) && w > 0.f) {
13401545
histos.fill(HIST("MCReco/hEtaPhiRecoEffWtd"), vz, sign, pt, eta, phi, w);
13411546
histos.fill(HIST("MCReco/hEtaPhiReco"), vz, sign, pt, eta, phi, 1.0);
@@ -1420,8 +1625,10 @@ struct RadialFlowDecorr {
14201625
histos.fill(HIST("hEta"), eta);
14211626
histos.fill(HIST("hPhi"), phi);
14221627

1423-
float eff = getEfficiency(multPV, pt, eta, 0, cfgEff);
1424-
float fake = getEfficiency(multPV, pt, eta, 1, cfgEff);
1628+
float eff = 1.f, fake = 0.f;
1629+
if (!getValidEffFake(multPV, pt, eta, cfgEff, eff, fake)) {
1630+
continue;
1631+
}
14251632
float flatW = getFlatteningWeight(vz, sign, pt, eta, phi, cfgFlat);
14261633
float w = flatW * (1.0 - fake) / eff;
14271634
if (!std::isfinite(w) || w <= 0.f || eff <= KFloatEpsilon) {
@@ -1609,8 +1816,10 @@ struct RadialFlowDecorr {
16091816
histos.fill(HIST("hEta"), eta);
16101817
histos.fill(HIST("hPhi"), phi);
16111818

1612-
float eff = getEfficiency(multPV, pt, eta, 0, cfgEff);
1613-
float fake = getEfficiency(multPV, pt, eta, 1, cfgEff);
1819+
float eff = 1.f, fake = 0.f;
1820+
if (!getValidEffFake(multPV, pt, eta, cfgEff, eff, fake)) {
1821+
continue;
1822+
}
16141823
float flatW = getFlatteningWeight(vz, sign, pt, eta, phi, cfgFlat);
16151824
float w = flatW * (1.0 - fake) / eff;
16161825
if (!std::isfinite(w) || w <= 0.f || eff <= KFloatEpsilon) {
@@ -2012,11 +2221,10 @@ struct RadialFlowDecorr {
20122221
ntrk++;
20132222
}
20142223

2015-
float eff = getEfficiency(coll.multNTracksPV(), pt, eta, 0, cfgEff);
2016-
if (eff <= KFloatEpsilon) {
2224+
float eff = 1.f, fake = 0.f;
2225+
if (!getValidEffFake(coll.multNTracksPV(), pt, eta, cfgEff, eff, fake)) {
20172226
continue;
20182227
}
2019-
float fake = getEfficiency(coll.multNTracksPV(), pt, eta, 1, cfgEff);
20202228
float w = (1.0f - fake) / eff;
20212229
if (!std::isfinite(w) || w <= 0.f) {
20222230
continue;
@@ -2087,8 +2295,10 @@ struct RadialFlowDecorr {
20872295
histos.fill(HIST("hEta"), eta);
20882296
histos.fill(HIST("hPhi"), phi);
20892297

2090-
float eff = getEfficiency(coll.multNTracksPV(), pt, eta, 0, cfgEff);
2091-
float fake = getEfficiency(coll.multNTracksPV(), pt, eta, 1, cfgEff);
2298+
float eff = 1.f, fake = 0.f;
2299+
if (!getValidEffFake(coll.multNTracksPV(), pt, eta, cfgEff, eff, fake)) {
2300+
continue;
2301+
}
20922302
float flatWeight = getFlatteningWeight(vz, sign, pt, eta, phi, cfgFlat);
20932303

20942304
histos.fill(HIST("pEffWeight_pt_eta_cent"), pt, eta, cent, eff);
@@ -2201,8 +2411,13 @@ struct RadialFlowDecorr {
22012411
LOGF(warning, "Data fluc: mean pT or mult map missing");
22022412
return;
22032413
}
2204-
if ((cfgEff || cfgFlat) && (!state.hEff || !state.hFake || !state.hFlatWeight)) {
2205-
LOGF(warning, "Data fluc: correction maps requested but not all present.");
2414+
if (cfgEff && (!state.hEff || !state.hFake)) {
2415+
LOGF(warning, "Data fluc: Efficiency maps requested but not present.");
2416+
return;
2417+
}
2418+
2419+
if (cfgFlat && !state.hFlatWeight) {
2420+
LOGF(warning, "Data fluc: Flattening map requested but not present.");
22062421
return;
22072422
}
22082423

@@ -2260,11 +2475,10 @@ struct RadialFlowDecorr {
22602475
continue;
22612476
}
22622477

2263-
float eff = getEfficiency(coll.multNTracksPV(), pt, eta, 0, cfgEff);
2264-
if (eff <= KFloatEpsilon) {
2478+
float eff = 1.f, fake = 0.f;
2479+
if (!getValidEffFake(coll.multNTracksPV(), pt, eta, cfgEff, eff, fake)) {
22652480
continue;
22662481
}
2267-
float fake = getEfficiency(coll.multNTracksPV(), pt, eta, 1, cfgEff);
22682482
float flatWeight = getFlatteningWeight(vz, sign, pt, eta, phi, cfgFlat);
22692483
float w = flatWeight * (1.0f - fake) / eff;
22702484
if (!std::isfinite(w) || w <= 0.f) {

0 commit comments

Comments
 (0)