From bbb5e943ff398d750226a6dd7215a8726e91f245 Mon Sep 17 00:00:00 2001 From: Matthias Kleiner Date: Tue, 5 Aug 2025 15:53:47 +0200 Subject: [PATCH 1/2] TPC: Add scaling of VDrift with T/P - scaling of the VDrift is automatically enabled - the reference T/P is extracted for VDrift objects without stored T/P if firstTime and lastTime of VDrift object is in the range +-20 minutes of available temperature and pressure - scaling with T/P can be disabled by setting 'TPCGasParam.Temperature=0;TPCGasParam.Pressure=0' - reference T/P is extracted automatically for online created VDrift with ITSTgl and laser method - add storing of temperature. pressure and used VDrift in timeseries --- .../TPC/include/DataFormatsTPC/LtrCalibData.h | 3 +- .../include/DataFormatsTPC/VDriftCorrFact.h | 29 ++++--- .../IntegratedClusterCalibrator.h | 5 +- Detectors/GlobalTracking/src/MatchTPCITS.cxx | 2 +- .../include/TPCCalibration/CalibLaserTracks.h | 11 ++- .../PressureTemperatureHelper.h | 20 +++++ .../TPCCalibration/TPCVDriftTglCalibration.h | 15 +++- .../include/TPCCalibration/VDriftHelper.h | 12 ++- .../TPC/calibration/src/CalibLaserTracks.cxx | 27 +++++- .../src/PressureTemperatureHelper.cxx | 77 +++++++++++++++- .../src/TPCVDriftTglCalibration.cxx | 3 +- .../TPC/calibration/src/VDriftHelper.cxx | 87 +++++++++++++++++-- .../TPCWorkflow/CalibLaserTracksSpec.h | 6 +- .../TPC/workflow/src/TPCTimeSeriesSpec.cxx | 15 ++-- .../workflow/src/TPCVDriftTglCalibSpec.cxx | 21 ++++- 15 files changed, 288 insertions(+), 45 deletions(-) diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/LtrCalibData.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/LtrCalibData.h index e410cd00dd3f6..e5e9b41229d50 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/LtrCalibData.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/LtrCalibData.h @@ -42,6 +42,7 @@ struct LtrCalibData { std::vector matchedLtrIDs; ///< matched laser track IDs std::vector nTrackTF; ///< number of laser tracks per TF std::vector dEdx; ///< dE/dx of each track + float tp{0.f}; ///< temperature over pressure ratio bool isValid() const { @@ -138,7 +139,7 @@ struct LtrCalibData { dEdx.clear(); } - ClassDefNV(LtrCalibData, 4); + ClassDefNV(LtrCalibData, 5); }; } // namespace o2::tpc diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/VDriftCorrFact.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/VDriftCorrFact.h index 03ad9755fedae..4cee3ab1ad50b 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/VDriftCorrFact.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/VDriftCorrFact.h @@ -26,14 +26,15 @@ namespace o2::tpc { struct VDriftCorrFact { - long firstTime{}; ///< first time stamp of processed TFs - long lastTime{}; ///< last time stamp of processed TFs - long creationTime{}; ///< time of creation - float corrFact{1.0}; ///< drift velocity correction factor (multiplicative) - float corrFactErr{0.0}; ///< stat error of correction factor - float refVDrift{0.}; ///< reference vdrift for which factor was extracted + long firstTime{}; ///< first time stamp of processed TFs + long lastTime{}; ///< last time stamp of processed TFs + long creationTime{}; ///< time of creation + float corrFact{1.0}; ///< drift velocity correction factor (multiplicative) + float corrFactErr{0.0}; ///< stat error of correction factor + float refVDrift{0.}; ///< reference vdrift for which factor was extracted float refTimeOffset{0.}; ///< additive time offset reference (\mus) float timeOffsetCorr{0.}; ///< additive time offset correction (\mus) + float refTP{0.}; ///< reference temperature / pressure for which refVDrift was extracted float getVDrift() const { return refVDrift * corrFact; } float getVDriftError() const { return refVDrift * corrFactErr; } @@ -41,12 +42,20 @@ struct VDriftCorrFact { float getTimeOffset() const { return refTimeOffset + timeOffsetCorr; } // renormalize VDrift reference and correction either to provided new reference (if >0) or to correction 1 wrt current reference - void normalize(float newVRef = 0.f) + void normalize(float newVRef = 0.f, float tp = 0.f) { + float normVDrift = newVRef; if (newVRef == 0.f) { - newVRef = refVDrift * corrFact; + normVDrift = refVDrift * corrFact; + if ((tp == 0) || (refTP == 0)) { + newVRef = normVDrift; // no T/P scaling applied + } else { + // linear scaling based on relative change of T/P + newVRef = normVDrift * (1 + (tp - refTP) / refTP); + refTP = tp; // update reference T/P + } } - float fact = refVDrift / newVRef; + float fact = refVDrift / normVDrift; refVDrift = newVRef; corrFactErr *= fact; corrFact *= fact; @@ -66,7 +75,7 @@ struct VDriftCorrFact { } } - ClassDefNV(VDriftCorrFact, 2); + ClassDefNV(VDriftCorrFact, 3); }; } // namespace o2::tpc diff --git a/Detectors/Calibration/include/DetectorsCalibration/IntegratedClusterCalibrator.h b/Detectors/Calibration/include/DetectorsCalibration/IntegratedClusterCalibrator.h index 8a6996c35f2b3..9720142d391b1 100644 --- a/Detectors/Calibration/include/DetectorsCalibration/IntegratedClusterCalibrator.h +++ b/Detectors/Calibration/include/DetectorsCalibration/IntegratedClusterCalibrator.h @@ -331,6 +331,9 @@ struct TimeSeriesdEdx { }; struct TimeSeriesITSTPC { + float mVDrift = 0; ///< drift velocity in cm/us + float mPressure = 0; ///< pressure + float mTemperature = 0; ///< temperature TimeSeries mTSTPC; ///< TPC standalone DCAs TimeSeries mTSITSTPC; ///< ITS-TPC standalone DCAs ITSTPC_Matching mITSTPCAll; ///< ITS-TPC matching efficiency for ITS standalone + afterburner @@ -499,7 +502,7 @@ struct TimeSeriesITSTPC { nVertexContributors_Quantiles.resize(nTotalQ); } - ClassDefNV(TimeSeriesITSTPC, 5); + ClassDefNV(TimeSeriesITSTPC, 6); }; } // end namespace tpc diff --git a/Detectors/GlobalTracking/src/MatchTPCITS.cxx b/Detectors/GlobalTracking/src/MatchTPCITS.cxx index c8c9dda6a4025..e16031f641829 100644 --- a/Detectors/GlobalTracking/src/MatchTPCITS.cxx +++ b/Detectors/GlobalTracking/src/MatchTPCITS.cxx @@ -101,7 +101,7 @@ void MatchTPCITS::run(const o2::globaltracking::RecoContainer& inp, break; } if (mVDriftCalibOn) { // in the beginning of the output vector we send the full and reference VDrift used for this TF - calib.emplace_back(mTPCVDrift, mTPCDrift.refVDrift, -999.); + calib.emplace_back(mTPCVDrift, mTPCDrift.refVDrift, mTPCDrift.refTP); calib.emplace_back(mTPCDriftTimeOffset, mTPCDrift.refTimeOffset, -999.); } diff --git a/Detectors/TPC/calibration/include/TPCCalibration/CalibLaserTracks.h b/Detectors/TPC/calibration/include/TPCCalibration/CalibLaserTracks.h index 15c9a8648a796..cecf3ed4b8dca 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/CalibLaserTracks.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/CalibLaserTracks.h @@ -25,7 +25,6 @@ #include #include -#include "CommonConstants/MathConstants.h" #include "CommonUtils/TreeStreamRedirector.h" #include "DataFormatsTPC/TrackTPC.h" #include "DataFormatsTPC/LaserTrack.h" @@ -74,10 +73,12 @@ class CalibLaserTracks ~CalibLaserTracks() = default; /// process all tracks of one TF - void fill(const gsl::span tracks); + /// \param tp ratio of temperature over pressure + void fill(const gsl::span tracks, float tp = 0); /// process all tracks of one TF - void fill(std::vector const& tracks); + /// \param tp ratio of temperature over pressure + void fill(std::vector const& tracks, float tp = 0); /// process single track void processTrack(const TrackTPC& track); @@ -163,6 +164,8 @@ class CalibLaserTracks float mDriftV{0}; ///< drift velocity used during reconstruction float mTOffsetMUS{0}; ///< time offset in \mus to impose float mZbinWidth{0}; ///< width of a bin in us + float mAvgTP{0}; ///< ratio of average temperature over pressure + float mAvgDriftV{0}; ///< average drift velocity used for the laser track calibration uint64_t mTFstart{0}; ///< start time of processed time frames uint64_t mTFend{0}; ///< end time of processed time frames LtrCalibData mCalibDataTF{}; ///< calibration data for single TF (debugging) @@ -184,7 +187,7 @@ class CalibLaserTracks /// perform fits on the matched z-position pairs to extract the drift velocity correction factor and trigger offset void fillCalibData(LtrCalibData& calibData, const std::vector& pairsA, const std::vector& pairsC); - ClassDefNV(CalibLaserTracks, 1); + ClassDefNV(CalibLaserTracks, 2); }; } // namespace o2::tpc diff --git a/Detectors/TPC/calibration/include/TPCCalibration/PressureTemperatureHelper.h b/Detectors/TPC/calibration/include/TPCCalibration/PressureTemperatureHelper.h index 671c2efb78a8f..8317fc6bc68d8 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/PressureTemperatureHelper.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/PressureTemperatureHelper.h @@ -63,15 +63,35 @@ class PressureTemperatureHelper /// get pressure for given time stamp in ms float getPressure(const ULong64_t timestamp) const { return interpolate(mPressure.second, mPressure.first, timestamp); } + /// manually set the pressure + void setPressure(const std::pair, std::vector>& pressure) { mPressure = pressure; } + + /// manually set the temperature + void setTemperature(const std::pair, std::vector>& temperatureA, const std::pair, std::vector>& temperatureC) + { + mTemperatureA = temperatureA; + mTemperatureC = temperatureC; + } + /// get temperature for given time stamp in ms dataformats::Pair getTemperature(const ULong64_t timestamp) const { return dataformats::Pair{interpolate(mTemperatureA.second, mTemperatureA.first, timestamp), interpolate(mTemperatureC.second, mTemperatureC.first, timestamp)}; } + /// get mean temperature over A and C side + float getMeanTemperature(const ULong64_t timestamp) const; + + // get ratio of temperature over pressure for given time stamp + float getTP(int64_t ts) const; + static constexpr o2::header::DataDescription getDataDescriptionPressure() { return o2::header::DataDescription{"pressure"}; } static constexpr o2::header::DataDescription getDataDescriptionTemperature() { return o2::header::DataDescription{"temperature"}; } + /// get minimum and maximum time stamps of the pressure and temperature data + std::pair getMinMaxTime() const; + protected: static void addInput(std::vector& inputs, o2::framework::InputSpec&& isp); static void addOutput(std::vector& outputs, o2::framework::OutputSpec&& osp); + static constexpr float toKelvin(float celsius) { return celsius + 273.15f; } // convert Celsius to Kelvin std::pair, std::vector> mPressure; ///< pressure values for both measurements std::pair, std::vector> mTemperatureA; ///< temperature values A-side diff --git a/Detectors/TPC/calibration/include/TPCCalibration/TPCVDriftTglCalibration.h b/Detectors/TPC/calibration/include/TPCCalibration/TPCVDriftTglCalibration.h index c4028f727983f..2b0aef8820acc 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/TPCVDriftTglCalibration.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/TPCVDriftTglCalibration.h @@ -32,6 +32,7 @@ struct TPCVDTglContainer { double driftVFullMean = 0.; static float tOffsetRef; static float driftVRef; + float tp = 0; TPCVDTglContainer(int ntgl, float tglMax, int ndtgl, float dtglMax) { @@ -42,9 +43,12 @@ struct TPCVDTglContainer { { histo = std::make_unique(*(src.histo.get())); entries = src.entries; + tp = src.tp; + driftVFullMean = src.driftVFullMean; } - void fill(const gsl::span> data) + /// \param tp ratio of temperature over pressure + void fill(const gsl::span> data, float currentTemperaturePressure = 0) { if (data.size() < 3) { // first 2 entres always contains the {full and reference VDrift} and {full and reference DriftTimeOffset} used for the TF return; @@ -59,12 +63,14 @@ struct TPCVDTglContainer { } // float vfull = data[0].first, vref = data[0].second; + const float temperaturePressure = (data[0].third == 0) ? currentTemperaturePressure : data[0].third; if (driftVRef == 0.f) { driftVRef = vref; } else if (driftVRef != vref) { LOGP(warn, "data with VDriftRef={} were received while initially was set to {}, keep old one", vref, driftVRef); } driftVFullMean = (driftVFullMean * nTFProc + vfull) / (nTFProc + 1); + tp = (tp * nTFProc + temperaturePressure) / (nTFProc + 1); if (tOffsetRef == 0.f) { tOffsetRef = data[1].first; // assign 1st full toffset as a reference } @@ -73,6 +79,11 @@ struct TPCVDTglContainer { void merge(const TPCVDTglContainer* other) { + const int norm = nTFProc + other->nTFProc; + if (norm > 0) { + tp = (tp * nTFProc + other->tp * other->nTFProc) / norm; + driftVFullMean = (driftVFullMean * nTFProc + other->driftVFullMean * other->nTFProc) / norm; + } entries += other->entries; histo->add(*(other->histo)); LOGP(debug, "Old entries:{} New entries:{} oldSum: {} newSum: {}", other->entries, entries, other->histo->getSum(), histo->getSum()); @@ -82,7 +93,7 @@ struct TPCVDTglContainer { { LOG(info) << "Nentries = " << entries; } - ClassDefNV(TPCVDTglContainer, 1); + ClassDefNV(TPCVDTglContainer, 2); }; class TPCVDriftTglCalibration : public o2::calibration::TimeSlotCalibration diff --git a/Detectors/TPC/calibration/include/TPCCalibration/VDriftHelper.h b/Detectors/TPC/calibration/include/TPCCalibration/VDriftHelper.h index a8af81fc65e8b..d600df201f985 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/VDriftHelper.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/VDriftHelper.h @@ -18,6 +18,7 @@ #include "GPUCommonRtypes.h" #include "DataFormatsTPC/VDriftCorrFact.h" +#include "TPCCalibration/PressureTemperatureHelper.h" #include #include #include @@ -56,6 +57,7 @@ class VDriftHelper Source getSource() const { return mSource; } static std::string_view getSourceName(Source s) { return SourceNames[s]; } std::string_view getSourceName() const { return SourceNames[mSource]; } + const auto& getPTHelper() const { return mPTHelper; } bool accountCCDBInputs(const o2::framework::ConcreteDataMatcher& matcher, void* obj); void extractCCDBInputs(o2::framework::ProcessingContext& pc, bool laser = true, bool itstpcTgl = true); @@ -63,16 +65,20 @@ class VDriftHelper protected: static void addInput(std::vector& inputs, o2::framework::InputSpec&& isp); + bool extractTPForVDrift(VDriftCorrFact& vdrift, int64_t tsStepMS = 100 * 1000); VDriftCorrFact mVDLaser{}; VDriftCorrFact mVDTPCITSTgl{}; VDriftCorrFact mVD{}; - Source mSource{Source::Param}; // update source - bool mUpdated = false; // signal update, must be reset once new value is fetched + Source mSource{Source::Param}; // update source + bool mUpdated = false; // signal update, must be reset once new value is fetched + bool mIsTPScalingPossible = false; // if T/P scaling is possible always perform the updating bool mForceParamDrift = false; // enforce vdrift from gasParam bool mForceParamOffset = false; // enforce offset from DetectorParam + bool mForceTPScaling = false; // enforce T/P scaling from gasParam (scaling disabled by negative T or P) uint32_t mMayRenormSrc = 0xffffffff; // if starting VDrift correction != 1, we will renorm reference in such a way that initial correction is 1.0, flag per source + PressureTemperatureHelper mPTHelper; // helper to extract pressure and temperature from CCDB - ClassDefNV(VDriftHelper, 1); + ClassDefNV(VDriftHelper, 2); }; } // namespace o2::tpc #endif diff --git a/Detectors/TPC/calibration/src/CalibLaserTracks.cxx b/Detectors/TPC/calibration/src/CalibLaserTracks.cxx index 1e4218c527f02..da52525328c6c 100644 --- a/Detectors/TPC/calibration/src/CalibLaserTracks.cxx +++ b/Detectors/TPC/calibration/src/CalibLaserTracks.cxx @@ -24,13 +24,13 @@ #include using namespace o2::tpc; -void CalibLaserTracks::fill(std::vector const& tracks) +void CalibLaserTracks::fill(std::vector const& tracks, float tp) { - fill(gsl::span(tracks.data(), tracks.size())); + fill(gsl::span(tracks.data(), tracks.size()), tp); } //______________________________________________________________________________ -void CalibLaserTracks::fill(const gsl::span tracks) +void CalibLaserTracks::fill(const gsl::span tracks, float tp) { // ===| clean up TF data |=== mZmatchPairsTFA.clear(); @@ -63,6 +63,9 @@ void CalibLaserTracks::fill(const gsl::span tracks) mCalibDataTF.firstTime = mTFstart; mCalibDataTF.lastTime = tfEnd; + mAvgTP = (mAvgTP * mCalibData.processedTFs + tp) / (mCalibData.processedTFs + 1); + mAvgDriftV = (mAvgDriftV * mCalibData.processedTFs + mDriftV) / (mCalibData.processedTFs + 1); + // ===| TF counters |=== ++mCalibData.processedTFs; ++mCalibDataTF.processedTFs; @@ -147,6 +150,8 @@ void CalibLaserTracks::processTrack(const TrackTPC& track) << "ltr=" << ltr // matched ideal laser track << "trOutLtr=" << parOutAtLtr // track rotated and propagated to ideal track position << "TPCTracks=" << writeTrack // original TPC track + << "mDriftV=" << mDriftV + << "laserTrackID=" << laserTrackID << "\n"; } } @@ -277,6 +282,18 @@ void CalibLaserTracks::merge(const CalibLaserTracks* other) mCalibData.firstTime = std::min(mCalibData.firstTime, other->mCalibData.firstTime); mCalibData.lastTime = std::max(mCalibData.lastTime, other->mCalibData.lastTime); + if ((mAvgTP > 0) && (other->mAvgTP > 0)) { + mAvgTP = (mAvgTP + other->mAvgTP) / 2.0; + } else if (other->mAvgTP > 0) { + mAvgTP = other->mAvgTP; + } + + if ((mAvgDriftV > 0) && (other->mAvgDriftV > 0)) { + mAvgDriftV = (mAvgDriftV + other->mAvgDriftV) / 2.0; + } else if (other->mAvgDriftV > 0) { + mAvgDriftV = other->mAvgDriftV; + } + sort(mZmatchPairsA); sort(mZmatchPairsC); @@ -296,6 +313,7 @@ void CalibLaserTracks::endTF() << "zPairsA=" << mZmatchPairsTFA << "zPairsC=" << mZmatchPairsTFC << "calibData=" << mCalibDataTF + << "mDriftV=" << mDriftV << "\n"; } } @@ -330,7 +348,7 @@ void CalibLaserTracks::fillCalibData(LtrCalibData& calibData, const std::vector< auto dvA = fit(pairsA, "A-Side"); auto dvC = fit(pairsC, "C-Side"); calibData.creationTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - calibData.refVDrift = mDriftV; + calibData.refVDrift = mAvgDriftV; calibData.dvOffsetA = dvA.x1; calibData.dvCorrectionA = dvA.x2; calibData.nTracksA = uint16_t(pairsA.size()); @@ -340,6 +358,7 @@ void CalibLaserTracks::fillCalibData(LtrCalibData& calibData, const std::vector< calibData.nTracksC = uint16_t(pairsC.size()); calibData.refTimeOffset = mTOffsetMUS; + calibData.tp = mAvgTP; } //______________________________________________________________________________ diff --git a/Detectors/TPC/calibration/src/PressureTemperatureHelper.cxx b/Detectors/TPC/calibration/src/PressureTemperatureHelper.cxx index d9a55e4aed2b9..2de4ee2086426 100644 --- a/Detectors/TPC/calibration/src/PressureTemperatureHelper.cxx +++ b/Detectors/TPC/calibration/src/PressureTemperatureHelper.cxx @@ -14,7 +14,7 @@ /// \author Matthias Kleiner #include "TPCCalibration/PressureTemperatureHelper.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBase/CDBTypes.h" #include "Framework/ProcessingContext.h" #include "DataFormatsTPC/DCS.h" #include "Framework/InputRecord.h" @@ -52,14 +52,25 @@ bool PressureTemperatureHelper::accountCCDBInputs(const ConcreteDataMatcher& mat mTemperatureC.second.clear(); for (const auto& dp : temp.statsA.data) { - mTemperatureA.first.emplace_back(dp.value.mean); + mTemperatureA.first.emplace_back(toKelvin(dp.value.mean)); mTemperatureA.second.emplace_back(dp.time); } for (const auto& dp : temp.statsC.data) { - mTemperatureC.first.emplace_back(dp.value.mean); + mTemperatureC.first.emplace_back(toKelvin(dp.value.mean)); mTemperatureC.second.emplace_back(dp.time); } + + // check if temperature data is available + if (mTemperatureA.first.empty() && mTemperatureC.first.empty()) { + float temperature = toKelvin(temp.getMeanTempRaw()); + mTemperatureA.first.emplace_back(temperature); + mTemperatureA.second.emplace_back(0); + mTemperatureC.first.emplace_back(temperature); + mTemperatureC.second.emplace_back(0); + LOGP(warning, "No temperature data available from fit. Using average temperature {} K", temperature); + } + return true; } return false; @@ -117,3 +128,63 @@ void PressureTemperatureHelper::sendPTForTS(o2::framework::ProcessingContext& pc pc.outputs().snapshot(Output{o2::header::gDataOriginTPC, o2::tpc::PressureTemperatureHelper::getDataDescriptionTemperature()}, temp); pc.outputs().snapshot(Output{o2::header::gDataOriginTPC, o2::tpc::PressureTemperatureHelper::getDataDescriptionPressure()}, pressure); } + +float PressureTemperatureHelper::getTP(int64_t ts) const +{ + const float pressure = getPressure(ts); + const auto temp = getMeanTemperature(ts); + if (pressure <= 0) { + LOGP(error, "Pressure {} is zero or negative, cannot compute T/P ratio for timestamp {}", pressure, ts); + return 0; + } + const float tp = temp / pressure; + return tp; +} + +float PressureTemperatureHelper::getMeanTemperature(const ULong64_t timestamp) const +{ + const auto temp = getTemperature(timestamp); + + float sumT = 0; + int w = 0; + constexpr float minTemp = toKelvin(15); + constexpr float maxTemp = toKelvin(25); + if (auto t = temp.first; t > minTemp && t < maxTemp) { + sumT += t; + ++w; + } + if (auto t = temp.second; t > minTemp && t < maxTemp) { + sumT += t; + ++w; + } + + if (w == 0) { + constexpr float defaultTemp = toKelvin(19.6440f); + LOGP(info, "Returning default temperature of {}K", defaultTemp); + return defaultTemp; + } + + const float meanT = sumT / w; + return meanT; +} + +std::pair PressureTemperatureHelper::getMinMaxTime() const +{ + ULong64_t minTime = std::numeric_limits::max(); + ULong64_t maxTime = 0; + + if (!mPressure.first.empty()) { + minTime = std::min(minTime, mPressure.second.front()); + maxTime = std::max(maxTime, mPressure.second.back()); + } + if (!mTemperatureA.first.empty()) { + minTime = std::min(minTime, mTemperatureA.second.front()); + maxTime = std::max(maxTime, mTemperatureA.second.back()); + } + if (!mTemperatureC.first.empty()) { + minTime = std::min(minTime, mTemperatureC.second.front()); + maxTime = std::max(maxTime, mTemperatureC.second.back()); + } + + return {minTime, maxTime}; +} diff --git a/Detectors/TPC/calibration/src/TPCVDriftTglCalibration.cxx b/Detectors/TPC/calibration/src/TPCVDriftTglCalibration.cxx index 61f0d816e1c11..316f4b25eff67 100644 --- a/Detectors/TPC/calibration/src/TPCVDriftTglCalibration.cxx +++ b/Detectors/TPC/calibration/src/TPCVDriftTglCalibration.cxx @@ -10,7 +10,6 @@ // or submit itself to any jurisdiction. #include "TPCCalibration/TPCVDriftTglCalibration.h" -#include "TPCBase/ParameterGas.h" #include "Framework/Logger.h" #include "MathUtils/fit.h" #include "CommonUtils/MemFileHelper.h" @@ -91,7 +90,7 @@ void TPCVDriftTglCalibration::finalizeSlot(Slot& slot) corrFact, corrFactErr, float(cont->driftVFullMean), - cont->tOffsetRef, 0.f}); + cont->tOffsetRef, 0.f, cont->tp}); // at this stage the correction object is defined wrt average corrected drift used for the slot processing, we want to redefine it to run-constant reference vdrift vd.normalize(cont->driftVRef); diff --git a/Detectors/TPC/calibration/src/VDriftHelper.cxx b/Detectors/TPC/calibration/src/VDriftHelper.cxx index fb262acc1afa1..2badf3bb510e8 100644 --- a/Detectors/TPC/calibration/src/VDriftHelper.cxx +++ b/Detectors/TPC/calibration/src/VDriftHelper.cxx @@ -20,6 +20,7 @@ #include "Framework/CCDBParamSpec.h" #include "Framework/InputRecord.h" #include "Framework/ConcreteDataMatcher.h" +#include "Framework/TimingInfo.h" using namespace o2::tpc; using namespace o2::framework; @@ -43,10 +44,19 @@ VDriftHelper::VDriftHelper() if (o2::conf::ConfigurableParam::getProvenance("TPCDetParam.DriftTimeOffset") == o2::conf::ConfigurableParam::EParamProvenance::kRT) { // we stick to this value mVD.creationTime = std::numeric_limits::max(); mForceParamOffset = true; - LOGP(info, "TPC dridt time offset was set from command line to {} mus ({} TB), will neglect update from CCDB", + LOGP(info, "TPC drift time offset was set from command line to {} mus ({} TB), will neglect update from CCDB", mVD.refTimeOffset, detpar.DriftTimeOffset); } + // check if temperature and pressure is set from the command line + if ((o2::conf::ConfigurableParam::getProvenance("TPCGasParam.Temperature") == o2::conf::ConfigurableParam::EParamProvenance::kRT) && (o2::conf::ConfigurableParam::getProvenance("TPCGasParam.Pressure") == o2::conf::ConfigurableParam::EParamProvenance::kRT)) { // we stick to this value + mForceTPScaling = true; + LOGP(info, "VDriftHelper: Temperature and pressure were set from command line to {} C and {} mbar, will neglect updates from CCDB", gaspar.Temperature, gaspar.Pressure); + if (gaspar.Temperature <= 0 || gaspar.Pressure <= 0) { + LOGP(info, "VDriftHelper: Disabling VDrift scaling with T / P"); + } + } + mUpdated = true; mSource = Source::Param; } @@ -74,11 +84,12 @@ void VDriftHelper::accountLaserCalibration(const LtrCalibData* calib, long fallB mVDLaser.corrFact = 1. / corr; mVDLaser.creationTime = calib->creationTime; mVDLaser.refTimeOffset = calib->refTimeOffset; + mVDLaser.refTP = calib->tp; mUpdated = true; mSource = Source::Laser; if (mMayRenormSrc & (0x1U << Source::Laser)) { // this was 1st setting? if (corr != 1.f) { // this may happen if old-style (non-normalized) standalone or non-normalized run-time laset calibration is used - LOGP(warn, "VDriftHelper: renorming initinal TPC refVDrift={}/correction={} to {}/1.0, source: {}", mVDLaser.refVDrift, mVDLaser.corrFact, mVDLaser.getVDrift(), getSourceName(mSource)); + LOGP(warn, "VDriftHelper: renorming initial TPC refVDrift={}/correction={} to {}/1.0, source: {}", mVDLaser.refVDrift, mVDLaser.corrFact, mVDLaser.getVDrift(), getSourceName(mSource)); mVDLaser.normalize(); // renorm reference to have correction = 1. } mMayRenormSrc &= ~(0x1U << Source::Laser); // unset MayRenorm @@ -103,11 +114,11 @@ void VDriftHelper::accountDriftCorrectionITSTPCTgl(const VDriftCorrFact* calib) mSource = Source::ITSTPCTgl; if (mMayRenormSrc & (0x1U << Source::ITSTPCTgl)) { // this was 1st setting? if (!mForceParamDrift && mVDTPCITSTgl.corrFact != 1.f) { // this may happen if calibration from prevous run is used - LOGP(warn, "VDriftHelper: renorming initinal TPC refVDrift={}/correction={} to {}/1.0, source: {}", mVDTPCITSTgl.refVDrift, mVDTPCITSTgl.corrFact, mVDTPCITSTgl.getVDrift(), getSourceName(mSource)); + LOGP(warn, "VDriftHelper: renorming initial TPC refVDrift={}/correction={} to {}/1.0, source: {}", mVDTPCITSTgl.refVDrift, mVDTPCITSTgl.corrFact, mVDTPCITSTgl.getVDrift(), getSourceName(mSource)); mVDTPCITSTgl.normalize(); // renorm reference to have correction = 1. } if (!mForceParamOffset && mVDTPCITSTgl.timeOffsetCorr != 0.) { - LOGP(warn, "VDriftHelper: renorming initinal TPC refTimeOffset={}/correction={} to {}/0.0, source: {}", mVDTPCITSTgl.refTimeOffset, mVDTPCITSTgl.timeOffsetCorr, mVDTPCITSTgl.getTimeOffset(), getSourceName()); + LOGP(warn, "VDriftHelper: renorming initial TPC refTimeOffset={}/correction={} to {}/0.0, source: {}", mVDTPCITSTgl.refTimeOffset, mVDTPCITSTgl.timeOffsetCorr, mVDTPCITSTgl.getTimeOffset(), getSourceName()); mVDTPCITSTgl.normalizeOffset(); } mMayRenormSrc &= ~(0x1U << Source::ITSTPCTgl); // unset MayRenorm @@ -135,9 +146,34 @@ void VDriftHelper::extractCCDBInputs(ProcessingContext& pc, bool laser, bool its if (itstpcTgl) { pc.inputs().get("vdriftTgl"); } - if (mUpdated) { // there was a change + mPTHelper.extractCCDBInputs(pc); + + if (mUpdated || mIsTPScalingPossible) { // there was a change // prefer among laser and tgl VDrift the one with the latest update time auto saveVD = mVD; + + // apply TP scaling of mVD if possible + if (float tp = mPTHelper.getTP(pc.services().get().creation); tp > 0) { + // try to extract refTP if needed + auto& vd = (mVDTPCITSTgl.creationTime < mVDLaser.creationTime) ? mVDLaser : mVDTPCITSTgl; + if (mForceTPScaling) { + const auto& gaspar = o2::tpc::ParameterGas::Instance(); + tp = (gaspar.Temperature > 0 && gaspar.Pressure > 0) ? ((gaspar.Temperature + 273.15) / gaspar.Pressure) : -1; + mIsTPScalingPossible = (tp > 0) && (vd.refTP > 0 || extractTPForVDrift(vd)); + } else { + mIsTPScalingPossible = (vd.refTP > 0) || extractTPForVDrift(vd); + } + if (mIsTPScalingPossible) { + mUpdated = true; + vd.normalize(0, tp); + if (vd.creationTime == saveVD.creationTime) { + LOGP(info, "VDriftHelper: Scaling VDrift from {} to {} with T/P from {} to {}", saveVD.getVDrift(), vd.getVDrift(), saveVD.refTP, vd.refTP); + } else { + LOGP(info, "VDriftHelper: Init new VDrift of {} with T/P {}", vd.getVDrift(), vd.refTP); + } + } + } + mVD = mVDTPCITSTgl.creationTime < mVDLaser.creationTime ? mVDLaser : mVDTPCITSTgl; auto& loserVD = mVDTPCITSTgl.creationTime < mVDLaser.creationTime ? mVDTPCITSTgl : mVDLaser; @@ -178,6 +214,8 @@ void VDriftHelper::requestCCDBInputs(std::vector& inputs, bool laser, // VDrift calibration may change during the run (in opposite to Laser calibration, at least at the moment), so ask per-TF query addInput(inputs, {"vdriftTgl", "TPC", "VDriftTgl", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalVDriftTgl), {}, 1)}); } + // adding pressure and temperature inputs + PressureTemperatureHelper::requestCCDBInputs(inputs); } //________________________________________________________ @@ -199,5 +237,42 @@ bool VDriftHelper::accountCCDBInputs(const ConcreteDataMatcher& matcher, void* o accountLaserCalibration(static_cast(obj)); return true; } - return false; + return mPTHelper.accountCCDBInputs(matcher, obj); +} + +bool VDriftHelper::extractTPForVDrift(VDriftCorrFact& vdrift, int64_t tsStepMS) +{ + const int64_t tsStart = vdrift.firstTime; + const int64_t tsEnd = vdrift.lastTime; + + // make sanity check of the time range + const auto [minValidTime, maxValidTime] = mPTHelper.getMinMaxTime(); + const int64_t minTimeAccepted = static_cast(minValidTime) - 20 * o2::ccdb::CcdbObjectInfo::MINUTE; + const int64_t maxTimeAccepted = static_cast(maxValidTime) + 20 * o2::ccdb::CcdbObjectInfo::MINUTE; + + // check if the stored time stamp range is valid i.e. check if the range is in the vicinity of the current time + if ((minTimeAccepted > tsEnd) || (tsStart > maxTimeAccepted)) { + // check if creation time can be used + LOGP(warn, "VDriftHelper: Time range of VDrift object {} - {} is not valid for time range of T/P object {} - {}! Do not extract ref. T/P for VDrift!", tsStart, tsEnd, minValidTime, maxValidTime); + return false; + } + + double meanTP = 0; + int countTP = 0; + + for (int64_t ts = tsStart; ts < tsEnd; ts += tsStepMS) { + meanTP += mPTHelper.getTP(ts); + ++countTP; + } + + if (countTP == 0) { + LOGP(error, "VDriftHelper: Could not get T/P for time range {} -> {}", tsStart, tsEnd); + return false; + } + + meanTP /= countTP; + + LOGP(info, "VDriftHelper: Setting mean T/P for VDrift to {} for time range {} -> {}", meanTP, tsStart, tsEnd); + vdrift.refTP = meanTP; + return true; } diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/CalibLaserTracksSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/CalibLaserTracksSpec.h index 207bea0e7fa42..3ae33c7c2a5db 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/CalibLaserTracksSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/CalibLaserTracksSpec.h @@ -64,6 +64,10 @@ class CalibLaserTracksDevice : public o2::framework::Task return; } mTPCVDriftHelper.extractCCDBInputs(pc); + const auto timestamp = pc.services().get().creation; + + // if reference temperature / pressure of VDrift object is zero then it was not corrected + const float tp = (mTPCVDriftHelper.getVDriftObject().refTP == 0) ? mTPCVDriftHelper.getPTHelper().getTP(timestamp) : mTPCVDriftHelper.getVDriftObject().refTP; if (mTPCVDriftHelper.isUpdated()) { mTPCVDriftHelper.acknowledgeUpdate(); mCalib.setVDriftRef(mTPCVDriftHelper.getVDriftObject().getVDrift()); @@ -75,7 +79,7 @@ class CalibLaserTracksDevice : public o2::framework::Task auto data = pc.inputs().get>("input"); mCalib.setTFtimes(startTime, endTime); - mCalib.fill(data); + mCalib.fill(data, tp); if (!mOnlyPublishOnEOS && mCalib.hasEnoughData(mMinNumberTFs) && !mPublished) { sendOutput(pc.outputs()); diff --git a/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx b/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx index a9f1e7d71da8e..5007019d52910 100644 --- a/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx +++ b/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx @@ -19,7 +19,6 @@ #include "Framework/ConfigParamRegistry.h" #include "Framework/DataProcessorSpec.h" #include "Framework/ControlService.h" -#include "DataFormatsTPC/WorkflowHelper.h" #include "TPCWorkflow/ProcessingHelpers.h" #include "TPCBase/Mapper.h" #include "DetectorsBase/GRPGeomHelper.h" @@ -31,7 +30,6 @@ #include "CommonUtils/TreeStreamRedirector.h" #include "MathUtils/Tsallis.h" #include "ReconstructionDataFormats/TrackTPCITS.h" -#include "CommonDataFormat/AbstractRefAccessor.h" #include "ReconstructionDataFormats/PrimaryVertex.h" #include "ReconstructionDataFormats/VtxTrackIndex.h" #include "ReconstructionDataFormats/VtxTrackRef.h" @@ -46,6 +44,7 @@ #include "ReconstructionDataFormats/MatchInfoTOF.h" #include "DataFormatsTOF/Cluster.h" #include "DataFormatsFT0/RecPoints.h" +#include "TPCCalibration/PressureTemperatureHelper.h" using namespace o2::globaltracking; using GTrackID = o2::dataformats::GlobalTrackID; @@ -127,16 +126,20 @@ class TPCTimeSeries : public Task { o2::base::GRPGeomHelper::instance().checkUpdates(pc); mTPCVDriftHelper.extractCCDBInputs(pc); + mPTHelper.extractCCDBInputs(pc); if (mTPCVDriftHelper.isUpdated()) { mTPCVDriftHelper.acknowledgeUpdate(); mVDrift = mTPCVDriftHelper.getVDriftObject().getVDrift(); LOGP(info, "Updated reference drift velocity to: {}", mVDrift); } + mBufferDCA.mVDrift = mVDrift; const int nBins = getNBins(); mTimeMS = o2::base::GRPGeomHelper::instance().getOrbitResetTimeMS() + processing_helpers::getFirstTForbit(pc) * o2::constants::lhc::LHCOrbitMUS / 1000; mRun = processing_helpers::getRunNumber(pc); + mBufferDCA.mTemperature = mPTHelper.getMeanTemperature(mTimeMS); + mBufferDCA.mPressure = mPTHelper.getPressure(mTimeMS); // init only once if (mAvgADCAr.size() != nBins) { @@ -870,6 +873,7 @@ class TPCTimeSeries : public Task void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final { mTPCVDriftHelper.accountCCDBInputs(matcher, obj); + mPTHelper.accountCCDBInputs(matcher, obj); o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj); } @@ -1107,6 +1111,7 @@ class TPCTimeSeries : public Task long mTimeMS{}; ///< time in MS of current TF int mRun{}; ///< run number int mMaxOccupancyHistBins{912}; ///< maximum number of occupancy bins + PressureTemperatureHelper mPTHelper; ///< helper to extract pressure and temperature from CCDB /// check if track passes coarse cuts bool acceptTrack(const TrackTPC& track) const { return std::abs(track.getTgl()) < mMaxTgl; } @@ -1823,12 +1828,11 @@ o2::framework::DataProcessorSpec getTPCTimeSeriesSpec(const bool disableWriter, dataRequest->requestTracks(srcTracks, useMC); dataRequest->requestClusters(GTrackID::getSourcesMask("TPC"), useMC); - dataRequest->requestFT0RecPoints(false); - bool tpcOnly = srcTracks == GTrackID::getSourcesMask("TPC"); if (!tpcOnly) { - dataRequest->requestPrimaryVertices(useMC); + dataRequest->requestFT0RecPoints(useMC); } + dataRequest->requestPrimaryVertices(useMC); const bool enableAskMatLUT = matType == o2::base::Propagator::MatCorrType::USEMatCorrLUT; auto ccdbRequest = std::make_shared(!disableWriter, // orbitResetTime @@ -1842,6 +1846,7 @@ o2::framework::DataProcessorSpec getTPCTimeSeriesSpec(const bool disableWriter, true); o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); + PressureTemperatureHelper::requestCCDBInputs(dataRequest->inputs); std::vector outputs; outputs.emplace_back(o2::header::gDataOriginTPC, getDataDescriptionTimeSeries(), 0, Lifetime::Sporadic); if (!disableWriter) { diff --git a/Detectors/TPC/workflow/src/TPCVDriftTglCalibSpec.cxx b/Detectors/TPC/workflow/src/TPCVDriftTglCalibSpec.cxx index 957ed07078cfe..8456fa3fa4740 100644 --- a/Detectors/TPC/workflow/src/TPCVDriftTglCalibSpec.cxx +++ b/Detectors/TPC/workflow/src/TPCVDriftTglCalibSpec.cxx @@ -20,6 +20,7 @@ #include "CCDB/CcdbApi.h" #include "CCDB/CcdbObjectInfo.h" #include "Framework/Task.h" +#include "TPCCalibration/PressureTemperatureHelper.h" using namespace o2::framework; @@ -52,12 +53,24 @@ class TPCVDriftTglCalibSpec : public Task return; } o2::base::GRPGeomHelper::instance().checkUpdates(pc); + mPTHelper.extractCCDBInputs(pc); auto data = pc.inputs().get>>("input"); o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mCalibrator->getCurrentTFInfo()); if (data.size()) { LOG(detail) << "Processing TF " << mCalibrator->getCurrentTFInfo().tfCounter << " with " << data.size() - 2 << " tracks"; // 1st entry is for VDrift, 2nd for the offset } - mCalibrator->process(data); + + // if no T/P scaling of the VDrift was performed get the current T/P + float tp = 0; + if (!data.empty()) { + // third value of first entry is the T/P ratio, if it is 0, we use the current T/P + if (data[0].third == 0) { + const auto timestamp = pc.services().get().creation; + tp = mPTHelper.getTP(timestamp); + } + } + + mCalibrator->process(data, tp); if (pc.transitionState() == TransitionHandlingState::Requested) { LOG(info) << "Run stop requested, finalizing"; mRunStopRequested = true; @@ -80,13 +93,15 @@ class TPCVDriftTglCalibSpec : public Task void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final { o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj); + mPTHelper.accountCCDBInputs(matcher, obj); } private: void sendOutput(DataAllocator& output); std::unique_ptr mCalibrator; std::shared_ptr mCCDBRequest; - bool mRunStopRequested = false; // flag that run was stopped (ant the last output is sent) + PressureTemperatureHelper mPTHelper; // helper to extract pressure and temperature from CCDB + bool mRunStopRequested = false; // flag that run was stopped (ant the last output is sent) }; //_____________________________________________________________ @@ -134,6 +149,8 @@ DataProcessorSpec getTPCVDriftTglCalibSpec(int ntgl, float tglMax, int ndtgl, fl outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, "TPCVDTGL"}, Lifetime::Sporadic); slot0frac = 1. - slot0frac; + PressureTemperatureHelper::requestCCDBInputs(inputs); + return DataProcessorSpec{ "tpc-vd-tgl-calib", inputs, From b0129ed1cf038246a03723c3dcd1cdce982f8013 Mon Sep 17 00:00:00 2001 From: Matthias Kleiner Date: Wed, 20 Aug 2025 09:50:03 +0200 Subject: [PATCH 2/2] VDrift: Keep refVDrift constant by changing corrFact with T/P --- .../Detectors/TPC/include/DataFormatsTPC/VDriftCorrFact.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/VDriftCorrFact.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/VDriftCorrFact.h index 4cee3ab1ad50b..a20c37e9b2cee 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/VDriftCorrFact.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/VDriftCorrFact.h @@ -47,11 +47,10 @@ struct VDriftCorrFact { float normVDrift = newVRef; if (newVRef == 0.f) { normVDrift = refVDrift * corrFact; - if ((tp == 0) || (refTP == 0)) { - newVRef = normVDrift; // no T/P scaling applied - } else { + newVRef = normVDrift; + if ((tp > 0) && (refTP > 0)) { // linear scaling based on relative change of T/P - newVRef = normVDrift * (1 + (tp - refTP) / refTP); + normVDrift *= refTP / tp; refTP = tp; // update reference T/P } }