From 5ebcf4395a07f331437bed08c3bdf4b3a29fe60b Mon Sep 17 00:00:00 2001 From: DongJo Kim Date: Tue, 20 Oct 2026 10:23:09 +0300 Subject: [PATCH 1/3] [PWGCF] Add a JCorran D-meson v02(pT) task. Measure the spectra-flow cumulant from FT0 Q-vectors and CF2ProngTracks, and keep a synthetic closure for the charged-hadron and D0 estimators. Co-authored-by: Cursor --- PWGCF/JCorran/Tasks/CMakeLists.txt | 10 + PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx | 374 +++++++++++++++++++++ PWGCF/JCorran/Tasks/jV02DmesonToy.cxx | 153 +++++++++ 3 files changed, 537 insertions(+) create mode 100644 PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx create mode 100644 PWGCF/JCorran/Tasks/jV02DmesonToy.cxx diff --git a/PWGCF/JCorran/Tasks/CMakeLists.txt b/PWGCF/JCorran/Tasks/CMakeLists.txt index 465083509d0..4c9c34b46c5 100644 --- a/PWGCF/JCorran/Tasks/CMakeLists.txt +++ b/PWGCF/JCorran/Tasks/CMakeLists.txt @@ -39,6 +39,16 @@ o2physics_add_dpl_workflow(epdzeroflow-analysis PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(j-v02-dmeson-analysis + SOURCES jV02DmesonAnalysis.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(j-v02-dmeson-toy + SOURCES jV02DmesonToy.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(j-fluc-efficiency-task SOURCES jFlucEfficiencyTask.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::JCorran diff --git a/PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx b/PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx new file mode 100644 index 00000000000..9cba9a6e61a --- /dev/null +++ b/PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx @@ -0,0 +1,374 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file jV02DmesonAnalysis.cxx +/// \brief D0 v02(pT) on the JCorran derived tables +/// \author Dong Jo Kim (djkim@jyu.fi) +/// +/// v02(pT) = _c / (_c ). +/// X and Y are the corrected Q2 of two forward detectors (default FT0A x FT0C) +/// from the combined Qvectors table, with the same indexing as jEPDzeroFlowAnalysis. +/// Z_D(pT) = N_D(pT) / N_ch. N_D comes from CF2ProngTracks (D0 and D0bar). +/// N_ch comes from CFTracks. Both derived tables are indexed to CFCollisions, +/// so the original collision is matched through CFCollRefs (one row per kept +/// collision, written by filter-correlations with cfgTransientTables). +/// The D azimuth is not part of the Q-vector. +/// Do not divide by an extra resolution: it cancels in the ratio. +/// +/// Definition: arXiv:2506.18690, Appendix A. + +#include "PWGCF/DataModel/CorrelationsDerived.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Qvectors.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +#define V02_FILL_QA(NAME, STEP) \ + for (int qaBin = 1; qaBin <= (STEP); ++qaBin) { \ + histos.fill(HIST(NAME), static_cast(qaBin)); \ + } + +struct JV02DmesonAnalysis { + enum Detector { + kFT0C = 0, + kFT0A, + kFT0M, + kFV0A, + kTPCpos, + kTPCneg, + kTPCall + }; + static constexpr int NDetectors = 7; + static constexpr int NCorrections = 4; + static constexpr int FullyCorrected = 3; + static constexpr int StepAccepted = 5; + static constexpr float InvalidQvector = 900.f; + + using MyCollisions = soa::Join; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + Configurable cfgCentEst{"cfgCentEst", "FT0C", "Centrality estimator: FT0C or FT0M"}; + Configurable cfgRefAName{"cfgRefAName", "FT0A", "Reference detector A: FT0A, FT0C, FT0M, FV0A, TPCpos, TPCneg, TPCall"}; + Configurable cfgRefBName{"cfgRefBName", "FT0C", "Reference detector B"}; + Configurable cfgQvecMode{"cfgQvecMode", 0, "Harmonic slot in qvector-table cfgnMods. 0 is v2 when cfgnMods starts at 2"}; + Configurable cfgNSubsample{"cfgNSubsample", 10, "Number of bootstrap subsamples"}; + Configurable cfgSeed{"cfgSeed", 1, "Seed for the subsample index"}; + Configurable cfgMaxOccupancy{"cfgMaxOccupancy", 999999, "Maximum track occupancy"}; + Configurable cfgMinOccupancy{"cfgMinOccupancy", 0, "Minimum track occupancy"}; + Configurable cfgVtxZ{"cfgVtxZ", 10.f, "Maximum |z_vtx| (cm)"}; + Configurable cfgCentMin{"cfgCentMin", 0.f, "Minimum centrality"}; + Configurable cfgCentMax{"cfgCentMax", 80.f, "Maximum centrality"}; + Configurable cfgEtaMax{"cfgEtaMax", 0.8f, "Maximum |eta| of CF tracks used for N_ch"}; + Configurable cfgPtMinCh{"cfgPtMinCh", 0.2f, "Minimum charged-track pT (GeV/c)"}; + Configurable cfgPtMaxCh{"cfgPtMaxCh", 3.f, "Maximum charged-track pT (GeV/c)"}; + Configurable cfgPtMinD{"cfgPtMinD", 1.f, "Minimum D0 pT (GeV/c)"}; + Configurable cfgPtMaxD{"cfgPtMaxD", 24.f, "Maximum D0 pT (GeV/c)"}; + Configurable cfgMinNch{"cfgMinNch", 10.f, "Minimum N_ch"}; + Configurable cfgMinSumAmpl{"cfgMinSumAmpl", 1e-4f, "Minimum Q-vector amplitude"}; + Configurable cfgRequireSel8{"cfgRequireSel8", true, "Require sel8"}; + Configurable cfgCutNoSameBunchPileup{"cfgCutNoSameBunchPileup", true, "Require kNoSameBunchPileup"}; + Configurable cfgCutGoodZvtxFT0vsPV{"cfgCutGoodZvtxFT0vsPV", true, "Require kIsGoodZvtxFT0vsPV"}; + Configurable cfgCutNoTimeFrameBorder{"cfgCutNoTimeFrameBorder", true, "Require kNoTimeFrameBorder"}; + Configurable cfgCutNoITSROFrameBorder{"cfgCutNoITSROFrameBorder", true, "Require kNoITSROFrameBorder"}; + Configurable cfgRejectDaughters{"cfgRejectDaughters", false, "Drop D0 daughter CF tracks from N_ch"}; + + ConfigurableAxis cfgAxisCent{"cfgAxisCent", {8, 0., 80.}, "Centrality axis"}; + ConfigurableAxis cfgAxisPtD{"cfgAxisPtD", {VARIABLE_WIDTH, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 8.f, 12.f, 16.f, 24.f}, "D0 pT axis"}; + ConfigurableAxis cfgAxisPtHad{"cfgAxisPtHad", {VARIABLE_WIDTH, 0.2f, 0.4f, 0.6f, 0.8f, 1.f, 1.2f, 1.5f, 2.f, 2.5f, 3.f}, "Charged-hadron pT axis"}; + ConfigurableAxis cfgAxisMass{"cfgAxisMass", {70, 1.70, 2.05}, "D0 invariant-mass axis"}; + + int mRefA = kFT0A; + int mRefB = kFT0C; + int mNSubsample = 1; + Preslice mPerCFTrack = aod::cftrack::cfCollisionId; + Preslice mPerCFCand = aod::cftrack::cfCollisionId; + const TAxis* mHadPtAxis = nullptr; + std::vector mHadCounts; + std::mt19937 mRng{1}; + std::uniform_int_distribution mSampleDist; + + int detectorId(const std::string& name) const + { + if (name == "FT0C") { + return kFT0C; + } + if (name == "FT0A") { + return kFT0A; + } + if (name == "FT0M") { + return kFT0M; + } + if (name == "FV0A") { + return kFV0A; + } + if (name == "TPCpos") { + return kTPCpos; + } + if (name == "TPCneg") { + return kTPCneg; + } + if (name == "TPCall") { + return kTPCall; + } + return -1; + } + + void init(InitContext const&) + { + mRefA = detectorId(cfgRefAName.value); + mRefB = detectorId(cfgRefBName.value); + if (mRefA < 0 || mRefB < 0 || mRefA == mRefB) { + LOGF(fatal, "cfgRefAName and cfgRefBName must be two different detectors"); + } + mNSubsample = cfgNSubsample > 0 ? cfgNSubsample.value : 1; + mRng.seed(static_cast(cfgSeed)); + mSampleDist = std::uniform_int_distribution(0, mNSubsample - 1); + + const AxisSpec subsampleAxis{mNSubsample, 0., static_cast(mNSubsample), "subsample"}; + const AxisSpec qaAxis{6, 0.5, 6.5, "1 seen; 2 sel8; 3 vertex; 4 quality; 5 centrality; 6 accepted"}; + + if (doprocessReference) { + histos.add("hadron/hEventQa", "1 seen; 2 sel8; 3 vertex; 4 quality; 5 centrality; 6 accepted", {HistType::kTH1F, {qaAxis}}); + histos.add("hadron/hEvents", "Selected events;centrality (%);events", {HistType::kTH1F, {cfgAxisCent}}); + histos.add("hadron/Prof_XY", "Bulk #LTXY#GT;centrality (%);#LTXY#GT", {HistType::kTProfile, {cfgAxisCent}}); + histos.add("hadron/Prof_Nch", "N_{ch};centrality (%);#LTN_{ch}#GT", {HistType::kTProfile, {cfgAxisCent}}); + histos.add("hadron/Prof_Z", "Z = n(p_{T})/N_{ch};centrality (%);p_{T} (GeV/c);#LTZ#GT", {HistType::kTProfile2D, {cfgAxisCent, cfgAxisPtHad}}); + histos.add("hadron/Prof_XYZ", "XY Z;centrality (%);p_{T} (GeV/c);#LTXYZ#GT", {HistType::kTProfile2D, {cfgAxisCent, cfgAxisPtHad}}); + histos.add("hadron/Prof_XY_sub", "Bulk #LTXY#GT per subsample;subsample;centrality (%);#LTXY#GT", {HistType::kTProfile2D, {subsampleAxis, cfgAxisCent}}); + histos.add("hadron/Prof_Z_sub", "Z per subsample;subsample;centrality (%);p_{T} (GeV/c);#LTZ#GT", {HistType::kTProfile3D, {subsampleAxis, cfgAxisCent, cfgAxisPtHad}}); + histos.add("hadron/Prof_XYZ_sub", "XYZ per subsample;subsample;centrality (%);p_{T} (GeV/c);#LTXYZ#GT", {HistType::kTProfile3D, {subsampleAxis, cfgAxisCent, cfgAxisPtHad}}); + mHadPtAxis = histos.get(HIST("hadron/Prof_Z"))->GetYaxis(); + mHadCounts.assign(mHadPtAxis->GetNbins(), 0.f); + } + if (doprocessD0) { + histos.add("D0/hEventQa", "1 seen; 2 sel8; 3 vertex; 4 quality; 5 centrality; 6 accepted", {HistType::kTH1F, {qaAxis}}); + histos.add("D0/hEvents", "Selected events;centrality (%);events", {HistType::kTH1F, {cfgAxisCent}}); + histos.add("D0/Prof_XY", "Bulk #LTXY#GT;centrality (%);#LTXY#GT", {HistType::kTProfile, {cfgAxisCent}}); + histos.add("D0/Prof_Nch", "N_{ch};centrality (%);#LTN_{ch}#GT", {HistType::kTProfile, {cfgAxisCent}}); + histos.add("D0/hMassRaw", "Weight 1/N_{ch};centrality (%);p_{T} (GeV/c);m (GeV/c^{2})", {HistType::kTH3F, {cfgAxisCent, cfgAxisPtD, cfgAxisMass}}, true); + histos.add("D0/hMassCorr", "Weight XY/N_{ch};centrality (%);p_{T} (GeV/c);m (GeV/c^{2})", {HistType::kTH3F, {cfgAxisCent, cfgAxisPtD, cfgAxisMass}}, true); + histos.add("D0/hMassRawSub", "Weight 1/N_{ch};subsample;centrality (%);p_{T} (GeV/c);m (GeV/c^{2})", {HistType::kTHnSparseF, {subsampleAxis, cfgAxisCent, cfgAxisPtD, cfgAxisMass}}, true); + histos.add("D0/hMassCorrSub", "Weight XY/N_{ch};subsample;centrality (%);p_{T} (GeV/c);m (GeV/c^{2})", {HistType::kTHnSparseF, {subsampleAxis, cfgAxisCent, cfgAxisPtD, cfgAxisMass}}, true); + } + } + + template + float centralityOf(const TCollision& collision) + { + if (cfgCentEst.value == "FT0M") { + return collision.centFT0M(); + } + return collision.centFT0C(); + } + + template + int passedEventStep(const TCollision& collision, float& centrality) + { + if (cfgRequireSel8 && !collision.sel8()) { + return 1; + } + if (std::abs(collision.posZ()) > cfgVtxZ) { + return 2; + } + const bool rejected = + (cfgCutNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) || + (cfgCutGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) || + (cfgCutNoTimeFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) || + (cfgCutNoITSROFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) || + collision.trackOccupancyInTimeRange() > cfgMaxOccupancy || + collision.trackOccupancyInTimeRange() < cfgMinOccupancy; + if (rejected) { + return 3; + } + centrality = centralityOf(collision); + if (centrality < cfgCentMin || centrality > cfgCentMax) { + return 4; + } + return 5; + } + + template + bool referenceProduct(const TCollision& collision, float& xy) + { + const auto& re = collision.qvecRe(); + const auto& im = collision.qvecIm(); + const auto& amp = collision.qvecAmp(); + const int idxA = cfgQvecMode * NDetectors * NCorrections + mRefA * NCorrections + FullyCorrected; + const int idxB = cfgQvecMode * NDetectors * NCorrections + mRefB * NCorrections + FullyCorrected; + const int ampA = cfgQvecMode * NDetectors + mRefA; + const int ampB = cfgQvecMode * NDetectors + mRefB; + if (idxA >= static_cast(re.size()) || idxB >= static_cast(im.size()) || ampA >= static_cast(amp.size()) || ampB >= static_cast(amp.size())) { + return false; + } + if (!(amp[ampA] > cfgMinSumAmpl) || !(amp[ampB] > cfgMinSumAmpl)) { + return false; + } + if (re[idxA] > InvalidQvector || re[idxB] > InvalidQvector) { + return false; + } + xy = re[idxA] * re[idxB] + im[idxA] * im[idxB]; + return std::isfinite(xy); + } + + template + float countCharged(const TTracks& tracks, const std::unordered_set& daughters, bool differential) + { + float nch = 0.f; + if (differential) { + std::fill(mHadCounts.begin(), mHadCounts.end(), 0.f); + } + for (const auto& track : tracks) { + const float pt = track.pt(); + if (track.sign() == 0 || pt < cfgPtMinCh || pt >= cfgPtMaxCh || std::abs(track.eta()) > cfgEtaMax) { + continue; + } + if (cfgRejectDaughters && daughters.contains(static_cast(track.globalIndex()))) { + continue; + } + nch += 1.f; + if (differential && mHadPtAxis != nullptr) { + const int bin = mHadPtAxis->FindBin(pt); + if (bin >= 1 && bin <= mHadPtAxis->GetNbins()) { + mHadCounts[bin - 1] += 1.f; + } + } + } + return nch; + } + + static bool isD0Decay(uint8_t decay) + { + return decay == aod::cf2prongtrack::D0ToPiK || decay == aod::cf2prongtrack::D0barToKPi || decay == aod::cf2prongtrack::D0barToKPiExclusive; + } + + template + static int cfCollisionIndex(const TRefs& refs) + { + if (refs.size() != 1) { + return -1; + } + return static_cast(refs.begin().globalIndex()); + } + + void processReference(MyCollisions::iterator const& collision, aod::BCs const&, aod::CFCollisions const&, aod::CFCollRefs const& refs, aod::CFTracks const& tracks) + { + float cent = -1.f; + float xy = 0.f; + const int step = passedEventStep(collision, cent); + V02_FILL_QA("hadron/hEventQa", step); + const int cfId = cfCollisionIndex(refs); + if (step < StepAccepted || cfId < 0 || !referenceProduct(collision, xy)) { + return; + } + const std::unordered_set noDaughters; + const float nch = countCharged(tracks.sliceBy(mPerCFTrack, cfId), noDaughters, true); + if (nch < cfgMinNch) { + return; + } + histos.fill(HIST("hadron/hEventQa"), 6.f); + const int sample = mSampleDist(mRng); + histos.fill(HIST("hadron/hEvents"), cent); + histos.fill(HIST("hadron/Prof_XY"), cent, xy); + histos.fill(HIST("hadron/Prof_Nch"), cent, nch); + histos.fill(HIST("hadron/Prof_XY_sub"), static_cast(sample), cent, xy); + const int nBins = mHadPtAxis->GetNbins(); + for (int ibin = 1; ibin <= nBins; ++ibin) { + const float pt = static_cast(mHadPtAxis->GetBinCenter(ibin)); + const float z = mHadCounts[ibin - 1] / nch; + histos.fill(HIST("hadron/Prof_Z"), cent, pt, z); + histos.fill(HIST("hadron/Prof_XYZ"), cent, pt, xy * z); + histos.fill(HIST("hadron/Prof_Z_sub"), static_cast(sample), cent, pt, z); + histos.fill(HIST("hadron/Prof_XYZ_sub"), static_cast(sample), cent, pt, xy * z); + } + } + PROCESS_SWITCH(JV02DmesonAnalysis, processReference, "Charged-hadron v02(pT) closure", true); + + void processD0(MyCollisions::iterator const& collision, aod::BCs const&, aod::CFCollisions const&, aod::CFCollRefs const& refs, aod::CF2ProngTracks const& candidates, aod::CFTracks const& tracks) + { + float cent = -1.f; + float xy = 0.f; + const int step = passedEventStep(collision, cent); + V02_FILL_QA("D0/hEventQa", step); + const int cfId = cfCollisionIndex(refs); + if (step < StepAccepted || cfId < 0 || !referenceProduct(collision, xy)) { + return; + } + auto eventCandidates = candidates.sliceBy(mPerCFCand, cfId); + auto eventTracks = tracks.sliceBy(mPerCFTrack, cfId); + + std::unordered_set daughters; + if (cfgRejectDaughters) { + for (const auto& cand : eventCandidates) { + if (!isD0Decay(cand.decay())) { + continue; + } + daughters.insert(static_cast(cand.cfTrackProng0Id())); + daughters.insert(static_cast(cand.cfTrackProng1Id())); + } + } + const float nch = countCharged(eventTracks, daughters, false); + if (nch < cfgMinNch) { + return; + } + histos.fill(HIST("D0/hEventQa"), 6.f); + const int sample = mSampleDist(mRng); + const float sampleCoord = static_cast(sample); + histos.fill(HIST("D0/hEvents"), cent); + histos.fill(HIST("D0/Prof_XY"), cent, xy); + histos.fill(HIST("D0/Prof_Nch"), cent, nch); + + const float wRaw = 1.f / nch; + const float wCorr = xy / nch; + for (const auto& cand : eventCandidates) { + if (!isD0Decay(cand.decay())) { + continue; + } + const float pt = cand.pt(); + if (pt < cfgPtMinD || pt >= cfgPtMaxD) { + continue; + } + const float mass = cand.invMass(); + histos.fill(HIST("D0/hMassRaw"), cent, pt, mass, wRaw); + histos.fill(HIST("D0/hMassCorr"), cent, pt, mass, wCorr); + histos.fill(HIST("D0/hMassRawSub"), sampleCoord, cent, pt, mass, wRaw); + histos.fill(HIST("D0/hMassCorrSub"), sampleCoord, cent, pt, mass, wCorr); + } + } + PROCESS_SWITCH(JV02DmesonAnalysis, processD0, "D0 and D0bar v02(pT) from CF2ProngTracks", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGCF/JCorran/Tasks/jV02DmesonToy.cxx b/PWGCF/JCorran/Tasks/jV02DmesonToy.cxx new file mode 100644 index 00000000000..beb6d6098d1 --- /dev/null +++ b/PWGCF/JCorran/Tasks/jV02DmesonToy.cxx @@ -0,0 +1,153 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file jV02DmesonToy.cxx +/// \brief One timeframe of synthetic collisions for JV02DmesonAnalysis. +/// \author Dong Jo Kim (djkim@jyu.fi) +/// +/// 200 events at centrality 25. N_ch = 20. +/// Even events: XY = 0.08, 6 tracks in pT [1.0, 1.2), 4 D0 at mass 1.864 and pT 3.5. +/// Odd events: XY = 0.02, 2 tracks in pT [1.0, 1.2), 1 D0 at the same mass and pT. +/// The remaining charged tracks sit at pT 0.3. +/// Expected charged-hadron v02 in the 1.1 GeV/c bin is 0.3. +/// Expected D0 v02, with an empty sideband, is 0.36. + +#include "PWGCF/DataModel/CorrelationsDerived.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Qvectors.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +struct JV02DmesonToy { + Produces bcs; + Produces collisions; + Produces evsels; + Produces centsC; + Produces centsM; + Produces qvectors; + Produces cfCollisions; + Produces cfRefs; + Produces cfTracks; + Produces cfCandidates; + + void fill() + { + constexpr int NEvents = 200; + constexpr int NDetectors = 7; + constexpr int NCorrections = 4; + constexpr int FullyCorrected = 3; + constexpr int Ft0C = 0; + constexpr int Ft0A = 1; + constexpr int EventPair = 2; + constexpr float Centrality = 25.f; + constexpr float Nch = 20.f; + const uint64_t selection = + (uint64_t{1} << aod::evsel::kNoSameBunchPileup) | + (uint64_t{1} << aod::evsel::kIsGoodZvtxFT0vsPV) | + (uint64_t{1} << aod::evsel::kNoTimeFrameBorder) | + (uint64_t{1} << aod::evsel::kNoITSROFrameBorder); + + std::vector re(NDetectors * NCorrections, 0.f); + std::vector im(NDetectors * NCorrections, 0.f); + std::vector amp(NDetectors, 0.f); + amp[Ft0A] = 1.f; + amp[Ft0C] = 1.f; + + for (int ievt = 0; ievt < NEvents; ++ievt) { + const bool high = ievt % EventPair == 0; + const float xy = high ? 0.08f : 0.02f; + const float q = std::sqrt(xy); + re[Ft0C * NCorrections + FullyCorrected] = q; + re[Ft0A * NCorrections + FullyCorrected] = q; + + bcs(544116, static_cast(ievt), uint64_t{0}, uint64_t{0}); + collisions(ievt, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, uint16_t{0}, 1.f, uint16_t{20}, 0.f, 0.f); + evsels(uint32_t{0}, selection, uint32_t{0}, false, true, ievt, -1, -1, -1, -1, 100, 0.f, 0.f); + centsC(Centrality); + centsM(Centrality); + qvectors(Centrality, true, re, im, amp); + cfCollisions(544116, 0.f, Nch, uint64_t{0}); + cfRefs(ievt); + + const int nSignalTracks = high ? 6 : 2; + for (int itrk = 0; itrk < static_cast(Nch); ++itrk) { + const float pt = itrk < nSignalTracks ? 1.1f : 0.3f; + const int8_t sign = itrk % EventPair == 0 ? int8_t{1} : int8_t{-1}; + cfTracks(ievt, pt, 0.f, 0.f, sign, uint8_t{1}); + } + const int nD0 = high ? 4 : 1; + for (int icand = 0; icand < nD0; ++icand) { + cfCandidates(ievt, 0, 1, 3.5f, 0.f, 0.f, static_cast(o2::constants::physics::MassD0), static_cast(aod::cf2prongtrack::D0ToPiK)); + } + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + DataProcessorSpec{ + "jv02-dmeson-toy", + Inputs{}, + Outputs{ + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputSpec{"TFN", "TFNumber"}, + OutputSpec{"TFF", "TFFilename"}, + }, + AlgorithmSpec{adaptStateless([](ProcessingContext& pc) { + JV02DmesonToy toy; + toy.bcs.resetCursor(std::move(pc.outputs().make(OutputForTable::ref()))); + toy.collisions.resetCursor(std::move(pc.outputs().make(OutputForTable::ref()))); + toy.evsels.resetCursor(std::move(pc.outputs().make(OutputForTable::ref()))); + toy.centsC.resetCursor(std::move(pc.outputs().make(OutputForTable::ref()))); + toy.centsM.resetCursor(std::move(pc.outputs().make(OutputForTable::ref()))); + toy.qvectors.resetCursor(std::move(pc.outputs().make(OutputForTable::ref()))); + toy.cfCollisions.resetCursor(std::move(pc.outputs().make(OutputForTable::ref()))); + toy.cfRefs.resetCursor(std::move(pc.outputs().make(OutputForTable::ref()))); + toy.cfTracks.resetCursor(std::move(pc.outputs().make(OutputForTable::ref()))); + toy.cfCandidates.resetCursor(std::move(pc.outputs().make(OutputForTable::ref()))); + toy.fill(); + pc.outputs().make(Output{"TFN", "TFNumber"}) = 1; + pc.outputs().make(Output{"TFF", "TFFilename"}) = std::string{"toy"}; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + })}, + }, + }; +} From eebe6491c3635adaa808df68ee3bf5935bf0f058 Mon Sep 17 00:00:00 2001 From: DongJo Kim Date: Tue, 20 Oct 2026 11:01:14 +0300 Subject: [PATCH 2/3] [PWGCF] Include for std::move in the v02 toy. Co-authored-by: Cursor --- PWGCF/JCorran/Tasks/jV02DmesonToy.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/PWGCF/JCorran/Tasks/jV02DmesonToy.cxx b/PWGCF/JCorran/Tasks/jV02DmesonToy.cxx index beb6d6098d1..9d07721f83c 100644 --- a/PWGCF/JCorran/Tasks/jV02DmesonToy.cxx +++ b/PWGCF/JCorran/Tasks/jV02DmesonToy.cxx @@ -40,6 +40,7 @@ #include #include #include +#include #include using namespace o2; From 853239cc71d9e88367860ef8c2fa017b43b514d3 Mon Sep 17 00:00:00 2001 From: DongJo Kim Date: Tue, 20 Oct 2026 11:03:42 +0300 Subject: [PATCH 3/3] [PWGCF] Align the v02 QA macro with clang-format. Co-authored-by: Cursor --- PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx b/PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx index 9cba9a6e61a..d30851776e4 100644 --- a/PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx +++ b/PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx @@ -53,9 +53,9 @@ using namespace o2; using namespace o2::framework; -#define V02_FILL_QA(NAME, STEP) \ - for (int qaBin = 1; qaBin <= (STEP); ++qaBin) { \ - histos.fill(HIST(NAME), static_cast(qaBin)); \ +#define V02_FILL_QA(NAME, STEP) \ + for (int qaBin = 1; qaBin <= (STEP); ++qaBin) { \ + histos.fill(HIST(NAME), static_cast(qaBin)); \ } struct JV02DmesonAnalysis {