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 1/3] 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) From 48ac04848faab68e48bcadb4bdffe7e9f6425258 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 2/3] Improve skill to migrate to the new CCDB fetcher New Analysis Framework features now supported. --- .claude/commands/migrate-ccdb.md | 170 ++++++++++++++++++++++++++++++- 1 file changed, 169 insertions(+), 1 deletion(-) diff --git a/.claude/commands/migrate-ccdb.md b/.claude/commands/migrate-ccdb.md index 48d0f49c971..4f6bce9ab5d 100644 --- a/.claude/commands/migrate-ccdb.md +++ b/.claude/commands/migrate-ccdb.md @@ -10,9 +10,20 @@ The old approach uses `Service` and calls `ccdb->get // In namespace o2::aod (or a sub-namespace): DECLARE_SOA_CCDB_COLUMN(StructName, getterName, ConcreteType, "CCDB/Object/Path"); +// ... or, when the object needs fixing up after deserialisation, the _FULL form, whose +// trailing argument is the finaliser (see "Objects needing post-deserialisation fixup"): +DECLARE_SOA_CCDB_COLUMN_FULL(StructName, "fStructName", getterName, ConcreteType, "CCDB/Object/Path", + [](ConcreteType* o) { return fixUp(o); }); + DECLARE_SOA_TIMESTAMPED_TABLE(TableName, aod::Timestamps, o2::aod::timestamp::Timestamp, 1, "TABLEDESC", ns::StructName, ns::OtherColumn); +// ... or, when the object is constant across something coarser than a timestamp, the +// uniform form (see "Uniformity: how often the object can change"): +DECLARE_SOA_UNIFORM_TABLE(TableName, aod::Timestamps, o2::aod::timestamp::Timestamp, + aod::BCs, o2::aod::bc::RunNumber, 1, "TABLEDESC", + ns::StructName); + // In the task — basic usage: using MyBCs = soa::Join; void process(MyBCs const& bcs) { @@ -84,6 +95,17 @@ DECLARE_SOA_TIMESTAMPED_TABLE(MyTaskCCDBObjects, aod::Timestamps, o2::aod::times } // namespace o2::aod ``` +Before writing the declaration, settle three things per column — each has its own section +below, and getting them wrong is silent rather than loud: + +1. **Does the object need fixing up after deserialisation?** If so use `DECLARE_SOA_CCDB_COLUMN_FULL` + with a finaliser — see "Objects needing post-deserialisation fixup". +2. **How often can the object change?** Timestamp (the default) or run — see "Uniformity: how + often the object can change". Choose from the object's validity, not from how the old code + happened to fetch it. +3. **Is the path the same for every run?** If it varies by period, declare the mapping in the + query string instead of porting the run-range `if/else` — see "Paths that vary by run". + Rules for naming: - `StructName` / `getterName`: derive from the type name, e.g. `GRPMagField` / `grpMagField`, `MeanVertex` / `meanVertex` - Table name: `CCDBObjects`, e.g. `SkimmerDalitzEECCDBObjects` @@ -141,8 +163,154 @@ After making changes: - **`getRunDuration()` calls**: these use `BasicCCDBManager` statically and are unrelated to per-BC fetching — do not touch them. - **`ctpRateFetcher` / other helpers**: out of scope. - **Multiple tasks in one file**: tasks can share a single CCDB table declaration if they need the same objects; otherwise each task gets its own with a unique `_Desc_`. -- **Non-BC timestamps**: if the timestamp comes from something other than a BC (e.g. computed manually), the migration is non-trivial — flag it instead of forcing it. +- **Non-BC timestamps**: if the timestamp comes from something other than a BC, the migration is non-trivial — flag it instead of forcing it. This is the single most common blocker in practice. `Common/Tools/EventSelectionModule.h:243` computes `ts = sorTimestamp / 2 + eorTimestamp / 2` (mid-run, from `getRunDuration` / `AggregatedRunInfo`) and fetches `EventSelectionParams`, `ITS/Config/AlpideParam`, `TriggerAliases` and `ITS/Calib/TimeDeadMap` at it. A BC-keyed column fetches at each BC's own timestamp instead, so migrating these silently changes which object version is served whenever an object is revised mid-run. They need a run-keyed table before they can move. - **Global/init-time fetches** (e.g. `efficiencyGlobal.cxx` style): not migratable — the timestamped-table mechanism requires a row in a BC-keyed table. - **Magnetic-field side effects**: tasks that compute `d_bz` from a fetched `GRPMagField` and seed a propagator can keep that logic, just sourcing the object from `bc.grpMagField()` instead of `ccdb->getForTimeStamp(...)`. +## Lessons learned (established in-tree, with references) + +### Why this migration matters beyond tidiness + +The per-task path Configurable is a silent-divergence trap. `propagationService` and `propagationServiceV2` share the identical `ccdb.lutPath` Configurable (`Common/Tools/StandardCCDBLoader.h:45`, default `GLO/Param/MatLUT`), but config JSONs key overrides by *device name*. Every config in the tree carries a `propagation-service` block setting `GLO/Param/MatLUTInner` and no `propagation-service-v2` block, so V2 silently fell back to the full LUT — different material corrections, no warning. After migration the path is one option on the fetcher device, and two tasks disagreeing produces a warning (`ArrowSupport.cxx:641-666`) instead of silence. + +### Objects needing post-deserialisation fixup + +Some objects are not usable straight out of the ROOT streamer. `MatLayerCylSet` is a `FlatObject`: its internal pointers are unfixed and its voxel lookup unbuilt until `MatLayerCylSet::rectifyPtrFromFile()` runs. Use the `_FULL` form, which carries the finaliser (the plain `DECLARE_SOA_CCDB_COLUMN` passes an identity one): + +```cpp +DECLARE_SOA_CCDB_COLUMN_FULL(MatLUT, "fMatLUT", matLUT, o2::base::MatLayerCylSet, "GLO/Param/MatLUT", //! + [](o2::base::MatLayerCylSet* lut) { return o2::base::MatLayerCylSet::rectifyPtrFromFile(lut); }); +``` + +The finaliser must be the **last** macro argument (commas in a lambda body are absorbed by `__VA_ARGS__`), has signature `T* (*)(T*)`, and runs on the receiving device once per (re)deserialisation, before the object is ever handed out. Ownership contract: whatever it returns is what the column cache later `delete`s, so a finaliser returning a *different* instance must dispose of the one it was given. + +Do **not** put this fixup in the task. There is no `finaliseCCDB` hook on the analysis path (`adaptAnalysisTask` wires only `EndOfStream`, `AnalysisTask.h:610-619`; grep confirms zero uses of `finaliseCCDB` in O2Physics), and even if there were, an opt-in hook means a task that forgets it gets a silently broken object. + +### Uniformity: how often the object can change + +Every CCDB table declares a *uniformity column*: rows sharing its value resolve to the same +object, so the fetcher queries once per distinct value instead of once per row. +`DECLARE_SOA_TIMESTAMPED_TABLE` defaults it to the timestamp column, which is the +pre-existing behaviour — every distinct timestamp may yield a different object. + +Pick it from the object's real validity, and only then: + +| Object changes ... | Uniformity | Declare with | +| --- | --- | --- | +| within a run (calibrations, drift velocity) | timestamp (default) | `DECLARE_SOA_TIMESTAMPED_TABLE` | +| per run or per period (geometry, material, per-period calibrations) | `aod::BCs` / `aod::bc::RunNumber` | `DECLARE_SOA_UNIFORM_TABLE` | + +Worked examples in the tree: `aod::TpcCalibCCDBObjects` keeps the timestamp default because +the TPC drift velocity genuinely varies within a run; `aod::GeomCCDBObjects` and +`aod::TrackTunerCCDBObjects` are run-uniform. + +Two consequences worth knowing before choosing: + +- The uniformity column may live in a **different table** from the timestamp — the run number + is on `aod::BCs`, the timestamp on `aod::Timestamps`. Both are handed to the fetcher + automatically (the table's `generateSources()` merges their originals) and read positionally. +- Positional reading is only sound if the two sources are **row-aligned**. ASoA encodes no + type-level relation between tables that merely have equal row counts, so this cannot be a + `static_assert`; the fetcher compares the two column lengths and fatals on a mismatch. + Anything joinable with the BCs is fine. + +### Paths that vary by run: declare a mapping, not code + +A column's path may be a plain path, or a mapping from uniformity value to path: + +``` +"520259-529691=…/pp2023/pass4/vsPhi;559348-559387=…/ppRef/polarity_positive;fallback" +``` + +Ranges are inclusive; either bound may be omitted (`-hi=path`, `lo-=path`); entries are +separated by `;`; an entry without `=` is an explicit fallback. **A value matching no range +is fatal**, deliberately — silently substituting another period's calibration is the failure +mode this whole mechanism exists to prevent. A string with no `=` is a plain path, so +existing columns are unaffected. + +The mapping is *data*, carried in the schema metadata. That matters: the CCDB fetcher is a +separate device and must not depend on code from the task that declared the column, so a +resolver lambda would not do. It also means the run ranges stop being compiled in — the whole +mapping is replaceable at runtime through the `ccdb:fXxx` option. + +This replaces hand-written run-range tables. `TrackTuner::getPathInputFileAutomaticFromCCDB()` +is the model case: ~50 lines of `else if (lo <= runNumber && runNumber <= hi)` became the +declaration in `Common/DataModel/TrackTunerCCDBObjects.h`. When porting one, **derive the +mapping mechanically and diff it against the source** — first-match-wins must reproduce the +`if/else` order, which matters whenever ranges overlap (in TrackTuner, one PbPb range sits +inside a pp range and must stay *after* it). + +### Serving migrated and un-migrated callers from one module + +Shared modules must keep working for tasks that have not migrated. Detect the capability +rather than adding a configuration flag: + +```cpp +auto const& bc = collision.template bc_as(); +if constexpr (requires { bc.vdriftTgl(); }) { + mVDriftMgr.update(bc.vdriftTgl()); // column path +} else { + mVDriftMgr.update(bc.timestamp()); // legacy CCDB query +} +``` + +The discarded branch is not instantiated, so an un-migrated caller compiles exactly as before +and a migrated one never references the CCDB manager. `strangenessBuilderModule::updateVDrift` +uses this. Where a whole function parameter falls away, add an overload of different arity +that forwards (see "Shared module signatures") and put a `static_assert` with a readable +message on the ccdb-free one, so calling it with an unjoined BC table names the missing table +instead of failing somewhere inside the template. + +### Two path settings must never both be live + +After migration the column is the single source of truth for a path. If the task still has an +old `Configurable` for the same object, **fail loudly when both are set** rather +than silently preferring one — that divergence is exactly the bug this migration exists to +kill. `TrackPropagationModule::init` fatals when `trackTuner.pathInputFile` is non-empty while +the calibrations come from columns, naming the option to use instead (`ccdb:fTrackTunerDca`). + +Caveat: this test only works for Configurables whose default is empty. One with a non-empty +default cannot be distinguished from an unset one, so that hole stays open until the framework +can report whether an option was explicitly set. + +### Grouping columns into tables + +One table per **family of objects used together with similar validity intervals** — not one per consuming task. Geometry and material description (`GLO/Param/MatLUT`, and later `GLO/Config/GeometryAligned`, `GLO/Config/Geometry`, `/Calib/Align`; see `GRPGeomRequest` in `O2/Detectors/Base/src/GRPGeomHelper.cxx:44-60`) is one family with essentially static validity. The GRP family changes per run, and `GRPMagField` is requested per timeframe in O2 (`GRPGeomHelper.cxx:72`). Splitting on that boundary keeps a task from fetching a multi-hundred-MB LUT it never asked for. + +**Several timestamped tables can be joined onto the same BCs.** `soa::Join` works: the duplicated `aod::Timestamps` is deduplicated when `originals` is merged (`ASoA.h:172-186`), giving 4 originals, and every accessor resolves. Do not invent per-use-case tables to work around a limitation that does not exist. + +### Global state is not a lookup + +Migrating removes CCDB *queries*, not side effects. Two things stay: + +- `Propagator::initFieldFromGRP()` rebuilds or rescales a `MagneticField`, attaches it to `TGeoGlobalMagField::Instance()` and locks it (`O2/Detectors/Base/src/Propagator.cxx:107-149`). Keep it guarded on run change. +- `Propagator::Instance()->setMatLUT()` is a pointer store, so it is cheaper to redo unconditionally every timeframe — and doing so picks up a relocated column buffer for free instead of dangling. + +Everything else (mean vertex, run number) should become a direct read at the point of use, with no cached member and no `initCCDB()` helper. A cached pointer plus a "did the buffer move?" check is strictly worse than reading the column fresh. + +`Propagator` cannot itself become a column value: private constructor, deleted copy/move, singleton `Instance()` (`Propagator.h:157-201`). + +### Shared module signatures + +If a shared module takes a `StandardCCDBLoader`, change it to take the values it actually uses (`int runNumber`, `MeanVertexObject const*`) and keep a thin forwarding overload for un-migrated callers, so V1 tasks stay byte-identical. `TrackPropagationModule::fillTrackTables` does this — the two overloads differ in arity, so overload resolution is unambiguous. + +### What the migration does and does not buy + +The fetcher downloads once into a shm cache and the column stores `(handle, segment, size)` (`AnalysisCCDBHelpers.cxx:213-222`). What is shared is the **serialised blob**; each consumer still streams its own heap copy in the column getter. So expect fewer downloads, one configuration point and cross-device consistency — but not a per-device RSS reduction. For a `FlatObject` like the LUT, real memory sharing needs a zero-copy path (`FlatObject::setActualBufferAddress`) that does not exist yet. + +### Known gaps in the mechanism + +- **Run-dependent objects are not served correctly.** The analysis fetcher still hardcodes `.runNumber = 1, .runDependent = 0` for every column, even though `CCDBFetcherHelper.cxx:189-195` implements the run-dependent query paths. `GLO/Config/GRPECS` is marked "Run dependent !!!" in O2 and already has a column — verify before relying on it. Now that a run-uniform table gives the fetcher a run number per row, wiring this through is small and worth doing. +- **`getForRun` is not the same query.** `BasicCCDBManager::getForRun` resolves the run duration and queries at *mid-run* (`BasicCCDBManager.h:364-374`); a column queries at each BC's timestamp. Identical for objects with one version per run, divergent otherwise. +- **Row cardinality, not query count.** The uniformity column already collapses the *queries* to one per distinct value, but the table still carries one row per BC per column — a `FixedSizeList`, 24 B, rebuilt every timeframe. Collapsing the rows too needs a non-extension table plus lookup by value at the consumer, which does not exist yet. So a run-uniform table costs the same arrow memory as before; what it saves is the fetching. +- **Multi-run dataframes.** Skimmed datasets can span runs. Every existing consumer configures from `bcs.begin()` and applies it to the whole DF (`propagationServiceV2.cxx`, `StandardCCDBLoader.h:70-77`, `strangenessBuilderModule.h:850`), which is wrong for such a DF. Migrating preserves this bug unless it is fixed deliberately — do not claim the migration fixes it. + +### Practical gotchas + +- `DECLARE_SOA_CCDB_COLUMN` expands to code using `TClass` and `TBufferFile`, but `ASoA.h` only sees them forward-declared. A translation unit that includes the column header without otherwise pulling in `` and `` fails to compile. Include them if needed. +- A failed fetch is fatal, not silent: if `extractCCDBPayload` returns null the getter aborts naming the type, the path and the `ccdb:` option to check. A mistyped path therefore stops the job rather than dereferencing null. +- Do not add a `sources` member to a table's metadata struct. It makes the struct satisfy both `soa::with_sources` and `soa::with_sources_generator`, and `getInputMetadata` becomes ambiguous. +- Device options are matched by device *name*. Never look a task's own option up by a hardcoded name (`device.name == "propagation-service"` silently matched nothing in `propagation-service-v2`); take the running device from `initContext.services().get()`. Spell the type out rather than using `auto`, or the pre-existing `option.defaultValue.get()` becomes a dependent name and needs `template`. +- Verify with the *control*: when changing a shared header, compile an un-migrated consumer too. A new error appearing in both is yours; the same errors in both means you changed nothing for them. + $ARGUMENTS From 5358f70d6f1c1035cd1b9c4c053765e11fdae1e2 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 3/3] Improve propagationServiceV2 * Make sure all the CCDB related objects get retrieved via the new table mechanism. * No need anymore for a centralised CCDBLoader object * Get rid of all BasicCCDBManager instances --- Common/DataModel/GloCCDBObjects.h | 39 ++++++- Common/DataModel/TpcCCDBObjects.h | 43 ++++++++ Common/DataModel/TrackTunerCCDBObjects.h | 59 ++++++++++ Common/TableProducer/propagationServiceV2.cxx | 101 +++++++++--------- 4 files changed, 189 insertions(+), 53 deletions(-) create mode 100644 Common/DataModel/TpcCCDBObjects.h create mode 100644 Common/DataModel/TrackTunerCCDBObjects.h diff --git a/Common/DataModel/GloCCDBObjects.h b/Common/DataModel/GloCCDBObjects.h index f97b684b0a1..d7048d53ef2 100644 --- a/Common/DataModel/GloCCDBObjects.h +++ b/Common/DataModel/GloCCDBObjects.h @@ -33,9 +33,21 @@ /// `DECLARE_SOA_TIMESTAMPED_TABLE` with the relevant subset of columns from /// the `o2::aod::ccdbGlo` namespace rather than joining `aod::GloCCDBObjects`. /// -/// Note: MatLayerCylSet is intentionally omitted — it requires -/// `MatLayerCylSet::rectifyPtrFromFile()` after deserialisation, which the -/// CCDB column mechanism does not perform. +/// The material LUT lives in `aod::GeomCCDBObjects` rather than here: it belongs to +/// the geometry/material family, whose validity is essentially static, and keeping it +/// out means joining `aod::GloCCDBObjects` does not drag in a multi-hundred-MB object +/// nobody asked for. Join whichever tables you need — the duplicated `aod::Timestamps` +/// is deduplicated: +/// \code +/// using BCsWithLUT = soa::Join; +/// // rectifyPtrFromFile() is applied by the column's finaliser, so the +/// // object handed back is ready to use; the task only has to (re)install it. +/// auto* lut = &bcs.begin().matLUT(); +/// if (lut != mLastLUT) { +/// o2::base::Propagator::Instance()->setMatLUT(lut); +/// mLastLUT = lut; +/// } +/// \endcode #ifndef COMMON_DATAMODEL_GLOCCDBOBJECTS_H_ #define COMMON_DATAMODEL_GLOCCDBOBJECTS_H_ @@ -44,6 +56,7 @@ #include #include #include +#include #include #include @@ -55,11 +68,31 @@ DECLARE_SOA_CCDB_COLUMN(GRPMagField, grpMagField, o2::parameters::GRPMagField, " DECLARE_SOA_CCDB_COLUMN(MeanVertex, meanVertex, o2::dataformats::MeanVertexObject, "GLO/Calib/MeanVertex"); //! DECLARE_SOA_CCDB_COLUMN(GRPECSObject, grpECS, o2::parameters::GRPECSObject, "GLO/Config/GRPECS"); //! DECLARE_SOA_CCDB_COLUMN(GRPLHCIFData, grpLHCIF, o2::parameters::GRPLHCIFData, "GLO/Config/GRPLHCIF"); //! + +/// The material LUT is a FlatObject: straight out of the ROOT streamer its internal +/// pointers are unfixed and its voxel lookup is unbuilt, so it is finalised with +/// MatLayerCylSet::rectifyPtrFromFile() before ever being handed to a task. +DECLARE_SOA_CCDB_COLUMN_FULL(MatLUT, "fMatLUT", matLUT, o2::base::MatLayerCylSet, "GLO/Param/MatLUT", //! + [](o2::base::MatLayerCylSet* lut) { return o2::base::MatLayerCylSet::rectifyPtrFromFile(lut); }); } // namespace ccdbGlo /// Full table — join with aod::BCsWithTimestamps to obtain all four objects. DECLARE_SOA_TIMESTAMPED_TABLE(GloCCDBObjects, aod::Timestamps, o2::aod::timestamp::Timestamp, 1, "GLOCCDBOBJ", //! ccdbGlo::GRPMagField, ccdbGlo::MeanVertex, ccdbGlo::GRPECSObject, ccdbGlo::GRPLHCIFData); + +/// Geometry and material description: objects which describe where the detector material +/// is, and which share an essentially static interval of validity. Kept apart from the GRP +/// family above, which changes per run (and, for GRPMagField, per timeframe). +/// The aligned/ideal geometry and the per-detector alignment objects belong here too when +/// they get columns; see GRPGeomRequest in O2 (GLO/Config/GeometryAligned, GLO/Config/Geometry, +/// /Calib/Align) for the family. +/// Join it alongside aod::GloCCDBObjects when a task needs both — the duplicated +/// aod::Timestamps is deduplicated when the joined table's originals are merged. +/// Uniform in the run number: the geometry/material description does not change within a +/// run, so the fetcher queries once per distinct run rather than once per BC. +DECLARE_SOA_UNIFORM_TABLE(GeomCCDBObjects, aod::Timestamps, o2::aod::timestamp::Timestamp, + aod::BCs, o2::aod::bc::RunNumber, 1, "GEOMCCDBOBJ", //! + ccdbGlo::MatLUT); } // namespace o2::aod #endif // COMMON_DATAMODEL_GLOCCDBOBJECTS_H_ diff --git a/Common/DataModel/TpcCCDBObjects.h b/Common/DataModel/TpcCCDBObjects.h new file mode 100644 index 00000000000..19b455e0234 --- /dev/null +++ b/Common/DataModel/TpcCCDBObjects.h @@ -0,0 +1,43 @@ +// Copyright 2019-2026 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 TpcCCDBObjects.h +/// \brief Declarative CCDB columns for TPC calibration objects. +/// +/// Unlike the geometry/material family in GloCCDBObjects.h, the drift velocity +/// genuinely varies within a run, so the table keeps the default uniformity — one +/// object per distinct timestamp — rather than collapsing per run. +/// +/// Usage: +/// \code +/// using BCsWithVDrift = soa::Join; +/// vdriftManager.update(bc.vdriftTgl()); +/// \endcode + +#ifndef COMMON_DATAMODEL_TPCCCDBOBJECTS_H_ +#define COMMON_DATAMODEL_TPCCCDBOBJECTS_H_ + +#include +#include +#include + +namespace o2::aod +{ +namespace ccdbTpc +{ +DECLARE_SOA_CCDB_COLUMN(VDriftTgl, vdriftTgl, o2::tpc::VDriftCorrFact, "TPC/Calib/VDriftTgl"); //! +} // namespace ccdbTpc + +DECLARE_SOA_TIMESTAMPED_TABLE(TpcCalibCCDBObjects, aod::Timestamps, o2::aod::timestamp::Timestamp, 1, "TPCCALIBCCDB", //! + ccdbTpc::VDriftTgl); +} // namespace o2::aod + +#endif // COMMON_DATAMODEL_TPCCCDBOBJECTS_H_ diff --git a/Common/DataModel/TrackTunerCCDBObjects.h b/Common/DataModel/TrackTunerCCDBObjects.h new file mode 100644 index 00000000000..2e4817efff3 --- /dev/null +++ b/Common/DataModel/TrackTunerCCDBObjects.h @@ -0,0 +1,59 @@ +// Copyright 2019-2026 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 TrackTunerCCDBObjects.h +/// \brief Declarative CCDB columns for the TrackTuner DCA / Q-over-pt calibrations. +/// +/// The DCA calibration is published under a different path per data-taking period, so +/// the column declares a uniformity-value-to-path mapping rather than a single path: +/// the fetcher picks the entry whose run range contains the row's run number. This +/// replaces TrackTuner::getPathInputFileAutomaticFromCCDB(), whose run ranges these are. +/// A run matching no range is a fatal error, as it was before — there is deliberately no +/// fallback entry, since silently using another period's calibration is worse than stopping. +/// +/// The ranges are the column's *default*; the whole mapping can be replaced at runtime +/// through the "ccdb:fTrackTunerDca" option, so adding a period need not be a code change. + +#ifndef COMMON_DATAMODEL_TRACKTUNERCCDBOBJECTS_H_ +#define COMMON_DATAMODEL_TRACKTUNERCCDBOBJECTS_H_ + +#include +#include + +#include + +namespace o2::aod +{ +namespace ccdbTrackTuner +{ +DECLARE_SOA_CCDB_COLUMN(TrackTunerDca, trackTunerDca, TList, //! + "520259-529691=Users/m/mfaggin/test/inputsTrackTuner/pp2023/pass4/vsPhi;" + "534998-543113=Users/m/mfaggin/test/inputsTrackTuner/pp2023/pass4/vsPhi;" + "529397-529418=Users/m/mfaggin/test/inputsTrackTuner/PbPb2023/apass4/vsPhi;" + "543437-545367=Users/m/mfaggin/test/inputsTrackTuner/PbPb2023/apass4/vsPhi;" + "549559-558807=Users/m/mfaggin/test/inputsTrackTuner/pp2024/pass1_minBias/vsPhi;" + "564356-564445=Users/m/mfaggin/test/inputsTrackTuner/OO/LHC25ae;" + "564468-564472=Users/m/mfaggin/test/inputsTrackTuner/OO/LHC25af;" + "559348-559387=Users/m/mfaggin/test/inputsTrackTuner/pp2024/ppRef/polarity_positive;" + "559408-559456=Users/m/mfaggin/test/inputsTrackTuner/pp2024/ppRef/polarity_negative"); + +DECLARE_SOA_CCDB_COLUMN(TrackTunerQOverPt, trackTunerQOverPt, TList, //! + "Users/h/hsharma/qOverPtGraphs"); +} // namespace ccdbTrackTuner + +/// Uniform in the run number: one calibration per data-taking period, so the fetcher +/// resolves the path and queries once per distinct run rather than once per BC. +DECLARE_SOA_UNIFORM_TABLE(TrackTunerCCDBObjects, aod::Timestamps, o2::aod::timestamp::Timestamp, + aod::BCs, o2::aod::bc::RunNumber, 1, "TRKTUNERCCDB", //! + ccdbTrackTuner::TrackTunerDca, ccdbTrackTuner::TrackTunerQOverPt); +} // namespace o2::aod + +#endif // COMMON_DATAMODEL_TRACKTUNERCCDBOBJECTS_H_ diff --git a/Common/TableProducer/propagationServiceV2.cxx b/Common/TableProducer/propagationServiceV2.cxx index 14f47a4c96d..7affd35c5e5 100644 --- a/Common/TableProducer/propagationServiceV2.cxx +++ b/Common/TableProducer/propagationServiceV2.cxx @@ -10,7 +10,7 @@ // or submit itself to any jurisdiction. /// \file propagationServiceV2.cxx -/// \brief V2: GRPMagField and MeanVertexObject sourced from aod::GloCCDBObjects declarative CCDB table. +/// \brief V2: GRPMagField, MeanVertexObject and the material LUT sourced from declarative CCDB tables. /// \author ALICE //=============================================================== @@ -28,12 +28,11 @@ #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/GloCCDBObjects.h" #include "Common/DataModel/PIDResponseTPC.h" -#include "Common/Tools/StandardCCDBLoader.h" +#include "Common/DataModel/TpcCCDBObjects.h" +#include "Common/DataModel/TrackTunerCCDBObjects.h" #include "Common/Tools/TrackPropagationModule.h" #include "Common/Tools/TrackTuner.h" -#include -#include #include #include #include @@ -66,15 +65,27 @@ using TracksWithExtra = soa::Join; using TracksExtraWithPID = soa::Join; struct propagationServiceV2 { - // Service kept for MatLUT (rectifyPtrFromFile) and - // strangenessBuilderModule (V-drift via ccdb->instance()). - // GRPMagField and MeanVertex are sourced from CCDB columns instead. - o2::framework::Configurable ccdburl{"ccdburl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Service ccdb; - - // propagation stuff — ccdbLoader used only for lut + mMeanVtx (set from column) + runNumber - o2::common::StandardCCDBLoaderConfigurables standardCCDBLoaderConfigurables; - o2::common::StandardCCDBLoader ccdbLoader; + // No CCDB client of any kind: GRPMagField, MeanVertex, the material LUT and the TPC + // drift correction all arrive as declarative CCDB columns. + // NB: TrackTuner still holds its own CcdbApi for the DCA calibration files when + // useTrackTuner is enabled; its path is derived from the run number, which a CCDB + // column cannot express today. + + // Keep every CCDB path user-overridable, as ccdb.lutPath / ccdb.grpmagPath / + // ccdb.mVtxPath were before the migration. The option name and default are derived + // from the column itself ("ccdb:fMatLUT" and friends), so they cannot drift from the + // declaration the way the old per-task Configurables did. Declaring them is enough — + // the accessors stay bc.matLUT() / bc.grpMagField() / bc.meanVertex(). + o2::framework::ConfigurableCCDBPath matLUTPath; + o2::framework::ConfigurableCCDBPath grpMagFieldPath; + o2::framework::ConfigurableCCDBPath meanVertexPath; + + // Everything this task needs is read straight off the CCDB columns at the point of + // use; no StandardCCDBLoader, and no CCDB query of its own. The single piece of + // retained state is the run number, needed only to avoid re-installing the magnetic + // field: that one is not a lookup but global state in the Propagator / + // TGeoGlobalMagField singletons, and installing it rebuilds or rescales the field map. + int mRunNumber = -1; // boilerplate: strangeness builder stuff o2::pwglf::strangenessbuilder::products products; @@ -93,46 +104,32 @@ struct propagationServiceV2 { o2::common::TrackPropagationConfigurables trackPropagationConfigurables; o2::common::TrackPropagationModule trackPropagation; - using BCsWithCCDB = soa::Join; + using BCsWithCCDB = soa::Join; // registry HistogramRegistry histos{"histos"}; void init(o2::framework::InitContext& initContext) { - // Only needed for MatLUT fetch and strangenessBuilderModule V-drift - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); - ccdb->setURL(ccdburl.value); - // task-specific - trackPropagation.init(trackPropagationConfigurables, trackTunerObj, histos, initContext); + trackPropagation.init(trackPropagationConfigurables, trackTunerObj, histos, initContext, /*calibFromCCDBColumns=*/true); strangenessBuilderModule.init(baseOpts, v0BuilderOpts, cascadeBuilderOpts, preSelectOpts, eventSelectOpts, histos, initContext); } - // Load MatLUT once (needs rectifyPtrFromFile, kept manual), set B-field and mean vertex - // once per run from GRPMagField/MeanVertex CCDB columns. + /// Install into the Propagator the two things which are global state rather than + /// values: the magnetic field and the material LUT. template - void initCCDB(TBC const& bc0) + void initPropagator(TBC const& bc0) { - if (ccdbLoader.runNumber != bc0.runNumber()) { + if (mRunNumber != bc0.runNumber()) { LOG(info) << "Setting B-field to current " << bc0.grpMagField().getL3Current() << " A for run " << bc0.runNumber() << " from GRPMagField CCDB column"; o2::base::Propagator::initFieldFromGRP(&bc0.grpMagField()); - ccdbLoader.mMeanVtx = &bc0.meanVertex(); - ccdbLoader.runNumber = bc0.runNumber(); - } else { - // Verify the CCDB column buffer has not been replaced mid-run. - // The deserialised pointer must be stable for the lifetime of a run. - if (&bc0.meanVertex() != ccdbLoader.mMeanVtx) { - LOG(fatal) << "MeanVertex CCDB column pointer changed within run " << bc0.runNumber() << " — unexpected buffer replacement"; - } - } - if (!ccdbLoader.lut) { - LOG(info) << "Loading material look-up table for run: " << bc0.runNumber(); - ccdbLoader.lut = o2::base::MatLayerCylSet::rectifyPtrFromFile( - ccdb->template getForRun(standardCCDBLoaderConfigurables.lutPath.value, bc0.runNumber())); - o2::base::Propagator::Instance()->setMatLUT(ccdbLoader.lut); + mRunNumber = bc0.runNumber(); } + // A pointer store, so it costs nothing to redo every timeframe — and doing so + // means a relocated column buffer is picked up for free instead of dangling. + // The column's finaliser has already run MatLayerCylSet::rectifyPtrFromFile. + o2::base::Propagator::Instance()->setMatLUT(&bc0.matLUT()); } void processRealData(soa::Join const& collisions, aod::V0s const& v0s, aod::Cascades const& cascades, aod::TrackedCascades const& trackedCascades, FullTracksExtIU const& tracks, BCsWithCCDB const& bcs) @@ -140,9 +137,10 @@ struct propagationServiceV2 { if (bcs.size() == 0) { return; } - initCCDB(bcs.begin()); - trackPropagation.fillTrackTables(trackPropagationConfigurables, trackTunerObj, ccdbLoader, collisions, tracks, trackPropagationProducts, histos); - strangenessBuilderModule.dataProcess(ccdb, histos, collisions, static_cast(nullptr), v0s, cascades, trackedCascades, tracks, bcs, static_cast(nullptr), products); + auto bc0 = bcs.begin(); + initPropagator(bc0); + trackPropagation.fillTrackTables(trackPropagationConfigurables, trackTunerObj, bc0.runNumber(), &bc0.meanVertex(), &bc0.trackTunerDca(), &bc0.trackTunerQOverPt(), collisions, tracks, trackPropagationProducts, histos); + strangenessBuilderModule.dataProcess(histos, collisions, static_cast(nullptr), v0s, cascades, trackedCascades, tracks, bcs, static_cast(nullptr), products); } void processMonteCarlo(soa::Join const& collisions, aod::McCollisions const& mccollisions, aod::V0s const& v0s, aod::Cascades const& cascades, aod::TrackedCascades const& trackedCascades, FullTracksExtLabeledIU const& tracks, BCsWithCCDB const& bcs, aod::McParticles const& mcParticles) @@ -150,9 +148,10 @@ struct propagationServiceV2 { if (bcs.size() == 0) { return; } - initCCDB(bcs.begin()); - trackPropagation.fillTrackTables(trackPropagationConfigurables, trackTunerObj, ccdbLoader, collisions, tracks, trackPropagationProducts, histos); - strangenessBuilderModule.dataProcess(ccdb, histos, collisions, mccollisions, v0s, cascades, trackedCascades, tracks, bcs, mcParticles, products); + auto bc0 = bcs.begin(); + initPropagator(bc0); + trackPropagation.fillTrackTables(trackPropagationConfigurables, trackTunerObj, bc0.runNumber(), &bc0.meanVertex(), &bc0.trackTunerDca(), &bc0.trackTunerQOverPt(), collisions, tracks, trackPropagationProducts, histos); + strangenessBuilderModule.dataProcess(histos, collisions, mccollisions, v0s, cascades, trackedCascades, tracks, bcs, mcParticles, products); } void processRealDataWithPID(soa::Join const& collisions, aod::V0s const& v0s, aod::Cascades const& cascades, aod::TrackedCascades const& trackedCascades, FullTracksExtIUWithPID const& tracks, BCsWithCCDB const& bcs) @@ -160,9 +159,10 @@ struct propagationServiceV2 { if (bcs.size() == 0) { return; } - initCCDB(bcs.begin()); - trackPropagation.fillTrackTables(trackPropagationConfigurables, trackTunerObj, ccdbLoader, collisions, tracks, trackPropagationProducts, histos); - strangenessBuilderModule.dataProcess(ccdb, histos, collisions, static_cast(nullptr), v0s, cascades, trackedCascades, tracks, bcs, static_cast(nullptr), products); + auto bc0 = bcs.begin(); + initPropagator(bc0); + trackPropagation.fillTrackTables(trackPropagationConfigurables, trackTunerObj, bc0.runNumber(), &bc0.meanVertex(), &bc0.trackTunerDca(), &bc0.trackTunerQOverPt(), collisions, tracks, trackPropagationProducts, histos); + strangenessBuilderModule.dataProcess(histos, collisions, static_cast(nullptr), v0s, cascades, trackedCascades, tracks, bcs, static_cast(nullptr), products); } void processMonteCarloWithPID(soa::Join const& collisions, aod::McCollisions const& mccollisions, aod::V0s const& v0s, aod::Cascades const& cascades, aod::TrackedCascades const& trackedCascades, FullTracksExtLabeledIUWithPID const& tracks, BCsWithCCDB const& bcs, aod::McParticles const& mcParticles) @@ -170,9 +170,10 @@ struct propagationServiceV2 { if (bcs.size() == 0) { return; } - initCCDB(bcs.begin()); - trackPropagation.fillTrackTables(trackPropagationConfigurables, trackTunerObj, ccdbLoader, collisions, tracks, trackPropagationProducts, histos); - strangenessBuilderModule.dataProcess(ccdb, histos, collisions, mccollisions, v0s, cascades, trackedCascades, tracks, bcs, mcParticles, products); + auto bc0 = bcs.begin(); + initPropagator(bc0); + trackPropagation.fillTrackTables(trackPropagationConfigurables, trackTunerObj, bc0.runNumber(), &bc0.meanVertex(), &bc0.trackTunerDca(), &bc0.trackTunerQOverPt(), collisions, tracks, trackPropagationProducts, histos); + strangenessBuilderModule.dataProcess(histos, collisions, mccollisions, v0s, cascades, trackedCascades, tracks, bcs, mcParticles, products); } PROCESS_SWITCH(propagationServiceV2, processRealData, "process real data", true);