Skip to content

Commit 115d870

Browse files
matthias-kleinerwiechula
authored andcommitted
Pressure: Fix last smoothed pressure value
The rolling median in makeRobustPressure was computed with a one-sided (past-only) window for the last query time(s) of each ~10-minute processing slot, because no future data existed yet in the buffer. This caused the last value(s) of each slot to lag behind any pressure trend, followed by a visible jump when the next slot started with a symmetric window. Fixed by withholding trailing query points until a 2x-timeInterval look-ahead margin of future data is available, picking them up seamlessly in the next slot. Also fixes: - CCDB objects were uploaded even for slots with no new data for that quantity; now skipped when there's nothing to store. - Pressure's CCDB end-validity now anchors on its own last data point plus a margin, instead of mLastCreationTime + generic extension
1 parent a69fc0a commit 115d870

4 files changed

Lines changed: 84 additions & 15 deletions

File tree

DataFormats/Detectors/TPC/src/DCS.cxx

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,17 @@ void fillBuffer(std::pair<std::vector<float>, std::vector<TimeStampType>>& buffe
354354
buffer = std::move(buffTmp);
355355
}
356356

357+
/// truncate all parallel vectors of a RollingStats to size n, keeping the leading n entries.
358+
/// Used to keep RobustPressure's members aligned with a `times` vector that got trimmed.
359+
void trimStats(o2::math_utils::RollingStats& stats, size_t n)
360+
{
361+
stats.median.resize(n);
362+
stats.std.resize(n);
363+
stats.nPoints.resize(n);
364+
stats.closestDistanceL.resize(n);
365+
stats.closestDistanceR.resize(n);
366+
}
367+
357368
void Pressure::makeRobustPressure(TimeStampType timeInterval, TimeStampType timeIntervalRef, TimeStampType tStart, TimeStampType tEnd, const int nthreads)
358369
{
359370
const auto surfaceAtmosPressurePair = surfaceAtmosPressure.getPairOfVector();
@@ -380,9 +391,9 @@ void Pressure::makeRobustPressure(TimeStampType timeInterval, TimeStampType time
380391

381392
/// minimum number of points in the interval - otherwise use the n closest points
382393
const int minPoints = 4;
383-
const auto cavernAtmosPressureStats = o2::math_utils::getRollingStatistics(mCavernAtmosPressure1Buff.second, mCavernAtmosPressure1Buff.first, times, timeInterval, nthreads, minPoints, minPoints);
384-
const auto cavernAtmosPressure2Stats = o2::math_utils::getRollingStatistics(mCavernAtmosPressure2Buff.second, mCavernAtmosPressure2Buff.first, times, timeInterval, nthreads, minPoints, minPoints);
385-
const auto surfaceAtmosPressureStats = o2::math_utils::getRollingStatistics(mSurfaceAtmosPressureBuff.second, mSurfaceAtmosPressureBuff.first, times, timeInterval, nthreads, minPoints, minPoints);
394+
auto cavernAtmosPressureStats = o2::math_utils::getRollingStatistics(mCavernAtmosPressure1Buff.second, mCavernAtmosPressure1Buff.first, times, timeInterval, nthreads, minPoints, minPoints);
395+
auto cavernAtmosPressure2Stats = o2::math_utils::getRollingStatistics(mCavernAtmosPressure2Buff.second, mCavernAtmosPressure2Buff.first, times, timeInterval, nthreads, minPoints, minPoints);
396+
auto surfaceAtmosPressureStats = o2::math_utils::getRollingStatistics(mSurfaceAtmosPressureBuff.second, mSurfaceAtmosPressureBuff.first, times, timeInterval, nthreads, minPoints, minPoints);
386397

387398
// subtract the moving median values from the different sensors if they are ok
388399
std::pair<std::vector<float>, std::vector<TimeStampType>> cavernAtmosPressure12;
@@ -421,9 +432,9 @@ void Pressure::makeRobustPressure(TimeStampType timeInterval, TimeStampType time
421432
fillBuffer(mPressure2SBuff, cavernAtmosPressure2S, tStartRef, minPointsRef);
422433

423434
// get long term median of diffs - this is used for normalization of the pressure values -
424-
const auto cavernAtmosPressure12Stats = o2::math_utils::getRollingStatistics(mPressure12Buff.second, mPressure12Buff.first, times, timeIntervalRef, nthreads, 3, minPointsRef);
425-
const auto cavernAtmosPressure1SStats = o2::math_utils::getRollingStatistics(mPressure1SBuff.second, mPressure1SBuff.first, times, timeIntervalRef, nthreads, 3, minPointsRef);
426-
const auto cavernAtmosPressure2SStats = o2::math_utils::getRollingStatistics(mPressure2SBuff.second, mPressure2SBuff.first, times, timeIntervalRef, nthreads, 3, minPointsRef);
435+
auto cavernAtmosPressure12Stats = o2::math_utils::getRollingStatistics(mPressure12Buff.second, mPressure12Buff.first, times, timeIntervalRef, nthreads, 3, minPointsRef);
436+
auto cavernAtmosPressure1SStats = o2::math_utils::getRollingStatistics(mPressure1SBuff.second, mPressure1SBuff.first, times, timeIntervalRef, nthreads, 3, minPointsRef);
437+
auto cavernAtmosPressure2SStats = o2::math_utils::getRollingStatistics(mPressure2SBuff.second, mPressure2SBuff.first, times, timeIntervalRef, nthreads, 3, minPointsRef);
427438

428439
// calculate diffs of median values
429440
const float maxDist = 20 * timeInterval;
@@ -518,6 +529,27 @@ void Pressure::makeRobustPressure(TimeStampType timeInterval, TimeStampType time
518529

519530
fillBuffer(mRobPressureBuff, robustPressureTmp, tStartRef, minPointsRef);
520531

532+
// drop trailing query times that don't yet have a full look-ahead margin of data
533+
// to their right in the buffer: the smoothing window is ±timeInterval, so without
534+
// it those points would be smoothed with a partially or fully one-sided (past-only)
535+
// window, biasing them low/high and causing a jump at the slot boundary.
536+
const auto& robBuffTimes = mRobPressureBuff.second;
537+
const TimeStampType lookaheadMargin = 2 * timeInterval;
538+
while (times.size() > 1 && !robBuffTimes.empty() && times.back() + lookaheadMargin > robBuffTimes.back()) {
539+
times.pop_back();
540+
}
541+
isOk.resize(times.size());
542+
543+
// the *Stats above were computed for the untrimmed query grid; truncate them to
544+
// match so all vectors stored in RobustPressure stay parallel/same length as time.
545+
// The dropped tail is simply recomputed (with a proper symmetric window) next slot.
546+
trimStats(cavernAtmosPressureStats, times.size());
547+
trimStats(cavernAtmosPressure2Stats, times.size());
548+
trimStats(surfaceAtmosPressureStats, times.size());
549+
trimStats(cavernAtmosPressure12Stats, times.size());
550+
trimStats(cavernAtmosPressure1SStats, times.size());
551+
trimStats(cavernAtmosPressure2SStats, times.size());
552+
521553
RobustPressure& pOut = robustPressure;
522554
pOut.surfaceAtmosPressure = std::move(surfaceAtmosPressureStats);
523555
pOut.cavernAtmosPressure2 = std::move(cavernAtmosPressure2Stats);

Detectors/TPC/dcs/include/TPCdcs/DCSProcessor.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ class DCSProcessor
102102
const auto& getTimeGas() const { return mTimeGas; }
103103
const auto& getTimePressure() const { return mTimePressure; }
104104

105+
/// CCDB validity start for the pressure object: last output time of the previous slot
106+
/// (0 on the first slot, falls back to mTimePressure.first in finalizePressure)
107+
auto getPressureCCDBStartTime() const { return mPressureCCDBStartTime; }
108+
105109
auto& getTemperature() { return mTemperature; }
106110
auto& getHighVoltage() { return mHighVoltage; }
107111
auto& getGas() { return mGas; }
@@ -121,6 +125,8 @@ class DCSProcessor
121125
dcs::TimeStampType mFitInterval{5 * 60 * 1000}; ///< fit interval (ms) e.g. for temparature data
122126
dcs::TimeStampType mPressureInterval{200 * 1000}; ///< interval (ms) for averaging pressure values
123127
dcs::TimeStampType mPressureIntervalRef{60 * 60 * 1000}; ///< interval (ms) for averaging pressure values for longer reference time interval
128+
dcs::TimeStampType mLastPressureOutputEndTime{0}; ///< last time stamp in pOut.time from previous finalizePressure call
129+
dcs::TimeStampType mPressureCCDBStartTime{0}; ///< CCDB validity start for current pressure slot
124130
bool mWriteDebug{false}; ///< switch to dump debug tree
125131
bool mRoundToInterval{false}; ///< round to full fit interval e.g. full minute
126132
bool mHasData{false}; ///< if there are data to process

Detectors/TPC/dcs/src/DCSProcessor.cxx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,20 @@ void DCSProcessor::finalizePressure()
178178
mTimePressure = {mPressure.getMinTime(), mPressure.getMaxTime()};
179179
// if there is data perform the processing
180180
if (mTimePressure.last > 0) {
181-
mPressure.makeRobustPressure(mPressureInterval, mPressureIntervalRef, mTimePressure.first, mTimePressure.last);
181+
// capture start for CCDB validity before updating mLastPressureOutputEndTime
182+
mPressureCCDBStartTime = (mLastPressureOutputEndTime > 0) ? mLastPressureOutputEndTime : mTimePressure.first;
183+
// if the previous slot withheld trailing points (no full look-ahead margin yet),
184+
// start a half-interval earlier so times[0] = mLastPressureOutputEndTime +
185+
// timeInterval picks up exactly where the previous slot's kept data ended,
186+
// regardless of how many trailing points it withheld
187+
auto tStart = mTimePressure.first;
188+
if (mLastPressureOutputEndTime > 0) {
189+
tStart = std::min(tStart, mLastPressureOutputEndTime + mPressureInterval / 2);
190+
}
191+
mPressure.makeRobustPressure(mPressureInterval, mPressureIntervalRef, tStart, mTimePressure.last);
192+
if (!mPressure.robustPressure.time.empty()) {
193+
mLastPressureOutputEndTime = mPressure.robustPressure.time.back();
194+
}
182195
}
183196
}
184197

Detectors/TPC/dcs/src/DCSSpec.cxx

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class DCSDevice : public o2::framework::Task
6060
void run(o2::framework::ProcessingContext& pc) final;
6161

6262
template <typename T>
63-
void sendObject(DataAllocator& output, T& obj, const CDBType calibType);
63+
void sendObject(DataAllocator& output, T& obj, const CDBType calibType, uint64_t startTime, uint64_t endTimeOverride = 0);
6464

6565
void updateCCDB(DataAllocator& output);
6666

@@ -162,14 +162,17 @@ void DCSDevice::run(o2::framework::ProcessingContext& pc)
162162
}
163163

164164
template <typename T>
165-
void DCSDevice::sendObject(DataAllocator& output, T& obj, const CDBType calibType)
165+
void DCSDevice::sendObject(DataAllocator& output, T& obj, const CDBType calibType, uint64_t startTime, uint64_t endTimeOverride)
166166
{
167167
LOGP(info, "Prepare CCDB for {}", CDBTypeMap.at(calibType));
168168

169169
std::map<std::string, std::string> md = mCDBStorage.getMetaData();
170170
o2::ccdb::CcdbObjectInfo w;
171-
// for online processing extend the validity range. Will be truncated with the adjustableEOV procedure
172-
o2::calibration::Utils::prepareCCDBobjectInfo(obj, w, CDBTypeMap.at(calibType), md, mUpdateIntervalStart, mLastCreationTime + 2 * mCCDBupdateInterval * 1000);
171+
// for online processing extend the validity range. Will be truncated with the adjustableEOV procedure.
172+
// endTimeOverride==0 (the default) means "use the generic extension"; callers that need a
173+
// different end-validity (currently only pressure - see updateCCDB()) pass their own value.
174+
const uint64_t endTime = endTimeOverride > 0 ? endTimeOverride : mLastCreationTime + 2 * mCCDBupdateInterval * 1000;
175+
o2::calibration::Utils::prepareCCDBobjectInfo(obj, w, CDBTypeMap.at(calibType), md, startTime, endTime);
173176
auto image = o2::ccdb::CcdbApi::createObjectImage(&obj, &w);
174177

175178
LOGP(info, "Sending object {} / {} of size {} bytes, valid for {} : {} ", w.getPath(), w.getFileName(), image->size(), w.getStartValidityTimestamp(), w.getEndValidityTimestamp());
@@ -179,10 +182,25 @@ void DCSDevice::sendObject(DataAllocator& output, T& obj, const CDBType calibTyp
179182

180183
void DCSDevice::updateCCDB(DataAllocator& output)
181184
{
182-
sendObject(output, mDCS.getTemperature(), CDBType::CalTemperature);
183-
sendObject(output, mDCS.getHighVoltage(), CDBType::CalHV);
184-
sendObject(output, mDCS.getGas(), CDBType::CalGas);
185-
sendObject(output, mDCS.getPressure(), CDBType::CalPressure);
185+
// only store an object if it actually received new data this slot; otherwise
186+
// we'd upload an empty object, for pressure additionally tagged with a stale
187+
// start-validity time left over from a previous slot
188+
if (mDCS.getTimeTemperature().last > 0) {
189+
sendObject(output, mDCS.getTemperature(), CDBType::CalTemperature, mUpdateIntervalStart);
190+
}
191+
if (mDCS.getTimeHighVoltage().last > 0) {
192+
sendObject(output, mDCS.getHighVoltage(), CDBType::CalHV, mUpdateIntervalStart);
193+
}
194+
if (mDCS.getTimeGas().last > 0) {
195+
sendObject(output, mDCS.getGas(), CDBType::CalGas, mUpdateIntervalStart);
196+
}
197+
if (mDCS.getTimePressure().last > 0) {
198+
const auto& pressureTime = mDCS.getPressure().robustPressure.time;
199+
const uint64_t genericMargin = 2 * uint64_t(mCCDBupdateInterval) * 1000;
200+
const uint64_t tailMargin = 2 * uint64_t(mDCS.getPressureInterval());
201+
const uint64_t pressureEnd = pressureTime.empty() ? 0 : static_cast<uint64_t>(pressureTime.back()) + std::max(genericMargin, tailMargin);
202+
sendObject(output, mDCS.getPressure(), CDBType::CalPressure, mDCS.getPressureCCDBStartTime(), pressureEnd);
203+
}
186204
}
187205

188206
/// ===| create DCS processor |=================================================

0 commit comments

Comments
 (0)