ATLAS Offline Software
Loading...
Searching...
No Matches
LISAnalysisTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
5#include <sstream>
15#include "TFile.h"
16#include "TString.h"
17#include "TH1D.h"
18
19namespace ZDC {
20
21LISAnalysisTool::LISAnalysisTool(std::string const& name) :
22 asg::AsgTool(name),
23 m_name(name)
24{
25#ifndef XAOD_STANDALONE
26 declareInterface<IZdcAnalysisTool>(this);
27#endif
28}
29
31 ATH_MSG_INFO("Initializing LISAnalysisTool with configuration: " << m_configuration);
32 ATH_MSG_INFO("Initializing LISAnalysisTool with BaselineStart: " << m_nBaselineStart << ", BaselineEnd: " << m_nBaselineEnd);
33 ATH_MSG_INFO("Initializing LISAnalysisTool with PulseStart: " << m_nPulsStart << ", PulseEnd: " << m_nPulsEnd);
34
35 // Format channel pedestals for printing
36 std::ostringstream pedestalStr;
37 pedestalStr << "[";
38 for (size_t i = 0; i < m_channelPedestals.value().size(); ++i) {
39 if (i > 0) pedestalStr << ", ";
40 pedestalStr << m_channelPedestals.value()[i];
41 }
42 pedestalStr << "]";
43 ATH_MSG_INFO("Initializing LISAnalysisTool with Pedestals: " << pedestalStr.str());
44
45
47
48 ATH_MSG_INFO("LIS Configuration:");
49 ATH_MSG_INFO(" NumSamples: " << m_numSamples);
50 ATH_MSG_INFO(" Presample: " << m_preSample);
51 ATH_MSG_INFO(" DeltaTSample: " << m_deltaTSample << " ns");
52 ATH_MSG_INFO(" SampleAnaStart: " << m_sampleAnaStart);
53 ATH_MSG_INFO(" SampleAnaEnd: " << m_sampleAnaEnd);
54
55 // If an aux suffix is provided, prepend it with "_"
56 std::string auxSuffix = m_auxSuffix;
57 if (auxSuffix != "") auxSuffix = "_" + auxSuffix;
58
59 // Initialize eventInfo access
60 ATH_CHECK(m_eventInfoKey.initialize());
61
62 // Initialize keys for reading ZDC event-level aux decor information
64 ATH_CHECK(m_eventTypeKey.initialize());
65
67 ATH_CHECK(m_DAQModeKey.initialize());
68
70 ATH_CHECK(m_robBCIDKey.initialize());
71
72 // Initialize WriteDecor handles
73 if (m_writeAux) {
74 m_ZdcLEDType = m_zdcSumContainerName + ".ZdcLEDType" + auxSuffix;
75 ATH_CHECK(m_ZdcLEDType.initialize());
76
77 m_LISPresampleADC = m_zdcModuleContainerName + ".LISPresample" + auxSuffix;
78 ATH_CHECK(m_LISPresampleADC.initialize());
79
80 m_LISADCSum = m_zdcModuleContainerName + ".LISADCSum" + auxSuffix;
81 ATH_CHECK(m_LISADCSum.initialize());
82
83 m_LISMaxADC = m_zdcModuleContainerName + ".LISMaxADC" + auxSuffix;
84 ATH_CHECK(m_LISMaxADC.initialize());
85
86 m_LISMaxSample = m_zdcModuleContainerName + ".LISMaxSample" + auxSuffix;
87 ATH_CHECK(m_LISMaxSample.initialize());
88
89 m_LISAvgTime = m_zdcModuleContainerName + ".LISAvgTime" + auxSuffix;
90 ATH_CHECK(m_LISAvgTime.initialize());
91
92 m_LISModuleStatus = m_zdcModuleContainerName + ".LISModuleStatus" + auxSuffix;
93 ATH_CHECK(m_LISModuleStatus.initialize());
94
95 }
96
97 m_init = true;
98 ATH_MSG_INFO("LISAnalysisTool initialization complete");
99
100 return StatusCode::SUCCESS;
101}
102
104 // PbPb 2025 configuration
106 m_preSample = 0;
107 m_deltaTSample = 3.125;
109 m_sampleAnaEnd = 23;
111
112 m_LEDBCID = {3476, 3479, 3482};
113 m_LEDCalreqIdx = {0 ,1 ,2};
114}
115
116StatusCode LISAnalysisTool::SetPedestals(unsigned int runNumber)
117{
118 std::string filename;
119 std::string runString;
120
121 runString = ("ZdcCalib_Run"+TString::Itoa(runNumber,10)+".root").Data();
122
123 filename = PathResolverFindCalibFile("ZdcAnalysis/" + runString );
124
125 if (filename.empty())
126 {
127 ATH_MSG_INFO("No LIS pedestals file found - using default pedestals");
128 return StatusCode::FAILURE;
129 }
130
131 ATH_MSG_INFO("Opening LIS pedestals file " << filename);
132 std::unique_ptr<TFile> fLISPed(TFile::Open(filename.c_str(), "READ"));
133
134 if (!fLISPed->IsOpen()) {
135 ATH_MSG_INFO ("SetPedestals: failed to open file: " << filename << ". Using default pedestals.");
136 return StatusCode::FAILURE;
137 }
138
139 // Read 8 TH1D histograms (one for each channel/module)
140 // Each histogram contains pedestal value (y-axis) vs lumiblock (x-axis)
141 //
142 bool readSuccess = true;
143
144 for (int channel = 0; channel < m_nLISChannels; channel++) {
145 std::string histName = "LIS_Pedestal_ch" + std::to_string(channel);
146
147 ATH_MSG_DEBUG("SetPedestals: Searching for histogram: " << histName);
148
149 TH1D* hist_ptr = static_cast<TH1D*>(fLISPed->GetObjectChecked(histName.c_str(), "TH1D"));
150
151 if (!hist_ptr) {
152 ATH_MSG_ERROR("SetPedestals: unable to read pedestal histogram " << histName);
153 readSuccess = false;
154 break;
155 }
156 else {
157 ATH_MSG_INFO("Successfully read pedestal histogram for channel " << channel);
158 // Store the histogram in member variable
159 hist_ptr->SetDirectory(0);
160 m_LISPedestals[channel].reset(hist_ptr);
161 }
162 }
163
164 fLISPed->Close();
165
166 if (readSuccess) {
167 ATH_MSG_INFO("Successfully loaded LIS pedestals from file");
168 }
169 else {
170 ATH_MSG_ERROR("SetPedestals: due to at least one error, LIS pedestals not loaded from file");
171 }
172
173 return readSuccess ? StatusCode::SUCCESS : StatusCode::FAILURE;
174}
175
176float LISAnalysisTool::GetPedestal(int channel, unsigned int lumiBlock) {
177 // Check if channel is valid
178 if (channel >= m_nLISChannels) {
179 ATH_MSG_WARNING("GetPedestals: invalid channel " << channel << ", returning 0");
180 return 0;
181 }
182
183 ATH_MSG_DEBUG("GetPedestals: m_MominalPedestals " << m_MominalPedestals );
184
185 if(!m_MominalPedestals) {
186 // Check if histogram is available for this channel
187 if (m_LISPedestals[channel]) {
188 // Find the bin corresponding to this lumiblock
189
190 if(lumiBlock < m_LISPedestals[channel]->GetXaxis()->GetXmin() || lumiBlock > m_LISPedestals[channel]->GetXaxis()->GetXmax())
191 {
192 ATH_MSG_DEBUG("GetPedestals: channel " << channel << ", LB " << lumiBlock
193 << ", lumiblock out of histogram range");
194 }
195 else{
196
197 int bin = m_LISPedestals[channel]->FindBin(lumiBlock);
198 float pedestal = m_LISPedestals[channel]->GetBinContent(bin);
199
200 // Check if pedestal value is valid (non-zero)
201 if (pedestal > 0) {
202 ATH_MSG_DEBUG("GetPedestals: channel " << channel << ", LB " << lumiBlock
203 << ", pedestal = " << pedestal << " (from histogram)");
204 return pedestal;
205 }
206 else {
207 ATH_MSG_DEBUG("GetPedestals: channel " << channel << ", LB " << lumiBlock
208 << ", no valid histogram entry");
209 }
210 }
211 }
212 else {
213 ATH_MSG_DEBUG("GetPedestals: no histogram available for channel " << channel);
214 }
215 }
216
217 // Fallback to predefined pedestals
218 ATH_MSG_DEBUG("GetPedestals: channel " << channel << ", LB " << lumiBlock
219 << ", using default pedestal = " << m_channelPedestals.value()[channel]);
220
221 return m_channelPedestals.value()[channel];
222}
223
224void LISAnalysisTool::setStatusBit(unsigned int& statusWord, unsigned int bitIndex) {
225 statusWord |= (1 << bitIndex);
226}
227
229 int side, int channel,
230 const std::vector<unsigned short>& data,
231 unsigned int startSample, unsigned int endSample,
232 unsigned int lumiBlock)
233{
234 ATH_MSG_DEBUG("Processing LIS data for side " << side << ", channel " << channel);
235
236 int ADCSum = 0;
237 int maxADCsub = -999;
238 unsigned int maxSample = 0;
239 float avgTime = 0.f;
240 unsigned int moduleStatus = 0;
241
242 if (data.empty()) {
243 ATH_MSG_DEBUG("Empty waveform data");
244 return LISModuleResults();
245 }
246
247 if (startSample >= data.size() || endSample >= data.size()) {
248 ATH_MSG_WARNING("Start or end sample number greater than number of samples");
249 return LISModuleResults();
250 }
251
252 // Get pedestal for this channel using histogram if available, otherwise use predefined value
253 float ModPedestal = GetPedestal(channel, lumiBlock);
254
255 // Calculate presample (baseline) from first few samples
256 float preFADC = 0;
257 unsigned int nBaseline = std::min(m_nBaselineSamples, static_cast<unsigned int>(data.size()));
258 for (unsigned int i = m_nBaselineStart; i < m_nBaselineEnd; ++i) {
259 preFADC += data[i];
260 }
261 preFADC /= nBaseline;
262
263 // Process samples in analysis window
264 for (unsigned int sample = startSample; sample <= endSample; sample++) {
265 int FADCsub = static_cast<int>(data[sample]) - static_cast<int>(std::round(ModPedestal));
266
267 float time = (sample + 0.5f) * m_deltaTSample;
268 ADCSum += FADCsub;
269
270 if (FADCsub > maxADCsub) {
271 maxADCsub = FADCsub;
272 maxSample = sample;
273 }
274
275 avgTime += time * FADCsub;
276 }
277
278 // Calculate average time
279 if (ADCSum != 0) {
280 avgTime /= ADCSum;
281 } else {
282 avgTime = 0.f;
283 }
284
285 // Set status bits
286 if(std::abs(preFADC - ModPedestal) > 15) {
287 setStatusBit(moduleStatus, LISModuleStatusBits::LIS_HighPedestal); // Set high pedestal bit
288 }
289
290 if(maxSample < m_nPulsStart) {
291 setStatusBit(moduleStatus, LISModuleStatusBits::LIS_EarlyPulse); // Set early pulse bit
292 }
293 else if(maxSample > m_nPulsEnd) {
294 setStatusBit(moduleStatus, LISModuleStatusBits::LIS_LatePulse); // Set late pulse bit
295 }
296
297 if(maxADCsub > m_ADCSaturationValue){ // ADC overflow threshold
298 setStatusBit(moduleStatus, LISModuleStatusBits::LIS_Overflow); // Set overflow bit
299 }
300
301 bool hasBadStatus = false;
302 for(int ibit = 1; ibit < LISModuleStatusBits::LIS_NumStatusBits; ibit++){
303 if(CheckStatusBit(moduleStatus, ibit)){
304 hasBadStatus = true;
305 break;
306 }
307 }
308 if(!hasBadStatus) {
309 setStatusBit(moduleStatus, LISModuleStatusBits::LIS_ValidData); // Set good status bit if no other bits are set
310 }
311 else{
312 setStatusBit(moduleStatus, LISModuleStatusBits::LIS_BadBit); // Set generic bad bit if any specific issue bits are set
313 }
314
315 ATH_MSG_DEBUG(" Presample: " << preFADC << ", ADCSum: " << ADCSum
316 << ", MaxADC: " << maxADCsub << ", MaxSample: " << maxSample
317 << ", AvgTime: " << avgTime);
318
319 return LISModuleResults(preFADC, ADCSum, maxADCsub, maxSample, avgTime, moduleStatus);
320}
321
323 ATH_MSG_DEBUG("Processing LIS module: side=" << module.zdcSide()
324 << ", module=" << module.zdcModule()
325 << ", channel=" << module.zdcChannel());
326
327 int LISModuleGain = 0;
328
329 if(module.zdcChannel() > 3){
330 LISModuleGain = 1; // gain 1 for channels 4-7
331 }
332 else{
333 LISModuleGain = 0; // gain 0 for channels 0-3
334 }
335
336 ATH_MSG_DEBUG("LIS module gain: " << LISModuleGain);
337
338 // Determine which gain data to use
339 static SG::ConstAccessor<std::vector<uint16_t>> g0dataAccessor("g0data");
340 static SG::ConstAccessor<std::vector<uint16_t>> g1dataAccessor("g1data");
341 const SG::ConstAccessor<std::vector<uint16_t>> &gainDataAccessor = (LISModuleGain == 0) ? g0dataAccessor : g1dataAccessor;
342
343 // Get waveform data
344 if (!gainDataAccessor.isAvailable(module)) {
345 ATH_MSG_DEBUG("No gain data available for this module");
346 return LISModuleResults();
347 }
348
349 const std::vector<uint16_t> &waveform = gainDataAccessor(module);
350 if (waveform.empty()) {
351 ATH_MSG_DEBUG("Empty waveform");
352 return LISModuleResults();
353 }
354
355 return processModuleData(module.zdcSide(), module.zdcChannel(),
356 waveform, m_sampleAnaStart, m_sampleAnaEnd, lumiBlock);
357}
358
360 xAOD::ZdcModuleContainer const& moduleContainer,
361 xAOD::ZdcModuleContainer const& moduleSumContainer) {
362
363 ATH_MSG_DEBUG("LISAnalysisTool::recoZdcModules processing event");
364
365 if (!m_init) {
366 ATH_MSG_WARNING("Tool not initialized!");
367 return StatusCode::FAILURE;
368 }
369
370 if (moduleContainer.empty()) {
371 return StatusCode::SUCCESS;
372 }
373 // Check for decoding errors
375 if (!eventInfo.isValid()) {
376 ATH_MSG_WARNING("EventInfo not valid");
377 return StatusCode::FAILURE;
378 }
379
380 bool lisErr = eventInfo->isEventFlagBitSet(xAOD::EventInfo::ForwardDet, ZdcEventInfo::LISDECODINGERROR);
381 if (lisErr) {
382 ATH_MSG_WARNING("LIS decoding error found - abandoning LISAnalysisTool!");
383 return StatusCode::SUCCESS;
384 }
385
388
389 // Get lumiblock from event info
390 unsigned int lumiBlock = eventInfo->lumiBlock();
391
392 unsigned int thisRunNumber = eventInfo->runNumber();
393 if (thisRunNumber != m_runNumber) {
394 ATH_MSG_INFO("LIS analysis tool will be configured for run " << thisRunNumber );
395 ATH_MSG_INFO("Pedestals will be configured for run " << thisRunNumber);
396 StatusCode status = SetPedestals(thisRunNumber);
397 if (status != StatusCode::SUCCESS) {
398 ATH_MSG_ERROR("Failed to set pedestals from calibration file for run " << thisRunNumber << ", using nominal pedestal values instead!");
399 m_MominalPedestals = true;
400 }
401
402 m_runNumber = thisRunNumber;
403 }
404 // Loop over the sum container to find event-level info (side == 0)
405 //
406 bool haveZdcEventInfo = false;
407 unsigned int eventType = ZdcEventInfo::ZdcEventUnknown;
408 unsigned int bcid = 0;
409
410 const xAOD::ZdcModule* moduleSumEventInfo_ptr = 0;
411
412 for (auto modSum : moduleSumContainer) {
413 //
414 // Module sum object with side == 0 contains event-level information
415 //
416 if (modSum->zdcSide() == 0) {
417 //
418 // Add the event type and bcid as aux decors
419 //
420 ATH_MSG_DEBUG("Found global sum");
421 eventType = eventTypeHandle(*modSum);
422 haveZdcEventInfo = true;
423 moduleSumEventInfo_ptr = modSum;
424 }
425 }
426
427 if (!haveZdcEventInfo) {
428 ATH_MSG_ERROR("Zdc event data not available (moduleSum with side = 0)");
429 return StatusCode::FAILURE;
430 }
431
432 // Get the BCID from the rodBCID vector
433 const std::vector<uint16_t>& rodBCID = rodBCIDHandle(*moduleSumEventInfo_ptr);
434 if (!rodBCID.empty()) {
435 bcid = rodBCID[0]; // Use first BCID from LUCROD
436 ATH_MSG_DEBUG("Retrieved BCID from rodBCID: " << bcid);
437 } else {
438 ATH_MSG_WARNING("rodBCID vector is empty, using EventInfo BCID");
439 bcid = eventInfo->bcid();
440 }
441
442 // Determine the LED type
443 //
444 unsigned int evtLEDType = ZdcEventInfo::LEDNone;
445
446 for (unsigned int idxLED = 0; idxLED < ZdcEventInfo::NumLEDs; idxLED++) {
447 //
448 // Does the BCID match one of those associated with the LEDs?
449 //
450 if (m_LEDBCID[idxLED] == bcid) {
451
452 evtLEDType = idxLED;
453 break;
454 }
455 }
456
457 if (evtLEDType == ZdcEventInfo::LEDNone && eventType == ZdcEventInfo::ZdcEventLED) {
458 //
459 // Thie BCID does not appear to be associated with one of the LEDs, print warning and quit processing
460 //
461 ATH_MSG_WARNING("LED event: Unexpected BCID found in data: bcid = " << bcid << m_configuration);
462 return StatusCode::SUCCESS;
463 }
464 else {
465 ATH_MSG_DEBUG("Event with BCID = " << bcid << " has LED type " << evtLEDType);
466 }
467
468 // Create write decoration handles
476
477 ATH_MSG_DEBUG("Processing " << moduleContainer.size() << " modules");
478
479 // Loop over modules and process LIS channels (module=5, type=2)
480 for (const auto* zdcModule : moduleContainer) {
481
482 LISModuleResults results = processLISModule(*zdcModule, lumiBlock);
483
484 ATH_MSG_DEBUG("Writing aux decors to LIS module: side=" << zdcModule->zdcSide()
485 << ", channel=" << zdcModule->zdcChannel());
486
487 // Write decorations
488 presampleHandle(*zdcModule) = results.getPresampleADC();
489 adcSumHandle(*zdcModule) = results.getADCSum();
490 maxADCHandle(*zdcModule) = results.getMaxADC();
491 maxSampleHandle(*zdcModule) = results.getMaxSample();
492 avgTimeHandle(*zdcModule) = results.getAvgTime();
493 moduleStatusHandle(*zdcModule) = results.getmoduleStatus();
494 }
495
496
497 if(eventType == ZdcEventInfo::ZdcEventLED){
498 // Write the LED type to the moduleSum container keep event-level data
499 //
500 LEDTypeHandle(*moduleSumEventInfo_ptr) = evtLEDType;
501 }
502
503 ATH_MSG_DEBUG("Finished event processing");
504
505 return StatusCode::SUCCESS;
506}
507
509 if (!m_init) {
510 ATH_MSG_WARNING("Tool not initialized!");
511 return StatusCode::FAILURE;
512 }
513
514 ATH_MSG_DEBUG("Trying to retrieve " << m_zdcModuleContainerName);
515
516 m_zdcModules = nullptr;
518
519 m_zdcSums = nullptr;
521
523
524 return StatusCode::SUCCESS;
525}
526
527} // namespace ZDC
#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)
Handle class for reading a decoration on an object.
Handle class for reading from StoreGate.
Handle class for adding a decoration to an object.
Handle class for recording to StoreGate.
@ Data
Definition BaseObject.h:11
Helper class to provide constant type-safe access to aux data.
char data[hepevt_bytes_allocation_ATLAS]
Definition HepEvt.cxx:11
std::string PathResolverFindCalibFile(const std::string &logical_file_name)
size_t size() const
Number of registered mappings.
Define enumerations for event-level ZDC data.
ServiceHandle< StoreGateSvc > & evtStore()
size_type size() const noexcept
Returns the number of elements in the collection.
bool empty() const noexcept
Returns true if the collection is empty.
Helper class to provide constant type-safe access to aux data.
bool isAvailable(const ELT &e) const
Test to see if this variable exists in the store.
Handle class for reading a decoration on an object.
virtual bool isValid() override final
Can the handle be successfully dereferenced?
Handle class for adding a decoration to an object.
SG::WriteDecorHandleKey< xAOD::ZdcModuleContainer > m_LISMaxADC
Gaudi::Property< bool > m_writeAux
SG::ReadHandleKey< xAOD::EventInfo > m_eventInfoKey
SG::WriteDecorHandleKey< xAOD::ZdcModuleContainer > m_LISMaxSample
Gaudi::Property< unsigned int > m_nBaselineEnd
SG::WriteDecorHandleKey< xAOD::ZdcModuleContainer > m_LISADCSum
LISAnalysisTool(std::string const &name)
Gaudi::Property< std::string > m_auxSuffix
SG::ReadDecorHandleKey< xAOD::ZdcModuleContainer > m_robBCIDKey
float GetPedestal(int channel, unsigned int lumiBlock)
unsigned int m_sampleAnaStart
Gaudi::Property< unsigned int > m_nPulsStart
SG::WriteDecorHandleKey< xAOD::ZdcModuleContainer > m_LISPresampleADC
SG::ReadDecorHandleKey< xAOD::ZdcModuleContainer > m_eventTypeKey
SG::WriteDecorHandleKey< xAOD::ZdcModuleContainer > m_ZdcLEDType
Gaudi::Property< unsigned int > m_nBaselineStart
Gaudi::Property< std::vector< float > > m_channelPedestals
std::vector< unsigned int > m_LEDCalreqIdx
StatusCode reprocessZdc() override
std::vector< unsigned int > m_LEDBCID
Gaudi::Property< std::string > m_configuration
unsigned int m_nBaselineSamples
SG::ReadDecorHandleKey< xAOD::ZdcModuleContainer > m_DAQModeKey
LISModuleResults processLISModule(const xAOD::ZdcModule &module, unsigned int lumiBlock)
StatusCode initialize() override
Dummy implementation of the initialisation function.
const xAOD::ZdcModuleContainer * m_zdcSums
Gaudi::Property< std::string > m_zdcModuleContainerName
StatusCode SetPedestals(unsigned int runNumber)
Gaudi::Property< unsigned int > m_nPulsEnd
const xAOD::ZdcModuleContainer * m_zdcModules
void setStatusBit(unsigned int &statusWord, unsigned int bitIndex)
SG::WriteDecorHandleKey< xAOD::ZdcModuleContainer > m_LISAvgTime
SG::WriteDecorHandleKey< xAOD::ZdcModuleContainer > m_LISModuleStatus
Gaudi::Property< std::string > m_zdcSumContainerName
unsigned int m_sampleAnaEnd
bool CheckStatusBit(unsigned int statusWord, unsigned int bitIndex)
LISModuleResults processModuleData(int side, int channel, const std::vector< unsigned short > &data, unsigned int startSample, unsigned int endSample, unsigned int lumiBlock)
std::array< std::unique_ptr< TH1 >, 8 > m_LISPedestals
StatusCode recoZdcModules(xAOD::ZdcModuleContainer const &moduleContainer, xAOD::ZdcModuleContainer const &moduleSumContainer) override
AsgTool(const std::string &name)
Constructor specifying the tool instance's name.
Definition AsgTool.cxx:58
@ ForwardDet
The forward detectors.
@ LIS_NumStatusBits
@ LIS_LatePulse
@ LIS_HighPedestal
@ LIS_Overflow
@ LIS_EarlyPulse
@ LIS_ValidData
ZdcModuleContainer_v1 ZdcModuleContainer
ZdcModule_v1 ZdcModule
Definition ZdcModule.h:15