7#include "GaudiKernel/SystemOfUnits.h"
28constexpr std::array<int, 3> q3Lags{-2, -1, 1};
29static_assert(std::ranges::is_sorted(q3Lags) && q3Lags.back() <= 1);
34int anchorIndex(
const std::vector<double>&
response) {
45 const int shapeSize = shape.size();
46 const int ofcSize = ofc.size();
48 auto shapeVal = [&](
int k) {
49 return (k >= 0 && k < shapeSize) ? shape[k] : 0.0f;
55 std::vector<double>
response(std::max(0, shapeSize + ofcSize - 1), 0.0);
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];
70 double pedestal)
const {
78 const int nSamples = samples.size();
79 const int ofcLen = ofc.size();
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 "
92 std::vector<double> samp_no_ped(nSamples);
93 for (
int i = 0; i < nSamples; ++i)
94 samp_no_ped[i] = samples[i] - pedestal;
96 std::vector<double> reco(nSamples, 0.0);
102 const int responseSize =
response.size();
103 if (responseSize == 0) {
105 <<
" OFC size " << ofcLen);
113 const int lagZero = anchorIndex(
response);
115 ATH_MSG_WARNING(
"Pulse response is nowhere positive, cannot subtract");
122 const double peakResponse =
response[lagZero];
125 std::vector<double> cache(responseSize, 0.0);
129 const int correctionLength = responseSize - 2 - lagZero;
132 std::vector<int> slotFreeAt(
m_nPulse, 0);
133 int belowCounter = 0;
135 const int loopEnd = std::max(0, nSamples - ofcLen + 1);
137 for (
int i = 0; i < loopEnd; ++i) {
147 std::fill(cache.begin(), cache.end(), 0.0);
150 std::fill(slotFreeAt.begin(), slotFreeAt.end(), 0);
154 double filtered = 0.0;
155 for (
int j = 0; j < ofcLen; ++j)
156 filtered += samp_no_ped[i + j] * ofc[j];
160 const double recoCurrent = filtered + cache[lagZero];
165 const int peak = i - 1;
166 if (peak + q3Lags.front() >= 0) {
167 const double A = reco.at(peak);
174 auto responseVal = [&](
int lag) {
175 const int k = lagZero + lag;
176 return (k >= 0 && k < responseSize) ?
response[k] : 0.0;
180 auto recoVal = [&](
int lag) {
181 return lag <= 0 ? reco.at(peak + lag) : recoCurrent;
185 for (
int lag : q3Lags)
186 Q3 += std::abs(recoVal(lag) -
A * responseVal(lag));
194 const auto slot = std::ranges::find_if(
195 slotFreeAt, [i](
int freeAt) {
return freeAt <= i; });
196 if (slot == slotFreeAt.end()) {
201 for (
int k = lagZero; k + 1 < responseSize; ++k)
203 *slot = i + correctionLength;
212 reco[i] = filtered + cache[lagZero];
215 std::rotate(cache.begin(), cache.begin() + 1, cache.end());
221 return reco[firstSample];
238 "useDB requested but neither Run1... nor Run2... initialized.");
239 return StatusCode::FAILURE;
248 const int minFirstSample = -q3Lags.front() + 1;
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;
259 return StatusCode::FAILURE;
263 "NPulse is 0, no pulse will be subtracted and the OFFC reduces to "
264 "plain optimal filtering");
267 const std::string cutmsg =
m_absECutFortQ.value() ?
"fabs(E)" :
"E";
270 << cutmsg <<
" above the threshold from COOL folder "
274 ATH_MSG_INFO(
"Time and quality computed for " << cutmsg <<
" above "
278 return StatusCode::SUCCESS;
284 << dropped <<
" for want of a free slot (NPulse = "
288 <<
" accepted pulses were not subtracted: their correction "
289 "is missing from the output. Raise NPulse to keep them");
290 return StatusCode::SUCCESS;
302 auto outputContainer = std::make_unique<LArRawChannelContainer>();
320 std::unique_ptr<LArDSPThresholdsFlat> run2DSPThresh;
327 run2DSPThresh = std::make_unique<LArDSPThresholdsFlat>(*dspThrshAttr);
330 "Failed to initialize LArDSPThresholdFlat from attribute list "
333 return StatusCode::FAILURE;
338 run1DSPThresh = dspThresh.
cptr();
341 return StatusCode::FAILURE;
346 for (
const LArDigit* digit : *inputContainer) {
352 const bool connected = cabling->isOnlineConnected(
id);
354 const std::vector<short>& samples = digit->samples();
355 const int gain = digit->gain();
356 const float p = peds->
pedestal(
id, gain);
359 const auto& ofca = ofcs->
OFC_a(
id, gain);
360 const auto& adc2mev = adc2MeVs->
ADC2MEV(
id, gain);
361 const size_t nOFC = ofca.size();
367 <<
m_onlineId->channel_name(
id) <<
" gain " << gain);
368 return StatusCode::FAILURE;
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;
386 <<
m_onlineId->channel_name(
id) <<
" gain " << gain);
387 return StatusCode::FAILURE;
394 <<
m_onlineId->channel_name(
id) <<
" gain " << gain);
395 return StatusCode::FAILURE;
402 bool saturated =
false;
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)
408 samp_no_ped[i] = samples[i + firstSample] - p;
411 uint16_t iquaShort = 0;
421 ecut = run2DSPThresh->tQThr(
id);
422 }
else if (run1DSPThresh) {
423 ecut = run1DSPThresh->
tQThr(
id);
426 return StatusCode::FAILURE;
432 const auto& fullShape = shapes->
Shape(
id, gain);
434 double A =
computeOFFC(samples, firstSample, ofca, fullShape, p);
436 const float E = adc2mev[0] +
A * adc2mev[1];
443 <<
" above threshold for tQ computation");
447 const auto& ofcb = ofcs->
OFC_b(
id, gain);
449 for (
size_t i = 0; i < nOFC; ++i) {
450 At +=
static_cast<double>(samp_no_ped[i]) * ofcb[i];
454 tau = (std::fabs(
A) > 0.1) ? At /
A : 0.0;
461 const std::vector<double> resp =
pulseResponse(fullShape, ofca);
462 const int shapeShift =
463 resp.empty() ? -1 : anchorIndex(resp) -
static_cast<int>(nOFC) + 1;
466 fullShape.size() < nOFC + shapeShift)) {
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;
477 std::span<const float> shape(fullShape.data() + shapeShift,
478 fullShape.size() - shapeShift);
482 const auto& fullshapeDer = shapes->
ShapeDer(
id, gain);
483 if (
ATH_UNLIKELY(fullshapeDer.size() < nOFC + shapeShift)) {
485 <<
m_onlineId->channel_name(
id) <<
" gain " << gain);
487 <<
", expected at least "
488 << nOFC + shapeShift);
489 return StatusCode::FAILURE;
492 std::span<const float> shapeDer(fullshapeDer.data() + shapeShift,
493 fullshapeDer.size() - shapeShift);
496 for (
size_t i = 0; i < nOFC; ++i) {
497 q += std::pow((
A * (shape[i] - tau * shapeDer[i]) - (samp_no_ped[i])),
503 for (
size_t i = 0; i < nOFC; ++i) {
504 q += std::pow((
A * shape[i] - (samp_no_ped[i])), 2);
509 iquaShort =
static_cast<uint16_t
>(std::min(q, 65535.0));
512 tau *= (Gaudi::Units::nanosecond /
513 Gaudi::Units::picosecond);
516 outputContainer->emplace_back(
id,
static_cast<int>(std::floor(E + 0.5)),
517 static_cast<int>(std::floor(tau + 0.5)),
524 return StatusCode::SUCCESS;
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
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...
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...
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
float tQThr(const HWIdentifier chid) const
Container class for LArDigit.
Liquid Argon digit base class.
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
StatusCode initialize() override
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
const LArOnlineID * m_onlineId
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.
StatusCode finalize() override
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.
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