ATLAS Offline Software
Loading...
Searching...
No Matches
LArOFFCRawChannelBuilder.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
6
7#include "GaudiKernel/SystemOfUnits.h"
15
16#include <algorithm>
17#include <array>
18#include <cmath>
19#include <memory>
20#include <span>
21#include <vector>
22
23namespace {
24// Lags entering the Q3 test, relative to the candidate peak. Lag 0 is left
25// out: its residual is identically zero, A being the corrected value at the
26// peak and the template 1 there. Nothing above lag +1 either, that is the
27// newest value available when the peak is tested.
28constexpr std::array<int, 3> q3Lags{-2, -1, 1};
29static_assert(std::ranges::is_sorted(q3Lags) && q3Lags.back() <= 1);
30
31// Entry of a pulse response that a pulse is found at. Pulses are identified as
32// local maxima of the filter output, so that is where the OFC window sits on
33// the shape. Undefined for an empty response, callers check.
34int anchorIndex(const std::vector<double>& response) {
35 return std::distance(response.begin(), std::ranges::max_element(response));
36}
37} // namespace
38
40 const ILArShape::ShapeRef_t& shape, const ILArOFC::OFCRef_t& ofc) const {
41 // Filter output for a unit-amplitude pulse: same correlation as the
42 // filtering loop in computeOFFC, samples replaced by the shape. Writing it
43 // as a convolution.
44
45 const int shapeSize = shape.size();
46 const int ofcSize = ofc.size();
47
48 auto shapeVal = [&](int k) {
49 return (k >= 0 && k < shapeSize) ? shape[k] : 0.0f;
50 };
51
52 // response[k] is the filter output for a pulse offset by k-(ofcSize-1)
53 // samples from the OFC window, tabulated over the whole range where the two
54 // overlap. Where the in-time position falls inside it is up to the OFCs.
55 std::vector<double> response(std::max(0, shapeSize + ofcSize - 1), 0.0);
56
57 for (int k = 0; k < static_cast<int>(response.size()); ++k) {
58 const int offset = k - (ofcSize - 1);
59 for (int j = 0; j < ofcSize; ++j)
60 response[k] += shapeVal(offset + j) * ofc[j];
61 }
62
63 return response;
64}
65
66double LArOFFCRawChannelBuilder::computeOFFC(const std::vector<short>& samples,
67 int firstSample,
68 const ILArOFC::OFCRef_t& ofc,
69 const ILArShape::ShapeRef_t& shape,
70 double pedestal) const {
71 // OFFC parameters (configured via job options)
72 // belowThreshold : ADC threshold to detect quiet regions
73 // belowTillReset : consecutive quiet samples before cache reset
74 // npulse : max number of overlapping pulses tracked
75 // Q3cut : shape-consistency cut for pulse acceptance
76 // filterThreshold : minimum filtered amplitude to seed a pulse
77
78 const int nSamples = samples.size();
79 const int ofcLen = ofc.size();
80
81 // Checked by the caller. The filter at position i needs samples
82 // i ... i+ofcLen-1, and every position it is evaluated at gets a corrected
83 // value, so the last one is nSamples-ofcLen.
84 if (ofcLen <= 0 || firstSample < 0 || firstSample + ofcLen > nSamples) {
85 ATH_MSG_WARNING("Cannot run the OFFC on " << nSamples << " samples with "
86 << ofcLen << " OFCs from sample "
87 << firstSample);
88 return 0.0;
89 }
90
91 // Pedestal subtraction
92 std::vector<double> samp_no_ped(nSamples);
93 for (int i = 0; i < nSamples; ++i)
94 samp_no_ped[i] = samples[i] - pedestal;
95
96 std::vector<double> reco(nSamples, 0.0);
97
98 // Precompute the filter response to a unit-amplitude pulse. cache[lagZero]
99 // is the correction of the sample being filtered, cache[lagZero+n] that of
100 // the one n later.
101 std::vector<double> response = pulseResponse(shape, ofc);
102 const int responseSize = response.size();
103 if (responseSize == 0) {
104 ATH_MSG_WARNING("Empty pulse response, shape size " << shape.size()
105 << " OFC size " << ofcLen);
106 return 0.0;
107 }
108
109 // Entry of response[] a pulse is found at. Pulses are identified as local
110 // maxima of 'filtered', so the template has to be anchored where the
111 // response peaks: that follows whatever alignment the OFCs carry, which is
112 // neither fixed nor the same for every channel (see LArOFCCondAlg).
113 const int lagZero = anchorIndex(response);
114 if (response[lagZero] <= 0.0) {
115 ATH_MSG_WARNING("Pulse response is nowhere positive, cannot subtract");
116 return 0.0;
117 }
118
119 // A is the filter output at that peak, so the template must be 1 there. It
120 // only already is when the response happens to peak where the OFCs were
121 // matched to the shape.
122 const double peakResponse = response[lagZero];
123 for (double& r : response)
124 r /= peakResponse;
125 std::vector<double> cache(responseSize, 0.0);
126
127 // A pulse writes cache entries lagZero ... responseSize-2, and entry k is
128 // consumed k-lagZero iterations later, so that is how long a slot stays busy.
129 const int correctionLength = responseSize - 2 - lagZero;
130
131 // Sample at which each pulse slot becomes available again
132 std::vector<int> slotFreeAt(m_nPulse, 0);
133 int belowCounter = 0;
134
135 const int loopEnd = std::max(0, nSamples - ofcLen + 1);
136
137 for (int i = 0; i < loopEnd; ++i) {
138
139 // Reset the correction cache after an extended quiet region
140 if (std::abs(samp_no_ped[i]) < m_belowThreshold)
141 ++belowCounter;
142 else
143 belowCounter = 0;
144
145 if (m_belowTillReset > 0 && belowCounter >= m_belowTillReset) {
146 belowCounter = 0;
147 std::fill(cache.begin(), cache.end(), 0.0);
148 // The corrections these slots were tracking have just been dropped, so
149 // the slots have to be released with them
150 std::fill(slotFreeAt.begin(), slotFreeAt.end(), 0);
151 }
152
153 // Standard OF filtering
154 double filtered = 0.0;
155 for (int j = 0; j < ofcLen; ++j)
156 filtered += samp_no_ped[i + j] * ofc[j];
157
158 // Corrected value of the sample just filtered, before anything found this
159 // iteration is subtracted from it
160 const double recoCurrent = filtered + cache[lagZero];
161
162 // Candidate peak: where a pulse would be in time with the OFCs, not where
163 // the pulse itself is largest. The search trails the filtering by one, a
164 // maximum can only be recognised once the following sample is in.
165 const int peak = i - 1;
166 if (peak + q3Lags.front() >= 0) {
167 const double A = reco.at(peak);
168
169 // Local maximum + amplitude cut on the corrected waveform: a pulse
170 // riding on the tail of one already subtracted need not be a local
171 // maximum of the raw filter output at all.
172 if (A > m_filterThreshold && A > reco.at(peak - 1) && A > recoCurrent) {
173
174 auto responseVal = [&](int lag) {
175 const int k = lagZero + lag;
176 return (k >= 0 && k < responseSize) ? response[k] : 0.0;
177 };
178 // reco[] is final for the peak and everything before it; the sample
179 // being filtered is not stored yet, and no later lag can occur here.
180 auto recoVal = [&](int lag) {
181 return lag <= 0 ? reco.at(peak + lag) : recoCurrent;
182 };
183
184 double Q3 = 0.0;
185 for (int lag : q3Lags)
186 Q3 += std::abs(recoVal(lag) - A * responseVal(lag));
187
188 // Accept pulse and subtract its forward correction, if NPulse leaves
189 // room for it. Shape mismatch scales with the amplitude while the
190 // noise floor does not, so the cut carries one term of each: written
191 // as a product rather than a ratio to avoid dividing, and A>0 here for
192 // any sensible FilterThreshold.
193 if (Q3 < m_Q3Offset + m_Q3cut * A) {
194 const auto slot = std::ranges::find_if(
195 slotFreeAt, [i](int freeAt) { return freeAt <= i; });
196 if (slot == slotFreeAt.end()) {
197 ++m_nDropped;
198 } else {
199 // The pulse peaks one sample back, so its lag n lands on the entry
200 // for sample i+n-1, i.e. cache[lagZero+n-1]
201 for (int k = lagZero; k + 1 < responseSize; ++k)
202 cache[k] -= response[k + 1] * A;
203 *slot = i + correctionLength;
205 }
206 }
207 }
208 }
209
210 // Corrected filter output of this sample, now including any pulse just
211 // accepted one sample back
212 reco[i] = filtered + cache[lagZero];
213
214 // Advance correction cache in time
215 std::rotate(cache.begin(), cache.begin() + 1, cache.end());
216 cache.back() = 0.0;
217 }
218
219 // Corrected equivalent of the plain OF amplitude, which is the filter output
220 // of the window starting at firstSample
221 return reco[firstSample];
222}
223
225 ATH_CHECK(m_digitKey.initialize());
226 ATH_CHECK(m_rawChannelKey.initialize());
227 ATH_CHECK(m_pedestalKey.initialize());
228 ATH_CHECK(m_adc2MeVKey.initialize());
229 ATH_CHECK(m_ofcKey.initialize());
230 ATH_CHECK(m_shapeKey.initialize());
231 ATH_CHECK(m_cablingKey.initialize());
234
235 if (m_useDBFortQ) {
236 if (m_run1DSPThresholdsKey.empty() && m_run2DSPThresholdsKey.empty()) {
238 "useDB requested but neither Run1... nor Run2... initialized.");
239 return StatusCode::FAILURE;
240 }
241 }
242
243 ATH_CHECK(detStore()->retrieve(m_onlineId, "LArOnlineID"));
244
245 // The earliest testable candidate peak sits -q3Lags.front() samples into the
246 // digit, so anything less leaves no room to find a pulse before the in-time
247 // window and the forward correction can never contribute.
248 const int minFirstSample = -q3Lags.front() + 1;
249 if (m_firstSample < minFirstSample) {
250 ATH_MSG_ERROR("firstSample is "
251 << m_firstSample.value() << ", must be >= " << minFirstSample
252 << " for the OFFC to find any pulse before the in-time "
253 "window (set LAr.ROD.nPreceedingSamples accordingly)");
254 return StatusCode::FAILURE;
255 }
256
257 if (m_nPulse < 0) {
258 ATH_MSG_ERROR("NPulse is " << m_nPulse.value() << ", must be >= 0");
259 return StatusCode::FAILURE;
260 }
261 if (m_nPulse == 0) {
263 "NPulse is 0, no pulse will be subtracted and the OFFC reduces to "
264 "plain optimal filtering");
265 }
266
267 const std::string cutmsg = m_absECutFortQ.value() ? "fabs(E)" : "E";
268 if (m_useDBFortQ) {
269 ATH_MSG_INFO("Time and quality computed for "
270 << cutmsg << " above the threshold from COOL folder "
271 << m_run1DSPThresholdsKey.key() << " (run1) "
272 << m_run2DSPThresholdsKey.key() << " (run2)");
273 } else {
274 ATH_MSG_INFO("Time and quality computed for " << cutmsg << " above "
275 << m_eCutFortQ.value());
276 }
277
278 return StatusCode::SUCCESS;
279}
280
282 const unsigned long dropped = m_nDropped;
283 ATH_MSG_INFO("Subtracted " << m_nSubtracted.load() << " pulses, dropped "
284 << dropped << " for want of a free slot (NPulse = "
285 << m_nPulse.value() << ")");
286 if (dropped > 0)
287 ATH_MSG_WARNING(dropped
288 << " accepted pulses were not subtracted: their correction "
289 "is missing from the output. Raise NPulse to keep them");
290 return StatusCode::SUCCESS;
291}
292
293StatusCode LArOFFCRawChannelBuilder::execute(const EventContext& ctx) const {
294
295 ATH_MSG_VERBOSE("Executing LArOFFCRawChannelBuilder::execute");
296
297 // Get event inputs from read handles:
298 const LArDigitContainer* inputContainer{};
299 ATH_CHECK(SG::get(inputContainer, m_digitKey, ctx));
300
301 // Write output via write handle
302 auto outputContainer = std::make_unique<LArRawChannelContainer>();
303
304 // Get Conditions input
305 const ILArPedestal* peds{};
306 ATH_CHECK(SG::get(peds, m_pedestalKey, ctx));
307
308 const LArADC2MeV* adc2MeVs{};
309 ATH_CHECK(SG::get(adc2MeVs, m_adc2MeVKey, ctx));
310
311 const ILArOFC* ofcs{nullptr};
312 ATH_CHECK(SG::get(ofcs, m_ofcKey, ctx));
313
314 const ILArShape* shapes{};
315 ATH_CHECK(SG::get(shapes, m_shapeKey, ctx));
316
317 const LArOnOffIdMapping* cabling{};
318 ATH_CHECK(SG::get(cabling, m_cablingKey, ctx));
319
320 std::unique_ptr<LArDSPThresholdsFlat> run2DSPThresh;
321 const LArDSPThresholdsComplete* run1DSPThresh = nullptr;
322 ATH_CHECK(SG::get(run1DSPThresh, m_run1DSPThresholdsKey, ctx));
323 if (m_useDBFortQ) {
324 if (!m_run2DSPThresholdsKey.empty()) {
327 run2DSPThresh = std::make_unique<LArDSPThresholdsFlat>(*dspThrshAttr);
328 if (ATH_UNLIKELY(!run2DSPThresh->good())) {
330 "Failed to initialize LArDSPThresholdFlat from attribute list "
331 "loaded from "
332 << m_run2DSPThresholdsKey.key() << ". Aborting.");
333 return StatusCode::FAILURE;
334 }
335 } else if (!m_run1DSPThresholdsKey.empty()) {
338 run1DSPThresh = dspThresh.cptr();
339 } else {
340 ATH_MSG_ERROR("No DSP threshold configured.");
341 return StatusCode::FAILURE;
342 }
343 }
344
345 // Loop over digits:
346 for (const LArDigit* digit : *inputContainer) {
347
348 const size_t firstSample = m_firstSample;
349
350 const HWIdentifier id = digit->hardwareID();
351
352 const bool connected = cabling->isOnlineConnected(id);
353
354 const std::vector<short>& samples = digit->samples();
355 const int gain = digit->gain();
356 const float p = peds->pedestal(id, gain);
357
358 // The following autos will resolve either into vectors or vector-proxies
359 const auto& ofca = ofcs->OFC_a(id, gain);
360 const auto& adc2mev = adc2MeVs->ADC2MEV(id, gain);
361 const size_t nOFC = ofca.size();
362
363 if (ATH_UNLIKELY(nOFC == 0)) {
364 if (!connected)
365 continue; // No conditions for disconencted channel, who cares?
366 ATH_MSG_ERROR("No valid OFCs for connected channel "
367 << m_onlineId->channel_name(id) << " gain " << gain);
368 return StatusCode::FAILURE;
369 }
370
371 // Sanity check on input conditions data: ensure the samples vector is
372 // compatible with the ofc_a size when preceeding samples are saved.
373 // Compared this way round because samples.size()-firstSample would wrap
374 // for a short digit.
375 if (samples.size() < firstSample + nOFC) {
376 ATH_MSG_ERROR("digit has " << samples.size() << " samples, need at least "
377 << firstSample + nOFC << " for firstSample "
378 << firstSample << " and OFC_a size " << nOFC);
379 return StatusCode::FAILURE;
380 }
381
383 if (!connected)
384 continue; // No conditions for disconencted channel, who cares?
385 ATH_MSG_ERROR("No valid pedestal for connected channel "
386 << m_onlineId->channel_name(id) << " gain " << gain);
387 return StatusCode::FAILURE;
388 }
389
390 if (ATH_UNLIKELY(adc2mev.size() < 2)) {
391 if (!connected)
392 continue; // No conditions for disconencted channel, who cares?
393 ATH_MSG_ERROR("No valid ADC2MeV for connected channel "
394 << m_onlineId->channel_name(id) << " gain " << gain);
395 return StatusCode::FAILURE;
396 }
397
398 // Apply OFFC to get amplitude
399 // Evaluate sums in double-precision to get consistent results
400 // across platforms.
401
402 bool saturated = false;
403 // Check saturation AND discount pedestal
404 std::vector<double> samp_no_ped(nOFC, 0.0);
405 for (size_t i = 0; i < nOFC; ++i) {
406 if (samples[i + firstSample] == 4096 || samples[i + firstSample] == 0)
407 saturated = true;
408 samp_no_ped[i] = samples[i + firstSample] - p;
409 }
410
411 uint16_t iquaShort = 0;
412 float tau = 0;
413
414 uint16_t prov = LArProv::DEFAULTRECO; // Means all constants from DB
415 if (saturated)
416 prov |= LArProv::SATURATED;
417
418 float ecut(0.);
419 if (m_useDBFortQ) {
420 if (run2DSPThresh) {
421 ecut = run2DSPThresh->tQThr(id);
422 } else if (run1DSPThresh) {
423 ecut = run1DSPThresh->tQThr(id);
424 } else {
425 ATH_MSG_ERROR("DSP threshold problem");
426 return StatusCode::FAILURE;
427 }
428 } else {
429 ecut = m_eCutFortQ;
430 }
431
432 const auto& fullShape = shapes->Shape(id, gain);
433
434 double A = computeOFFC(samples, firstSample, ofca, fullShape, p);
435
436 const float E = adc2mev[0] + A * adc2mev[1];
437
438 const float E1 = m_absECutFortQ.value() ? std::fabs(E) : E;
439
440 if (E1 > ecut) {
441 ATH_MSG_VERBOSE("Channel " << m_onlineId->channel_name(id) << " gain "
442 << gain
443 << " above threshold for tQ computation");
444 prov |= LArProv::QTPRESENT; // time+quality information are available
445
446 // Get time by applying OFC-b coefficients:
447 const auto& ofcb = ofcs->OFC_b(id, gain);
448 double At = 0;
449 for (size_t i = 0; i < nOFC; ++i) {
450 At += static_cast<double>(samp_no_ped[i]) * ofcb[i];
451 }
452
453 // Divide A*t/A to get time
454 tau = (std::fabs(A) > 0.1) ? At / A : 0.0;
455
456 // Get Q-factor. The shape has to be offset by the index the OFC window
457 // is matched to, which is not the digit offset: the digitisation writes
458 // shape index k-nPreceedingSamples into digit sample k. Reading it back
459 // from the conditions also covers the HEC shift and the fallback that
460 // LArOFCCondAlg applies per channel.
461 const std::vector<double> resp = pulseResponse(fullShape, ofca);
462 const int shapeShift =
463 resp.empty() ? -1 : anchorIndex(resp) - static_cast<int>(nOFC) + 1;
464
465 if (ATH_UNLIKELY(shapeShift < 0 ||
466 fullShape.size() < nOFC + shapeShift)) {
467 if (!connected)
468 continue; // No conditions for disconnected channel, who cares?
469 ATH_MSG_ERROR("No valid shape for channel "
470 << m_onlineId->channel_name(id) << " gain " << gain);
471 ATH_MSG_ERROR("Got size " << fullShape.size() << " and offset "
472 << shapeShift << ", expected at least "
473 << nOFC << " samples from there");
474 return StatusCode::FAILURE;
475 }
476
477 std::span<const float> shape(fullShape.data() + shapeShift,
478 fullShape.size() - shapeShift);
479
480 double q = 0;
481 if (m_useShapeDer) {
482 const auto& fullshapeDer = shapes->ShapeDer(id, gain);
483 if (ATH_UNLIKELY(fullshapeDer.size() < nOFC + shapeShift)) {
484 ATH_MSG_ERROR("No valid shape derivative for channel "
485 << m_onlineId->channel_name(id) << " gain " << gain);
486 ATH_MSG_ERROR("Got size " << fullshapeDer.size()
487 << ", expected at least "
488 << nOFC + shapeShift);
489 return StatusCode::FAILURE;
490 }
491
492 std::span<const float> shapeDer(fullshapeDer.data() + shapeShift,
493 fullshapeDer.size() - shapeShift);
494
495
496 for (size_t i = 0; i < nOFC; ++i) {
497 q += std::pow((A * (shape[i] - tau * shapeDer[i]) - (samp_no_ped[i])),
498 2);
499 }
500 } // end if useShapeDer
501 else {
502 // Q-factor w/o shape derivative
503 for (size_t i = 0; i < nOFC; ++i) {
504 q += std::pow((A * shape[i] - (samp_no_ped[i])), 2);
505 }
506 }
507
508 // Clamp before the cast, q can exceed the range of int
509 iquaShort = static_cast<uint16_t>(std::min(q, 65535.0));
510
511 tau -= ofcs->timeOffset(id, gain);
512 tau *= (Gaudi::Units::nanosecond /
513 Gaudi::Units::picosecond); // Convert time to ps
514 } // end if above cut
515
516 outputContainer->emplace_back(id, static_cast<int>(std::floor(E + 0.5)),
517 static_cast<int>(std::floor(tau + 0.5)),
518 iquaShort, prov, (CaloGain::CaloGain)gain);
519 }
520
522 ATH_CHECK(outputHandle.record(std::move(outputContainer)));
523
524 return StatusCode::SUCCESS;
525}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_UNLIKELY(x)
MDT_Response response
Handle class for reading from StoreGate.
const ServiceHandle< StoreGateSvc > & detStore() const
virtual OFCRef_t OFC_b(const HWIdentifier &id, int gain, int tbin=0) const =0
virtual OFCRef_t OFC_a(const HWIdentifier &id, int gain, int tbin=0) const =0
access to OFCs by online ID, gain, and tbin (!=0 for testbeam)
LArVectorProxy OFCRef_t
This class defines the interface for accessing Optimal Filtering coefficients for each channel provid...
Definition ILArOFC.h:26
virtual float timeOffset(const HWIdentifier &CellID, int gain) const =0
virtual float pedestal(const HWIdentifier &id, int gain) const =0
LArVectorProxy ShapeRef_t
This class defines the interface for accessing Shape (Nsample variable, Dt = 25 ns fixed) @stereotype...
Definition ILArShape.h:26
virtual ShapeRef_t Shape(const HWIdentifier &id, int gain, int tbin=0, int mode=0) const =0
virtual ShapeRef_t ShapeDer(const HWIdentifier &id, int gain, int tbin=0, int mode=0) const =0
const LArVectorProxy ADC2MEV(const HWIdentifier &id, int gain) const
Definition LArADC2MeV.h:32
float tQThr(const HWIdentifier chid) const
Container class for LArDigit.
Liquid Argon digit base class.
Definition LArDigit.h:25
std::vector< double > pulseResponse(const ILArShape::ShapeRef_t &shape, const ILArOFC::OFCRef_t &ofc) const
Filter output for a unit-amplitude pulse, tabulated over every offset of the shape against the OFC wi...
SG::WriteHandleKey< LArRawChannelContainer > m_rawChannelKey
Gaudi::Property< bool > m_useDBFortQ
double computeOFFC(const std::vector< short > &samples, int firstSample, const ILArOFC::OFCRef_t &ofc, const ILArShape::ShapeRef_t &shape, double pedestal) const
SG::ReadCondHandleKey< ILArShape > m_shapeKey
Gaudi::Property< double > m_belowThreshold
The OFFC extends optimal filtering by finding pulses in the preceding samples and subtracting their e...
Gaudi::Property< bool > m_absECutFortQ
Gaudi::Property< int > m_nPulse
Maximum number of pulse corrections in flight at once.
SG::ReadCondHandleKey< LArADC2MeV > m_adc2MeVKey
Gaudi::Property< int > m_firstSample
Index of the digit sample the OFC window starts at, i.e.
SG::ReadCondHandleKey< LArOnOffIdMapping > m_cablingKey
SG::ReadCondHandleKey< ILArPedestal > m_pedestalKey
Gaudi::Property< float > m_eCutFortQ
SG::ReadHandleKey< LArDigitContainer > m_digitKey
Gaudi::Property< double > m_filterThreshold
Minimum pile-up corrected amplitude required to accept a pulse peak.
Gaudi::Property< double > m_Q3Offset
Absolute term of the Q3 cut, in ADC.
SG::ReadCondHandleKey< ILArOFC > m_ofcKey
Gaudi::Property< double > m_Q3cut
Quality cut for pulse acceptance.
Gaudi::Property< bool > m_useShapeDer
std::atomic< unsigned long > m_nDropped
StatusCode execute(const EventContext &ctx) const override
SG::ReadCondHandleKey< AthenaAttributeList > m_run2DSPThresholdsKey
SG::ReadCondHandleKey< LArDSPThresholdsComplete > m_run1DSPThresholdsKey
Gaudi::Property< int > m_belowTillReset
Number of consecutive below-threshold samples after which the pending corrections are dropped,...
std::atomic< unsigned long > m_nSubtracted
Accepted pulses, and those NPulse left no room to subtract.
const_pointer_type cptr()
StatusCode record(std::unique_ptr< T > data)
Record a const object to the store.
int r
Definition globals.cxx:22
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
void rotate(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > mid, typename DataModel_detail::iterator< DVL > end)
Specialization of rotate for DataVector/List.
hold the test vectors and ease the comparison