From 5f2d56adfa6a6e780d933c40d44c67d640d17ec9 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 2 Sep 2026 11:26:44 +0200 Subject: [PATCH] Add forward compatibility with the new propagation-service-v2 --- Common/Core/TPCVDriftManager.h | 21 ++++++ Common/Tools/TrackPropagationModule.h | 93 ++++++++++++++++++-------- Common/Tools/TrackTuner.h | 15 ++++- PWGLF/Utils/strangenessBuilderModule.h | 47 +++++++++++-- 4 files changed, 143 insertions(+), 33 deletions(-) diff --git a/Common/Core/TPCVDriftManager.h b/Common/Core/TPCVDriftManager.h index 18c02b016b9..0a76fe59c5b 100644 --- a/Common/Core/TPCVDriftManager.h +++ b/Common/Core/TPCVDriftManager.h @@ -63,6 +63,27 @@ class TPCVDriftManager LOGP(info, "Updated VDrift for timestamp {} with vdrift={:.7f} (cm/ns)", mVD->creationTime, mTPCVDriftNS); } + // Adopts a drift correction obtained elsewhere, typically straight from the + // aod::TpcCalibCCDBObjects column, so no CCDB manager is involved at all. + void update(const o2::tpc::VDriftCorrFact& vd) noexcept + { + if (mVD == &vd) { // same object as last time, nothing to recompute + return; + } + if (vd.firstTime < 0 || vd.lastTime < 0) { + LOGP(error, "Got invalid VDriftCorrFact created at {}", vd.creationTime); + mValid = false; + return; + } + mVD = &vd; + + // TODO account for laser calib + + mTPCVDriftNS = mVD->refVDrift * mVD->corrFact * 1e-3; + mValid = true; + LOGP(debug, "Updated VDrift for timestamp {} with vdrift={:.7f} (cm/ns)", mVD->creationTime, mTPCVDriftNS); + } + template [[nodiscard]] bool moveTPCTrack(const Collision& col, const TrackExtra& trackExtra, Track& track) noexcept { diff --git a/Common/Tools/TrackPropagationModule.h b/Common/Tools/TrackPropagationModule.h index 2fa7ea8a10b..d7f7dd6a75d 100644 --- a/Common/Tools/TrackPropagationModule.h +++ b/Common/Tools/TrackPropagationModule.h @@ -22,6 +22,7 @@ #include "Common/Tools/TrackTuner.h" #include +#include #include #include #include @@ -31,13 +32,13 @@ #include #include #include -#include #include #include #include #include #include +#include #include #include @@ -109,7 +110,9 @@ class TrackPropagationModule bool autoDetectDcaCalib = false; // track tuner setting template - void init(TConfigurableGroup const& cGroup, TrackTuner& trackTunerObj, THistoRegistry& registry, TInitContext& initContext) + /// \param calibFromCCDBColumns the task supplies the TrackTuner calibrations from the + /// aod::TrackTunerCCDBObjects columns, so nothing is fetched from CCDB here. + void init(TConfigurableGroup const& cGroup, TrackTuner& trackTunerObj, THistoRegistry& registry, TInitContext& initContext, bool calibFromCCDBColumns = false) { // Checking if the tables are requested in the workflow and enabling them fillTracks = o2::common::core::isTableRequiredInWorkflow(initContext, "Tracks"); @@ -176,23 +179,31 @@ class TrackPropagationModule /// read the track tuner instance configurations, /// to understand whether the TrackTuner::getDcaGraphs function can be called here (input path from string/configurables) /// or inside the process function, to "auto-detect" the input file based on the run number - const auto& workflows = initContext.services().template get(); - for (o2::framework::DeviceSpec const& device : workflows.devices) { /// loop over devices - if (device.name == "propagation-service") { - // loop over the options - // to find the value of TrackTuner::autoDetectDcaCalib - for (const auto& option : device.options) { /// loop over options - if (option.name == "trackTuner.autoDetectDcaCalib") { - // found it! - autoDetectDcaCalib = option.defaultValue.get(); - break; - } - } /// end loop over options + // Read the option off the device we are actually running in. This used to search + // the workflow for a device literally named "propagation-service", which silently + // matched nothing in any other task (propagation-service-v2, -run2, ...), leaving + // autoDetectDcaCalib at its default no matter how the task was configured. + o2::framework::DeviceSpec const& device = initContext.services().template get(); + for (const auto& option : device.options) { /// loop over options + if (option.name == "trackTuner.autoDetectDcaCalib") { + // found it! + autoDetectDcaCalib = option.defaultValue.get(); break; } - } /// end loop over devices + } /// end loop over options LOG(info) << "[TrackPropagationModule] trackTuner.autoDetectDcaCalib it's equal to " << autoDetectDcaCalib; - if (!autoDetectDcaCalib) { + if (calibFromCCDBColumns && trackTunerObj.isInputFileFromCCDB) { + // The column is the single source of truth for the path: its default carries the + // per-period mapping and it is overridden through "ccdb:fTrackTunerDca". Silently + // preferring one of two path settings is how calibrations diverge unnoticed, so a + // leftover trackTuner.pathInputFile is an error rather than a shadowed value. + if (!trackTunerObj.pathInputFile.empty()) { + LOG(fatal) << "[TrackPropagationModule] trackTuner.pathInputFile is set to '" << trackTunerObj.pathInputFile + << "' while the TrackTuner calibrations are taken from the aod::TrackTunerCCDBObjects columns. " + << "Set the path through the \"ccdb:fTrackTunerDca\" option instead, or unset trackTuner.pathInputFile."; + } + LOG(info) << "[TrackPropagationModule] TrackTuner calibrations come from CCDB columns; graphs retrieved in the process function"; + } else if (!autoDetectDcaCalib) { LOG(info) << "[TrackPropagationModule] retrieve the graphs already (we are in propagationService::Init() function)"; trackTunerObj.getDcaGraphs(); } else { @@ -215,24 +226,50 @@ class TrackPropagationModule registry.template get(HIST("hPropagation"))->GetXaxis()->SetBinLabel(3, "Propagation OK"); } + /// Legacy overload for callers still holding a StandardCCDBLoader; forwards the two + /// run-scoped values actually used. Prefer the overload below, which lets the caller + /// source them from CCDB columns instead of a CCDB query. template void fillTrackTables(TConfigurableGroup const& cGroup, TrackTuner& trackTunerObj, TCCDBLoader const& ccdbLoader, TCollisions const& collisions, TTracks const& tracks, TOutputGroup& cursors, THistoRegistry& registry) + { + fillTrackTables(cGroup, trackTunerObj, ccdbLoader.runNumber, ccdbLoader.mMeanVtx, collisions, tracks, cursors, registry); + } + + /// Takes the run-scoped conditions it actually needs (run number for the TrackTuner + /// path, mean vertex for the DCA reference) rather than a CCDB loader object, so that + /// callers are free to source them from CCDB columns instead of a CCDB query. + template + void fillTrackTables(TConfigurableGroup const& cGroup, TrackTuner& trackTunerObj, int currentRunNumber, o2::dataformats::MeanVertexObject const* meanVtx, TCollisions const& collisions, TTracks const& tracks, TOutputGroup& cursors, THistoRegistry& registry) + { + fillTrackTables(cGroup, trackTunerObj, currentRunNumber, meanVtx, nullptr, nullptr, collisions, tracks, cursors, registry); + } + + /// As above, plus the TrackTuner calibration lists taken from the + /// aod::TrackTunerCCDBObjects columns. When they are given, the run-range table that + /// getPathInputFileAutomaticFromCCDB() would have walked has already been applied by + /// the CCDB fetcher, so no CCDB query happens here at all. + template + void fillTrackTables(TConfigurableGroup const& cGroup, TrackTuner& trackTunerObj, int currentRunNumber, o2::dataformats::MeanVertexObject const* meanVtx, TList* dcaCalib, TList* qOverPtCalib, TCollisions const& collisions, TTracks const& tracks, TOutputGroup& cursors, THistoRegistry& registry) { /// retrieve the TrackTuner calibration graphs *if not done yet* /// i.e. if autodetect is required - if (cGroup.useTrackTuner.value && autoDetectDcaCalib && !trackTunerObj.areGraphsConfigured) { + if (cGroup.useTrackTuner.value && !trackTunerObj.areGraphsConfigured && (autoDetectDcaCalib || dcaCalib != nullptr)) { - /// get the run number from the ccdb loader, already initialized - const int runNumber = ccdbLoader.runNumber; - trackTunerObj.setRunNumber(runNumber); + trackTunerObj.setRunNumber(currentRunNumber); - /// setup the "auto-detected" path based on the run number - trackTunerObj.getPathInputFileAutomaticFromCCDB(); - trackTunedTracks->SetTitle(trackTunerObj.outputString.c_str()); + if (dcaCalib != nullptr) { + /// the path was resolved by the CCDB fetcher from the column's run-range mapping + trackTunedTracks->SetTitle(trackTunerObj.outputString.c_str()); + trackTunerObj.getDcaGraphs(dcaCalib, qOverPtCalib); + } else { + /// setup the "auto-detected" path based on the run number + trackTunerObj.getPathInputFileAutomaticFromCCDB(); + trackTunedTracks->SetTitle(trackTunerObj.outputString.c_str()); - /// now that the path is ok, retrieve the graphs - trackTunerObj.getDcaGraphs(); + /// now that the path is ok, retrieve the graphs + trackTunerObj.getDcaGraphs(); + } } if (!fillTracks) { @@ -314,11 +351,11 @@ class TrackPropagationModule } } else { if (fillTracksCov) { - mVtx.setPos({ccdbLoader.mMeanVtx->getX(), ccdbLoader.mMeanVtx->getY(), ccdbLoader.mMeanVtx->getZ()}); - mVtx.setCov(ccdbLoader.mMeanVtx->getSigmaX() * ccdbLoader.mMeanVtx->getSigmaX(), 0.0f, ccdbLoader.mMeanVtx->getSigmaY() * ccdbLoader.mMeanVtx->getSigmaY(), 0.0f, 0.0f, ccdbLoader.mMeanVtx->getSigmaZ() * ccdbLoader.mMeanVtx->getSigmaZ()); + mVtx.setPos({meanVtx->getX(), meanVtx->getY(), meanVtx->getZ()}); + mVtx.setCov(meanVtx->getSigmaX() * meanVtx->getSigmaX(), 0.0f, meanVtx->getSigmaY() * meanVtx->getSigmaY(), 0.0f, 0.0f, meanVtx->getSigmaZ() * meanVtx->getSigmaZ()); isPropagationOK = o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, mTrackParCov, 2.f, matCorr, &mDcaInfoCov); } else { - isPropagationOK = o2::base::Propagator::Instance()->propagateToDCABxByBz({ccdbLoader.mMeanVtx->getX(), ccdbLoader.mMeanVtx->getY(), ccdbLoader.mMeanVtx->getZ()}, mTrackPar, 2.f, matCorr, &mDcaInfo); + isPropagationOK = o2::base::Propagator::Instance()->propagateToDCABxByBz({meanVtx->getX(), meanVtx->getY(), meanVtx->getZ()}, mTrackPar, 2.f, matCorr, &mDcaInfo); } } if (isPropagationOK) { diff --git a/Common/Tools/TrackTuner.h b/Common/Tools/TrackTuner.h index 13a52e46ae7..5700038291d 100644 --- a/Common/Tools/TrackTuner.h +++ b/Common/Tools/TrackTuner.h @@ -19,7 +19,6 @@ #define COMMON_TOOLS_TRACKTUNER_H_ #include -#include #include #include #include @@ -630,6 +629,20 @@ struct TrackTuner : o2::framework::ConfigurableGroup { ccdb_object_qoverpt = dynamic_cast(inputFileQoverPt->Get("ccdb_object")); } + getDcaGraphs(ccdb_object_dca, ccdb_object_qoverpt); + } + + /// \brief Builds the correction graphs from lists obtained elsewhere, typically straight + /// from the aod::TrackTunerCCDBObjects columns, so no CCDB client is involved. + void getDcaGraphs(TList* ccdb_object_dca, TList* ccdb_object_qoverpt) + { + /// abort if the graphs were already loaded + if (areGraphsConfigured) { + LOG(fatal) << "[TrackTuner::getDcaGraphs()] Function already called, i.e. the calibrations are already loaded. This further call should never happen. Aborting..."; + } + std::string grOneOverPtPionNameMC = "sigmaVsPtMc"; + std::string grOneOverPtPionNameData = "sigmaVsPtData"; + // choose wheter to use corrections w/ PV refit or w/o it, and retrieve the proper TList std::string dir = "woPvRefit"; if (usePvRefitCorrections) { diff --git a/PWGLF/Utils/strangenessBuilderModule.h b/PWGLF/Utils/strangenessBuilderModule.h index 563b75e6d95..f40111668cb 100644 --- a/PWGLF/Utils/strangenessBuilderModule.h +++ b/PWGLF/Utils/strangenessBuilderModule.h @@ -49,6 +49,7 @@ #include #include +#include #include #include #include @@ -844,6 +845,30 @@ class BuilderModule return idx; } + // Feed the V-drift manager from the aod::TpcCalibCCDBObjects column when the BC table + // carries it, else fall back to a CCDB query. Lets migrated and un-migrated tasks share + // this module unchanged. + template + void updateVDrift(TCollision const& collision) + { + auto const& bc = collision.template bc_as(); + if constexpr (requires { bc.vdriftTgl(); }) { + mVDriftMgr.update(bc.vdriftTgl()); + } else { + mVDriftMgr.update(bc.timestamp()); + } + } + + // Overload for tasks whose BC table carries the V-drift CCDB column: nothing in here + // needs a CCDB manager any more, so they need not own one. + template + bool initCCDB(TBCs const& bcs, TCollisions const& collisions) + { + static_assert(requires(typename TBCs::iterator bc) { bc.vdriftTgl(); }, "initCCDB without a CCDB manager needs a BC table joined with aod::TpcCalibCCDBObjects"); + std::nullptr_t noCCDB{}; + return initCCDB(noCCDB, bcs, collisions); + } + template bool initCCDB(TCCDB& ccdb, TBCs const& bcs, TCollisions const& collisions) { @@ -872,8 +897,12 @@ class BuilderModule if (v0BuilderOpts.generatePhotonCandidates.value && v0BuilderOpts.moveTPCOnlyTracks.value) { // initialize only if needed, avoid unnecessary CCDB calls - mVDriftMgr.init(&ccdb->instance()); - mVDriftMgr.update(timestamp); + if constexpr (requires { bc.vdriftTgl(); }) { + mVDriftMgr.update(bc.vdriftTgl()); + } else { + mVDriftMgr.init(&ccdb->instance()); + mVDriftMgr.update(timestamp); + } } return true; @@ -1021,7 +1050,7 @@ class BuilderModule // handle TPC-only tracks properly (photon conversions) if (v0BuilderOpts.moveTPCOnlyTracks) { if (collision.has_bc()) { - mVDriftMgr.update(collision.template bc_as().timestamp()); + updateVDrift(collision); } if (isPosTPCOnly) { // Nota bene: positive is TPC-only -> this entire V0 merits treatment as photon candidate @@ -1498,7 +1527,7 @@ class BuilderModule continue; } if (v0BuilderOpts.generatePhotonCandidates && v0BuilderOpts.moveTPCOnlyTracks && collision.has_bc()) { - mVDriftMgr.update(collision.template bc_as().timestamp()); + updateVDrift(collision); } } auto const& posTrack = tracks.rawIteratorAt(v0.posTrackId); @@ -2757,6 +2786,16 @@ class BuilderModule return returnValue; } + //__________________________________________________ + // Overload for tasks sourcing every conditions object from CCDB columns; see initCCDB above. + template + void dataProcess(THistoRegistry& histos, TCollisions const& collisions, TMCCollisions const& mccollisions, TV0s const& v0s, TCascades const& cascades, TTrackedCascades const& trackedCascades, TTracks const& tracks, TBCs const& bcs, TMCParticles const& mcParticles, TProducts& products) + { + static_assert(requires(typename TBCs::iterator bc) { bc.vdriftTgl(); }, "dataProcess without a CCDB manager needs a BC table joined with aod::TpcCalibCCDBObjects"); + std::nullptr_t noCCDB{}; + dataProcess(noCCDB, histos, collisions, mccollisions, v0s, cascades, trackedCascades, tracks, bcs, mcParticles, products); + } + //__________________________________________________ template void dataProcess(TCCDB& ccdb, THistoRegistry& histos, TCollisions const& collisions, TMCCollisions const& mccollisions, TV0s const& v0s, TCascades const& cascades, TTrackedCascades const& trackedCascades, TTracks const& tracks, TBCs const& bcs, TMCParticles const& mcParticles, TProducts& products)