From 73f4e66e8cd21f676d39ebbbef70a248dc4af6f8 Mon Sep 17 00:00:00 2001 From: KotliarovAr Date: Thu, 22 Oct 2026 13:16:13 +0000 Subject: [PATCH 1/5] PWGJE: Add FT0 event-activity helpers and detector-level MC means Add minimal FT0 helpers for scaled FT0C and FT0M event activity. Introduce grouped data and detector-level MC normalization means, validated as nonzero in init(). Existing selections and histograms remain unchanged. --- PWGJE/Tasks/trackEfficiency.cxx | 55 +++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/PWGJE/Tasks/trackEfficiency.cxx b/PWGJE/Tasks/trackEfficiency.cxx index 779f6df4805..b9cec0aa1a0 100644 --- a/PWGJE/Tasks/trackEfficiency.cxx +++ b/PWGJE/Tasks/trackEfficiency.cxx @@ -95,6 +95,19 @@ struct TrackEfficiency { Configurable useTrueTrackWeight{"useTrueTrackWeight", true, "test configurable, should be set to 1 then config removed once well tested"}; + // Normalization FT0 by means + struct FT0A : ConfigurableGroup { + std::string prefix = "ft0a"; + Configurable mean{"mean", 3499., "Mean FT0A signal"}; + Configurable meanMCDetLevel{"meanMCDetLevel", 3499., "Mean FT0A signal at MC detector level"}; + } ft0a; + + struct FT0C : ConfigurableGroup { + std::string prefix = "ft0c"; + Configurable mean{"mean", 844.6, "Mean FT0C signal"}; + Configurable meanMCDetLevel{"meanMCDetLevel", 844.6, "Mean FT0C signal at MC detector level"}; + } ft0c; + // systematics variation - Run 2 guidelines: https://twiki.cern.ch/twiki/bin/view/ALICE/AliDPGtoolsTrackSystematicUncertainty TrackSelection customTrackSelection; Configurable useCustomTrackSelection{"useCustomTrackSelection", false, "whether to use the custom cuts (used for cut variation for tracking efficiency systematics)"}; @@ -205,6 +218,10 @@ struct TrackEfficiency { LOGP(fatal, "Configurable acceptSplitCollisions has wrong input value; stopping workflow"); } + if (ft0a.mean == 0.f || ft0c.mean == 0.f || ft0a.meanMCDetLevel == 0.f || ft0c.meanMCDetLevel == 0.f) { + LOGP(fatal, "FT0A and FT0C means must be non-zero to avoid division by zero; stopping workflow"); + } + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); @@ -1711,6 +1728,44 @@ struct TrackEfficiency { } } PROCESS_SWITCH(TrackEfficiency, processQcCheck, "Histograms for QC checks", false); + + // FT0 event activity helpers + struct FT0Metrics { + float multFT0A = 0.f; + float multFT0C = 0.f; + float multFT0M = 0.f; + float scaledFT0A = 0.f; + float scaledFT0C = 0.f; + float scaledFT0M = 0.f; + }; + + float getScaledFT0(const float& multFT0, const float& meanFT0) + { + return multFT0 / meanFT0; + } + + float getScaledFT0M(const float& scaledMultFT0A, const float& scaledMultFT0C) + { + return 0.5 * (scaledMultFT0A + scaledMultFT0C); + } + + FT0Metrics makeFT0Metrics(float multFT0A, float multFT0C, float meanFT0A, float meanFT0C) + { + FT0Metrics values; + values.multFT0A = multFT0A; + values.multFT0C = multFT0C; + values.multFT0M = multFT0A + multFT0C; + values.scaledFT0A = getScaledFT0(values.multFT0A, meanFT0A); + values.scaledFT0C = getScaledFT0(values.multFT0C, meanFT0C); + values.scaledFT0M = getScaledFT0M(values.scaledFT0A, values.scaledFT0C); + return values; + } + + template + FT0Metrics getFT0Metrics(const Collision& collision, float meanFT0A, float meanFT0C) + { + return makeFT0Metrics(collision.multFT0A(), collision.multFT0C(), meanFT0A, meanFT0C); + } }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) From 1e3a43c639a510d48c06edb952f71c71a581fb65 Mon Sep 17 00:00:00 2001 From: KotliarovAr Date: Thu, 22 Oct 2026 13:48:05 +0000 Subject: [PATCH 2/5] PWGJE: Add FT0 EA thresholds and update detector-level MC means Add raw-data and detector-level MC FT0C/FT0M threshold axes in the Hist configurable group. Set MC means to 2640 for FT0A and 802.9 for FT0C. Reject near-zero means using std::fabs and float epsilon. --- PWGJE/Tasks/trackEfficiency.cxx | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/PWGJE/Tasks/trackEfficiency.cxx b/PWGJE/Tasks/trackEfficiency.cxx index b9cec0aa1a0..063de78fe1f 100644 --- a/PWGJE/Tasks/trackEfficiency.cxx +++ b/PWGJE/Tasks/trackEfficiency.cxx @@ -38,6 +38,7 @@ #include #include +#include #include #include @@ -99,15 +100,28 @@ struct TrackEfficiency { struct FT0A : ConfigurableGroup { std::string prefix = "ft0a"; Configurable mean{"mean", 3499., "Mean FT0A signal"}; - Configurable meanMCDetLevel{"meanMCDetLevel", 3499., "Mean FT0A signal at MC detector level"}; + Configurable meanMCDetLevel{"meanMCDetLevel", 2640., "Mean FT0A signal at MC detector level"}; } ft0a; struct FT0C : ConfigurableGroup { std::string prefix = "ft0c"; Configurable mean{"mean", 844.6, "Mean FT0C signal"}; - Configurable meanMCDetLevel{"meanMCDetLevel", 844.6, "Mean FT0C signal at MC detector level"}; + Configurable meanMCDetLevel{"meanMCDetLevel", 802.9, "Mean FT0C signal at MC detector level"}; } ft0c; + // FT0C event activity thresholds + struct Hist : ConfigurableGroup { + std::string prefix = "hist"; + + // Raw data + ConfigurableAxis multFT0CThresh{"multFT0CThresh", {VARIABLE_WIDTH, 0.0, 0.109421, 0.215022, 0.343214, 0.50515, 0.712277, 0.978245, 1.3189, 1.75744, 2.36001, 3.59696, 25.}, "Percentiles of scaled FT0C: 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%, 1%"}; + ConfigurableAxis multFT0MThresh{"multFT0MThresh", {VARIABLE_WIDTH, 0.0, 0.130987, 0.237916, 0.36664, 0.528441, 0.734705, 0.996611, 1.32785, 1.74896, 2.31024, 3.37852, 25.}, "Percentiles of scaled FT0M: 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%, 1%"}; + + // MC detector-level data + ConfigurableAxis multFT0CThreshMCDetLevel{"multFT0CThreshMCDetLevel", {VARIABLE_WIDTH, 0.0, 0.124992, 0.243769, 0.380291, 0.543998, 0.748078, 1.00424, 1.3237, 1.72648, 2.27373, 3.43144, 25.}, "Percentiles of scaled FT0C: 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%, 1%"}; + ConfigurableAxis multFT0MThreshMCDetLevel{"multFT0MThreshMCDetLevel", {VARIABLE_WIDTH, 0.0, 0.142622, 0.262095, 0.397069, 0.558622, 0.762008, 1.01685, 1.33219, 1.72377, 2.24008, 3.26309, 25.}, "Percentiles of scaled FT0M: 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%, 1%"}; + } hist; + // systematics variation - Run 2 guidelines: https://twiki.cern.ch/twiki/bin/view/ALICE/AliDPGtoolsTrackSystematicUncertainty TrackSelection customTrackSelection; Configurable useCustomTrackSelection{"useCustomTrackSelection", false, "whether to use the custom cuts (used for cut variation for tracking efficiency systematics)"}; @@ -218,8 +232,10 @@ struct TrackEfficiency { LOGP(fatal, "Configurable acceptSplitCollisions has wrong input value; stopping workflow"); } - if (ft0a.mean == 0.f || ft0c.mean == 0.f || ft0a.meanMCDetLevel == 0.f || ft0c.meanMCDetLevel == 0.f) { - LOGP(fatal, "FT0A and FT0C means must be non-zero to avoid division by zero; stopping workflow"); + constexpr float minFT0Mean = std::numeric_limits::epsilon(); + if (std::fabs(ft0a.mean.value) <= minFT0Mean || std::fabs(ft0c.mean.value) <= minFT0Mean || + std::fabs(ft0a.meanMCDetLevel.value) <= minFT0Mean || std::fabs(ft0c.meanMCDetLevel.value) <= minFT0Mean) { + LOGP(fatal, "FT0A and FT0C means must have magnitude greater than {} to avoid near-zero denominators; stopping workflow", minFT0Mean); } eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); From 48e81f146dc87250ce966a09e661075757f8ad5b Mon Sep 17 00:00:00 2001 From: KotliarovAr Date: Thu, 22 Oct 2026 14:34:44 +0000 Subject: [PATCH 3/5] PWGJE: Fill track-pT spectra versus FT0 event activity Add separate scaled FT0C and FT0M track-pT histograms for data and detector-level MC using configurable EA axes. Select normalization means and histogram fills through an explicit TrackSample template parameter, preserving existing cuts and weights. --- PWGJE/Tasks/trackEfficiency.cxx | 36 +++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/PWGJE/Tasks/trackEfficiency.cxx b/PWGJE/Tasks/trackEfficiency.cxx index 063de78fe1f..495d2c82595 100644 --- a/PWGJE/Tasks/trackEfficiency.cxx +++ b/PWGJE/Tasks/trackEfficiency.cxx @@ -144,6 +144,11 @@ struct TrackEfficiency { SplitOkCheckFirstAssocCollOnly // 2 }; + enum class TrackSample { + Data, + MCDetLevel + }; + template bool isAcceptedTrack(TJetTrack const& jetTrack) { @@ -174,9 +179,15 @@ struct TrackEfficiency { return std::abs(charge) >= chargeUnit; } - template + template void fillTrackHistograms(TCollision const& collision, TJetTracks const& jetTracks, float weight = 1.0) { + const float meanFT0A = sample == TrackSample::Data ? ft0a.mean.value : ft0a.meanMCDetLevel.value; + const float meanFT0C = sample == TrackSample::Data ? ft0c.mean.value : ft0c.meanMCDetLevel.value; + const auto ft0Metrics = getFT0Metrics(collision, meanFT0A, meanFT0C); + const auto scaledFT0C = ft0Metrics.scaledFT0C; + const auto scaledFT0M = ft0Metrics.scaledFT0M; + for (auto const& track : jetTracks) { if (!isAcceptedTrack(track)) { continue; @@ -190,6 +201,13 @@ struct TrackEfficiency { float centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); registry.fill(HIST("h2_centrality_track_pt"), centrality, track.pt(), weight); + if constexpr (sample == TrackSample::Data) { + registry.fill(HIST("hScaledFT0CTrackPt"), scaledFT0C, track.pt(), weight); + registry.fill(HIST("hScaledFT0MTrackPt"), scaledFT0M, track.pt(), weight); + } else { + registry.fill(HIST("hScaledFT0CTrackPt_MCDetLevel"), scaledFT0C, track.pt(), weight); + registry.fill(HIST("hScaledFT0MTrackPt_MCDetLevel"), scaledFT0M, track.pt(), weight); + } registry.fill(HIST("h2_centrality_track_eta"), centrality, track.eta(), weight); registry.fill(HIST("h2_centrality_track_phi"), centrality, track.phi(), weight); registry.fill(HIST("h2_centrality_track_energy"), centrality, track.energy(), weight); @@ -287,6 +305,10 @@ struct TrackEfficiency { AxisSpec etaAxisEff{etaEffBinning, "#eta"}; AxisSpec phiAxisEff{phiEffBinning, "#phi"}; AxisSpec centAxis = {centralityBinning, "centrality (%)"}; + AxisSpec scaledFT0C{hist.multFT0CThresh, "FT0C / #LT FT0C #GT"}; + AxisSpec scaledFT0M{hist.multFT0MThresh, "FT0M^{*}"}; + AxisSpec scaledFT0CMCDetLevel{hist.multFT0CThreshMCDetLevel, "FT0C / #LT FT0C #GT"}; + AxisSpec scaledFT0MMCDetLevel{hist.multFT0MThreshMCDetLevel, "FT0M^{*}"}; AxisSpec intRateAxis = {intRateBinning, "int. rate (kHz)"}; AxisSpec occupancyAxis = {140, -0.5, 13999.5, "occupancy"}; AxisSpec nTracksAxis = {16001, -1., 16000, "n tracks"}; @@ -379,6 +401,12 @@ struct TrackEfficiency { registry.add("h2_track_pt_high_track_sigma1overpt", "#sigma(1/#it{p}_{T}); #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {{90, 10., 100.}, {10000, 0.0, 1.0}}}); registry.add("h3_intrate_centrality_track_pt", "interaction rate vs centrality vs track pT; int. rate; centrality; #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH3F, {intRateAxis, centAxis, {200, 0., 200.}}}); registry.add("h3_track_pt_track_eta_track_phi", "track pT vs track #eta vs track #varphi; #it{p}_{T,track} (GeV/#it{c}); #eta_{track}; #varphi_{track}", {HistType::kTH3F, {{200, 0., 200.}, {100, -1.0, 1.0}, {160, -1.0, 7.}}}); + + // EA analysis + registry.add("hScaledFT0CTrackPt", "scaled FT0C vs track pT; FT0C / #LT FT0C #GT; #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {scaledFT0C, {200, 0., 200.}}}); + registry.add("hScaledFT0MTrackPt", "scaled FT0M vs track pT; FT0M^{*}; #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {scaledFT0M, {200, 0., 200.}}}); + registry.add("hScaledFT0CTrackPt_MCDetLevel", "MC detector-level scaled FT0C vs track pT; FT0C / #LT FT0C #GT; #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {scaledFT0CMCDetLevel, {200, 0., 200.}}}); + registry.add("hScaledFT0MTrackPt_MCDetLevel", "MC detector-level scaled FT0M vs track pT; FT0M^{*}; #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {scaledFT0MMCDetLevel, {200, 0., 200.}}}); } if (doprocessParticles || doprocessParticlesWeighted) { @@ -886,7 +914,7 @@ struct TrackEfficiency { return; } - fillTrackHistograms(collision, jetTracks); + fillTrackHistograms(collision, jetTracks); } PROCESS_SWITCH(TrackEfficiency, processTracksFromData, "QA for charged tracks in data", false); @@ -914,7 +942,7 @@ struct TrackEfficiency { return; } - fillTrackHistograms(collision, jetTracks); + fillTrackHistograms(collision, jetTracks); } PROCESS_SWITCH(TrackEfficiency, processTracksFromMc, "QA for charged tracks in MC without weights", false); @@ -943,7 +971,7 @@ struct TrackEfficiency { return; } - fillTrackHistograms(collision, jetTracks, eventWeight); + fillTrackHistograms(collision, jetTracks, eventWeight); } PROCESS_SWITCH(TrackEfficiency, processTracksFromMcWeighted, "QA for charged tracks in weighted MC", false); From 0bad1cd17e30ebb065a2e5113479b3e12555b3af Mon Sep 17 00:00:00 2001 From: KotliarovAr Date: Fri, 23 Oct 2026 09:48:56 +0000 Subject: [PATCH 4/5] PWGJE: Adjust track QA selection and weighted MC inputs Remove the weight-derived per-track pTHat fraction cut from the shared track histogram helper and group EA fills after the existing QA fills. Add JCollisionOutliers to weighted MC collision inputs and obtain the event weight immediately before the event-level pTHat calculation. --- PWGJE/Tasks/trackEfficiency.cxx | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/PWGJE/Tasks/trackEfficiency.cxx b/PWGJE/Tasks/trackEfficiency.cxx index 495d2c82595..f4ed26a7464 100644 --- a/PWGJE/Tasks/trackEfficiency.cxx +++ b/PWGJE/Tasks/trackEfficiency.cxx @@ -193,21 +193,9 @@ struct TrackEfficiency { continue; } - float pTHat = simPtRef / (std::pow(weight, 1.0 / pTHatExponent)); - if (track.pt() > pTHatMaxFractionMCD * pTHat) { - continue; - } - float centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); registry.fill(HIST("h2_centrality_track_pt"), centrality, track.pt(), weight); - if constexpr (sample == TrackSample::Data) { - registry.fill(HIST("hScaledFT0CTrackPt"), scaledFT0C, track.pt(), weight); - registry.fill(HIST("hScaledFT0MTrackPt"), scaledFT0M, track.pt(), weight); - } else { - registry.fill(HIST("hScaledFT0CTrackPt_MCDetLevel"), scaledFT0C, track.pt(), weight); - registry.fill(HIST("hScaledFT0MTrackPt_MCDetLevel"), scaledFT0M, track.pt(), weight); - } registry.fill(HIST("h2_centrality_track_eta"), centrality, track.eta(), weight); registry.fill(HIST("h2_centrality_track_phi"), centrality, track.phi(), weight); registry.fill(HIST("h2_centrality_track_energy"), centrality, track.energy(), weight); @@ -217,6 +205,14 @@ struct TrackEfficiency { registry.fill(HIST("h2_track_pt_high_track_sigmapt"), track.pt(), track.sigma1Pt() * track.pt(), weight); registry.fill(HIST("h3_intrate_centrality_track_pt"), collision.hadronicRate(), centrality, track.pt(), weight); registry.fill(HIST("h3_track_pt_track_eta_track_phi"), track.pt(), track.eta(), track.phi(), weight); + + if constexpr (sample == TrackSample::Data) { + registry.fill(HIST("hScaledFT0CTrackPt"), scaledFT0C, track.pt(), weight); + registry.fill(HIST("hScaledFT0MTrackPt"), scaledFT0M, track.pt(), weight); + } else { + registry.fill(HIST("hScaledFT0CTrackPt_MCDetLevel"), scaledFT0C, track.pt(), weight); + registry.fill(HIST("hScaledFT0MTrackPt_MCDetLevel"), scaledFT0M, track.pt(), weight); + } } } @@ -946,7 +942,7 @@ struct TrackEfficiency { } PROCESS_SWITCH(TrackEfficiency, processTracksFromMc, "QA for charged tracks in MC without weights", false); - void processTracksFromMcWeighted(soa::Filtered>::iterator const& collision, + void processTracksFromMcWeighted(soa::Filtered>::iterator const& collision, aod::JetMcCollisions const&, soa::Filtered> const& jetTracks, soa::Join const&) @@ -954,7 +950,6 @@ struct TrackEfficiency { if (!collision.has_mcCollision()) { // the collision is fake and has no associated mc coll; skip as .mccollision() cannot be called return; } - float eventWeight = collision.mcCollision().weight(); if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { return; } @@ -966,6 +961,7 @@ struct TrackEfficiency { return; } + float eventWeight = collision.mcCollision().weight(); float pTHat = collision.mcCollision().ptHard() < pTHatSettingSentinelValue ? collision.mcCollision().ptHard() : simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)); if (pTHat < ptHatMin || pTHat > ptHatMax) { // only allows mcCollisions with weight in between min and max return; From 638977a43fbe498af7541859a98d70df1fb689d5 Mon Sep 17 00:00:00 2001 From: KotliarovAr Date: Tue, 27 Oct 2026 13:39:10 +0000 Subject: [PATCH 5/5] PWGJE: Reject outlier collisions in weighted MC track QA Skip collisions flagged as outliers in processTracksFromMcWeighted. --- PWGJE/Tasks/trackEfficiency.cxx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/PWGJE/Tasks/trackEfficiency.cxx b/PWGJE/Tasks/trackEfficiency.cxx index f4ed26a7464..4ee3a336db3 100644 --- a/PWGJE/Tasks/trackEfficiency.cxx +++ b/PWGJE/Tasks/trackEfficiency.cxx @@ -953,6 +953,11 @@ struct TrackEfficiency { if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { return; } + + if (collision.isOutlier()) { + return; + } + float centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); if (cutCentrality && (centrality < centralityMin || centralityMax < centrality)) { return;