7#include "GaudiKernel/SystemOfUnits.h"
29 const int shapeSize = shape.size();
30 const int ofcSize = ofc.size();
32 std::vector<double> ofcRev(ofcSize);
33 for (
int i = 0; i < ofcSize; ++i)
34 ofcRev[i] = ofc[ofcSize - 1 - i];
36 const int fullLen = shapeSize + ofcSize - 1;
37 std::vector<double> fullConv(fullLen, 0.0);
39 for (
int i = 0; i < shapeSize; ++i)
40 for (
int j = 0; j < ofcSize; ++j)
41 fullConv[i + j] += shape[i] * ofcRev[j];
44 const int startIndex = 2;
45 if (startIndex >= fullLen)
48 return std::vector<double>(fullConv.begin() + startIndex, fullConv.end());
55 double pedestal)
const {
63 const int nSamples = samples.size();
64 const int ofcLen = ofc.size();
67 std::vector<double> samp_no_ped(nSamples);
68 for (
int i = 0; i < nSamples; ++i)
69 samp_no_ped[i] = samples[i] - pedestal;
71 std::vector<double> filtered(nSamples, 0.0);
72 std::vector<double> reco(nSamples, 0.0);
76 const int convSize = convPulse.size();
77 std::vector<double> cache(convSize, 0.0);
80 auto it = std::ranges::max_element(shape);
81 int shapemax = std::distance(shape.begin(), it);
84 std::vector<int> context(
m_nPulse, 0);
87 const int loopEnd = std::max(0, nSamples - ofcLen);
89 for (
int i = 0; i < loopEnd; ++i) {
95 std::fill(cache.begin(), cache.end(), 0.0);
102 for (
int j = 0; j < ofcLen; ++j)
103 filtered[i] += samp_no_ped[i + j] * ofc[j];
107 reco[i - 1] = filtered[i - 1] + cache[2];
108 const double A = reco[i - 1];
112 filtered[i - 1] > filtered[i]) {
115 auto shapeVal = [&](
int k) {
116 return (k >= 0 && k < (
int)shape.size()) ? shape[k] : 0.0;
120 Q3 += std::abs(filtered[i - 2] -
A * shapeVal(shapemax - 1));
121 Q3 += std::abs(filtered[i - 1] -
A * shapeVal(shapemax));
122 Q3 += std::abs(filtered[i] -
A * shapeVal(shapemax + 1));
123 Q3 += std::abs(filtered[i - 3] -
A * shapeVal(shapemax - 2));
127 for (
int& c : context) {
129 for (
int k = 0; k < convSize; ++k)
130 cache[k] -= convPulse[k] *
A;
140 std::rotate(cache.begin(), cache.begin() + 1, cache.end());
143 for (
int& c : context)
149 return reco[firstSample + 1];
166 "useDB requested but neither Run1... nor Run2... initialized.");
167 return StatusCode::FAILURE;
174 ATH_MSG_ERROR(
"firstSample must be >= 5 to allow for OFFC processing");
175 return StatusCode::FAILURE;
178 const std::string cutmsg =
m_absECutFortQ.value() ?
" fabs(E) < " :
" E < ";
179 ATH_MSG_INFO(
"Energy cut for time and quality computation: "
180 << cutmsg <<
" taken from COOL folder "
184 return StatusCode::SUCCESS;
196 auto outputContainer = std::make_unique<LArRawChannelContainer>();
214 std::unique_ptr<LArDSPThresholdsFlat> run2DSPThresh;
221 run2DSPThresh = std::make_unique<LArDSPThresholdsFlat>(*dspThrshAttr);
224 "Failed to initialize LArDSPThresholdFlat from attribute list "
227 return StatusCode::FAILURE;
232 run1DSPThresh = dspThresh.
cptr();
235 return StatusCode::FAILURE;
240 for (
const LArDigit* digit : *inputContainer) {
246 const bool connected = cabling->isOnlineConnected(
id);
248 const std::vector<short>& samples = digit->samples();
249 const int gain = digit->gain();
250 const float p = peds->
pedestal(
id, gain);
253 const auto& ofca = ofcs->
OFC_a(
id, gain);
254 const auto& adc2mev = adc2MeVs->
ADC2MEV(
id, gain);
255 const size_t nOFC = ofca.size();
260 const size_t nSamples = samples.size() - firstSample;
261 if (nSamples < nOFC) {
263 << nSamples <<
", must be >= OFC_a size: " << ofca.size());
264 return StatusCode::FAILURE;
271 <<
m_onlineId->channel_name(
id) <<
" gain " << gain);
272 return StatusCode::FAILURE;
279 <<
m_onlineId->channel_name(
id) <<
" gain " << gain);
280 return StatusCode::FAILURE;
287 bool saturated =
false;
289 std::vector<double> samp_no_ped(nOFC, 0.0);
290 for (
size_t i = 0; i < nOFC; ++i) {
291 if (samples[i + firstSample] == 4096 || samples[i + firstSample] == 0)
293 samp_no_ped[i] = samples[i + firstSample] - p;
296 uint16_t iquaShort = 0;
299 uint16_t prov = 0xa5;
306 ecut = run2DSPThresh->tQThr(
id);
307 }
else if (run1DSPThresh) {
308 ecut = run1DSPThresh->
tQThr(
id);
311 return StatusCode::FAILURE;
317 const auto& fullShape = shapes->
Shape(
id, gain);
319 double A =
computeOFFC(samples, firstSample, ofca, fullShape, p);
321 const float E = adc2mev[0] +
A * adc2mev[1];
328 <<
" above threshold for tQ computation");
333 const auto& ofcb = ofcs->
OFC_b(
id, gain);
335 for (
size_t i = 0; i < nOFC; ++i) {
336 At +=
static_cast<double>(samp_no_ped[i]) * ofcb[i];
342 tau = (std::fabs(
A) > 0.1) ? At /
A : 0.0;
347 const size_t nSamples = samples.size();
348 if (fullShape.size() > nSamples && nSamples == 4 &&
m_firstSample == 0) {
354 if (
ATH_UNLIKELY(fullShape.size() < nOFC + firstSample)) {
358 <<
m_onlineId->channel_name(
id) <<
" gain " << gain);
359 ATH_MSG_ERROR(
"Got size " << fullShape.size() <<
", expected at least "
360 << nSamples + firstSample);
361 return StatusCode::FAILURE;
364 std::span<const float> shape(fullShape.data() + firstSample, fullShape.size() - firstSample);
368 const auto& fullshapeDer = shapes->
ShapeDer(
id, gain);
369 if (
ATH_UNLIKELY(fullshapeDer.size() < nOFC + firstSample)) {
371 <<
m_onlineId->channel_name(
id) <<
" gain " << gain);
373 <<
", expected at least "
374 << nOFC + firstSample);
375 return StatusCode::FAILURE;
378 std::span<const float> shapeDer(fullshapeDer.data() + firstSample, fullshapeDer.size() - firstSample);
381 for (
size_t i = 0; i < nOFC; ++i) {
382 q += std::pow((
A * (shape[i] - tau * shapeDer[i]) - (samp_no_ped[i])),
388 for (
size_t i = 0; i < nOFC; ++i) {
389 q += std::pow((
A * shape[i] - (samp_no_ped[i])), 2);
393 int iqua = std::min(
static_cast<int>(q), 0xFFFF);
395 iquaShort =
static_cast<uint16_t
>(iqua & 0xFFFF);
398 tau *= (Gaudi::Units::nanosecond /
399 Gaudi::Units::picosecond);
402 outputContainer->emplace_back(
id,
static_cast<int>(std::floor(E + 0.5)),
403 static_cast<int>(std::floor(tau + 0.5)),
410 return StatusCode::SUCCESS;
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_VERBOSE(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.
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 algorithm extends the standard optimal filtering by identifying in-time pulses and subtracti...
Gaudi::Property< bool > m_absECutFortQ
Gaudi::Property< int > m_nPulse
Maximum number of overlapping pulses that can be tracked simultaneously.
SG::ReadCondHandleKey< LArADC2MeV > m_adc2MeVKey
Gaudi::Property< int > m_firstSample
SG::ReadCondHandleKey< LArOnOffIdMapping > m_cablingKey
std::vector< double > convolvePulse(const ILArShape::ShapeRef_t &shape, const ILArOFC::OFCRef_t &ofc) const
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 filtered amplitude required to consider a sample as a pulse peak.
SG::ReadCondHandleKey< ILArOFC > m_ofcKey
Gaudi::Property< double > m_Q3cut
Quality cut used for pulse acceptance.
Gaudi::Property< bool > m_useShapeDer
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 required before the forward-subtraction cache is reset.
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