ATLAS Offline Software
Loading...
Searching...
No Matches
LArRODBCIDCorrAlg.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
9
10#include "LArRODBCIDCorrAlg.h"
11
18
19#include <algorithm>
20#include <cmath>
21#include <limits>
22#include <memory>
23
24namespace {
25
29constexpr float xlumiMC = 0.158478605f;
30
33constexpr float minBunchIntensity = 0.001f;
34
36constexpr float minAdc2MeVSlope = 1e-9f;
37
39constexpr size_t hecShiftNSamples = 4;
40
41} // anonymous namespace
42
44{
45 ATH_CHECK( m_digitKey.initialize() );
46 ATH_CHECK( m_digitCorrKey.initialize() );
47 ATH_CHECK( m_adc2MeVKey.initialize() );
48 ATH_CHECK( m_cablingKey.initialize() );
49 ATH_CHECK( m_shapeKey.initialize() );
50 ATH_CHECK( m_minBiasAvgKey.initialize() );
51 ATH_CHECK( m_eventInfoKey.initialize() );
52
53 ATH_CHECK( detStore()->retrieve (m_onlineId, "LArOnlineID") );
54
56 ATH_MSG_ERROR( "MinBunchCrossing (" << m_minBunchCrossing
57 << ") is larger than MaxBunchCrossing ("
58 << m_maxBunchCrossing << ")" );
59 return StatusCode::FAILURE;
60 }
61
63
64 ATH_MSG_INFO( "firstSample = " << m_firstSample
65 << ", so digit sample 0 corresponds to bunch crossing "
66 << m_firstSample << " relative to the triggered one" );
67
68 return StatusCode::SUCCESS;
69}
70
71StatusCode LArRODBCIDCorrAlg::execute (const EventContext& ctx) const
72{
73 ATH_MSG_DEBUG( "Executing LArRODBCIDCorrAlg" );
74
76 if (!digitHandle.isValid()) {
77 ATH_MSG_ERROR( "Failed to retrieve LArDigitContainer "
78 << m_digitKey.key() );
79 return StatusCode::FAILURE;
80 }
81
83 if (!cablingHdl.isValid()) {
84 ATH_MSG_ERROR( "Invalid cabling conditions handle" );
85 return StatusCode::FAILURE;
86 }
87 const LArOnOffIdMapping* cabling = *cablingHdl;
88
90 if (!shapesHdl.isValid()) {
91 ATH_MSG_ERROR( "Invalid shape conditions handle" );
92 return StatusCode::FAILURE;
93 }
94 const ILArShape* shapes = *shapesHdl;
95
97 if (!adc2MeVHdl.isValid()) {
98 ATH_MSG_ERROR( "Invalid ADC2MeV conditions handle" );
99 return StatusCode::FAILURE;
100 }
101 const LArADC2MeV* adc2MeVs = *adc2MeVHdl;
102
104 if (!minBiasAvgHdl.isValid()) {
105 ATH_MSG_ERROR( "Invalid MinBiasAverage conditions handle" );
106 return StatusCode::FAILURE;
107 }
108 const ILArMinBiasAverage* minBiasAvg = minBiasAvgHdl.cptr();
109
111 if (!eventInfo.isValid()) {
112 ATH_MSG_ERROR( "Failed to retrieve EventInfo" );
113 return StatusCode::FAILURE;
114 }
115
116 const int bcid = static_cast<int> (eventInfo->bcid());
117 if (bcid < 0 || bcid >= s_nBCID) {
118 ATH_MSG_ERROR( "BCID " << bcid << " is outside [0, " << s_nBCID << ")" );
119 return StatusCode::FAILURE;
120 }
121 const float intperBC = eventInfo->averageInteractionsPerCrossing();
122
124 auto output = std::make_unique<LArDigitContainer> (SG::OWN_ELEMENTS);
125 LArDigitContainer* outputPtr = output.get();
126 outputPtr->reserve (digitHandle->size());
127 ATH_CHECK( outHandle.record (std::move (output)) );
128
129 // Declared outside the loop so that the buffers are reused across channels.
130 std::vector<float> pileupADC;
131 std::vector<short> correctedSamples;
132 size_t nClamped = 0;
133
134 for (const LArDigit* digit : *digitHandle) {
135
136 const HWIdentifier id = digit->hardwareID();
137 const CaloGain::CaloGain gain = digit->gain();
138 const std::vector<short>& rawSamples = digit->samples();
139
140 float slope = 0;
141 float mbEnergy = 0;
142 ILArShape::ShapeRef_t pulseShape;
143 bool correctable = cabling->isOnlineConnected (id) && !rawSamples.empty();
144 if (correctable) {
145 const auto& adc2mev = adc2MeVs->ADC2MEV (id, gain);
146 correctable = adc2mev.size() >= 2 &&
147 std::abs (adc2mev[1]) > minAdc2MeVSlope;
148 if (correctable) {
149 slope = adc2mev[1];
150 pulseShape = shapes->Shape (id, gain);
151 correctable = !pulseShape.empty();
152 }
153 if (correctable) {
154 // A non-positive average means no correction, as in CaloBCIDCoeffs.
155 // This also catches ILArMinBiasAverage::ERRORCODE.
156 mbEnergy = minBiasAvg->minBiasAverage (id);
157 correctable = mbEnergy > 0;
158 }
159 }
160
161 // Channels for which no correction can be computed are copied through
162 // unchanged, so that the output container always mirrors the input.
163 if (!correctable) {
164 outputPtr->push_back (std::make_unique<LArDigit> (id, gain, rawSamples));
165 continue;
166 }
167
168 // LArHitEMapToDigitAlg shifts the HEC shape by one sample in the four
169 // sample readout, so reproduce that here to stay aligned with the digits.
170 const bool hecShift = rawSamples.size() == hecShiftNSamples &&
171 m_firstSample == 0 &&
172 m_onlineId->isHECchannel (id);
173
174 pileupADC.assign (rawSamples.size(), 0);
175 computePileupADCCorrection (mbEnergy, pulseShape,
176 bcid, slope, intperBC, hecShift, pileupADC);
177
178 // The corrected value is clamped to the range of short rather than to the
179 // ADC range -> the raw channel builders treat samples of 0 and 4096 as
180 // saturation markers, so clipping there would flag saturation spuriously.
181
182 correctedSamples.resize (rawSamples.size());
183 for (size_t i = 0; i < rawSamples.size(); ++i) {
184 const long corrected =
185 std::lround (static_cast<double> (rawSamples[i]) -
186 static_cast<double> (pileupADC[i]));
187 const long clamped =
188 std::clamp (corrected,
189 static_cast<long> (std::numeric_limits<short>::lowest()),
190 static_cast<long> (std::numeric_limits<short>::max()));
191 if (clamped != corrected) {
192 ++nClamped;
193 }
194 correctedSamples[i] = static_cast<short> (clamped);
195 }
196
197 outputPtr->push_back
198 (std::make_unique<LArDigit> (id, gain, correctedSamples));
199 }
200
201 if (nClamped > 0) {
202 ATH_MSG_WARNING( nClamped << " corrected sample(s) fell outside the range "
203 "of short and were clamped" );
204 }
205
206 ATH_MSG_DEBUG( "Wrote " << outputPtr->size() << " digits to "
207 << m_digitCorrKey.key() );
208
209 return StatusCode::SUCCESS;
210}
211
213{
214 const std::vector<float>& pattern = m_beamIntensityPattern;
215 if (pattern.empty()) {
216 ATH_MSG_ERROR( "BeamIntensityPattern is empty. It must be set from "
217 "flags.Digitization.PU.BeamIntensityPattern." );
218 return StatusCode::FAILURE;
219 }
220
221 const int nPattern = static_cast<int> (pattern.size());
222 m_lumi.assign (s_nBCID, 0);
223
224 // Reproduce BunchCrossingCondAlg, so that the set of filled bunch crossings
225 // used here is identical to the one in BunchCrossingCondData, which is what
226 // the energy level correction uses.
227 if (s_nBCID % nPattern != 0) {
228 ATH_MSG_INFO( "Bunch pattern of length " << nPattern
229 << " does not fit into " << s_nBCID );
230 // As in BunchCrossingCondAlg, the loop deliberately stops short of the
231 // half orbit so as not to produce an odd pattern half way round.
232 const int nGuard = 20;
233 for (int i = 0; i < s_nBCID / 2 - nGuard; ++i) {
234 const int pos1 = i % nPattern;
235 const int pos2 = nPattern - 1 - (i % nPattern);
236 if (pattern[pos1] > minBunchIntensity) {
237 m_lumi[i] = xlumiMC;
238 }
239 if (pattern[pos2] > minBunchIntensity) {
240 m_lumi[s_nBCID - 1 - i] = xlumiMC;
241 }
242 }
243 }
244 else {
245 ATH_MSG_INFO( "Bunch pattern of length " << nPattern << " fits into "
246 << s_nBCID );
247 for (int i = 0; i < s_nBCID; ++i) {
248 if (pattern[i % nPattern] > minBunchIntensity) {
249 m_lumi[i] = xlumiMC;
250 }
251 }
252 }
253
254 const int nFilled = std::count_if (m_lumi.begin(), m_lumi.end(),
255 [] (float lumi) { return lumi > 0; });
256 ATH_MSG_INFO( "Filled " << nFilled << " of " << s_nBCID
257 << " bunch crossings" );
258 if (nFilled == 0) {
259 ATH_MSG_ERROR( "No filled bunch crossing found in the beam intensity "
260 "pattern, so the correction would be identically zero." );
261 return StatusCode::FAILURE;
262 }
263
264 return StatusCode::SUCCESS;
265}
266
267void
269 const ILArShape::ShapeRef_t& pulseShape,
270 int bcid,
271 float adc2MeVSlope,
272 float intperBC,
273 bool hecShift,
274 std::vector<float>& pileupADC) const
275{
276 const int nShape = static_cast<int> (pulseShape.size());
277 const int nSamples = static_cast<int> (pileupADC.size());
278 const int firstSample = m_firstSample + (hecShift ? 1 : 0);
279
280 for (int i = 0; i < nSamples; ++i) {
281 // Digit sample i is fed by a pulse from bunch crossing bc through shape
282 // index j, with j = i - bc + firstSample.
283 double sum = 0;
284 for (int j = 0; j < nShape; ++j) {
285 const int bc = i + firstSample - j;
286 if (bc < m_minBunchCrossing || bc > m_maxBunchCrossing) {
287 continue;
288 }
289 int idx = (bcid + bc) % s_nBCID;
290 if (idx < 0) {
291 idx += s_nBCID;
292 }
293 sum += static_cast<double> (m_lumi[idx]) * pulseShape[j];
294 }
295 pileupADC[i] =
296 static_cast<float> (mbEnergy * sum * intperBC / adc2MeVSlope);
297 }
298}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
Subtract the BCID-dependent pile-up offset from the LArDigit samples before digital filterings.
Handle class for reading from StoreGate.
Handle class for recording to StoreGate.
const ServiceHandle< StoreGateSvc > & detStore() const
void reserve(size_type n)
Attempt to preallocate enough memory for a specified number of elements.
value_type push_back(value_type pElem)
Add an element to the end of the collection.
size_type size() const noexcept
Returns the number of elements in the collection.
virtual const float & minBiasAverage(const HWIdentifier &id) const =0
access to average of E in minimum bias events index by Identifier
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
const LArVectorProxy ADC2MEV(const HWIdentifier &id, int gain) const
Definition LArADC2MeV.h:32
Container class for LArDigit.
Liquid Argon digit base class.
Definition LArDigit.h:25
void computePileupADCCorrection(float mbEnergy, const ILArShape::ShapeRef_t &pulseShape, int bcid, float adc2MeVSlope, float intperBC, bool hecShift, std::vector< float > &pileupADC) const
Compute the mean pile-up ADC offset per sample for one channel.
Gaudi::Property< int > m_minBunchCrossing
SG::ReadCondHandleKey< LArADC2MeV > m_adc2MeVKey
SG::ReadCondHandleKey< ILArMinBiasAverage > m_minBiasAvgKey
SG::ReadHandleKey< xAOD::EventInfo > m_eventInfoKey
SG::ReadCondHandleKey< LArOnOffIdMapping > m_cablingKey
virtual StatusCode initialize() override
std::vector< float > m_lumi
Per-BCID luminosity, indexed by absolute BCID in [0, s_nBCID).
virtual StatusCode execute(const EventContext &ctx) const override
SG::ReadCondHandleKey< ILArShape > m_shapeKey
const LArOnlineID * m_onlineId
Online identifier helper, used to recognise HEC channels.
static constexpr int s_nBCID
Number of bunch crossings in one LHC orbit.
Gaudi::Property< int > m_firstSample
Gaudi::Property< int > m_maxBunchCrossing
SG::WriteHandleKey< LArDigitContainer > m_digitCorrKey
StatusCode fillLumi()
Fill m_lumi from the beam intensity pattern.
SG::ReadHandleKey< LArDigitContainer > m_digitKey
Gaudi::Property< std::vector< float > > m_beamIntensityPattern
const_pointer_type cptr()
virtual bool isValid() override final
Can the handle be successfully dereferenced?
StatusCode record(std::unique_ptr< T > data)
Record a const object to the store.
@ OWN_ELEMENTS
this data object owns its elements