ATLAS Offline Software
Loading...
Searching...
No Matches
LArRawChannelMonAlg.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
10#include "CLHEP/Units/SystemOfUnits.h"
12#include "Identifier/Identifier.h"
14#include "Identifier/Range.h"
20#include "AthenaKernel/Units.h"
23
24#include <algorithm>
25#include <cmath>
26#include <stdexcept>
27#include <complex>
28#include <format>
29#include <tuple>
30
31using namespace std::complex_literals;
32
33namespace {
34
35enum : int8_t {
36 EMBA,
37 EMBC,
38 EMECA,
39 EMECC,
40 HECA,
41 HECC,
42 FCALA,
43 FCALC,
44 NDETECTORS,
45 UNDEF = -1
46};
47
48constexpr unsigned numberOfSlotsPerFeedthrough(std::size_t det) {
49 bool b{det == ::EMBA || det == ::EMBC};
50 return b ? 14 : 15;
51}
52
53constexpr std::array<int8_t, 8> partitions() {
54 return {::EMBA, ::EMBC, ::EMECA, ::EMECC,
56}
57
58} // namespace
59
60
62 ISvcLocator *pSvcLocator)
63 : AthMonitorAlgorithm(name, pSvcLocator) {
64 using T = decltype(m_det_to_nchannels);
65 static_assert(std::tuple_size_v<T> >= ::NDETECTORS);
66 using U = decltype(m_monitoring_tool_index);
67 static_assert(std::tuple_size_v<U> >= ::NDETECTORS);
68}
69
70
72
73
75{
76 ATH_MSG_DEBUG("===> start " << name() << "::initialize <=== ");
77
78 ATH_CHECK(m_larFlagKey.initialize());
80 ATH_CHECK(detStore()->retrieve(m_lar_online_id_ptr, "LArOnlineID"));
81 ATH_CHECK(m_bcContKey.initialize());
82 ATH_CHECK(m_bcMask.buildBitMask(m_problemsToMask, msg()));
83 ATH_CHECK(m_atlasReady_tools.retrieve());
84 ATH_CHECK(m_noiseKey.initialize());
85 ATH_CHECK(m_cablingKey.initialize());
86
87 auto get_detector = [&](auto hwid) {
88 const bool sideA = m_lar_online_id_ptr->pos_neg(hwid);
89 if (m_lar_online_id_ptr->isEMBchannel(hwid))
90 return sideA ? ::EMBA : ::EMBC;
91 else if (m_lar_online_id_ptr->isEMECchannel(hwid))
92 return sideA ? ::EMECA : ::EMECC;
93 else if (m_lar_online_id_ptr->isHECchannel(hwid))
94 return sideA ? ::HECA : ::HECC;
95 else if (m_lar_online_id_ptr->isFCALchannel(hwid))
96 return sideA ? ::FCALA : ::FCALC;
97 return ::UNDEF; };
98
99 // Create FEB hash -> Detector map
100 std::size_t feb_hash_max = m_lar_online_id_ptr->febHashMax();
101 m_feb_hash_to_detector.resize(feb_hash_max, ::UNDEF);
102 auto end_feb = m_lar_online_id_ptr->feb_end();
103 for (auto itr = m_lar_online_id_ptr->feb_begin(); itr != end_feb; ++itr) {
104 IdentifierHash feb_hash = m_lar_online_id_ptr->feb_Hash(*itr);
105 auto det = get_detector(*itr);
106 if (feb_hash < m_feb_hash_to_detector.size()) {
107 m_feb_hash_to_detector.at(feb_hash) = det;
108 } else {
109 ATH_MSG_WARNING("FEB hash out of range, ignored.");
110 }
111 }
112
113 // Count number of channels in each detector ---
114 std::fill(m_det_to_nchannels.begin(), m_det_to_nchannels.end(), 0);
115 auto citr = m_lar_online_id_ptr->channel_begin();
116 auto citr_end = m_lar_online_id_ptr->channel_end();
117 for (; citr != citr_end; ++citr) {
118 // TODO: skip unconnected/masked channels, but these may depend on IOV...
119 auto det = get_detector(*citr);
120 if (det != ::UNDEF)
121 m_det_to_nchannels[det] += 1;
122 }
123
124 std::vector<std::string> det2str(::NDETECTORS);
125 det2str[::EMBA] = "EMBA";
126 det2str[::EMBC] = "EMBC";
127 det2str[::EMECA] = "EMECA";
128 det2str[::EMECC] = "EMECC";
129 det2str[::HECA] = "HECA";
130 det2str[::HECC] = "HECC";
131 det2str[::FCALA] = "FCalA";
132 det2str[::FCALC] = "FCalC";
133
134 if (msgLvl(MSG::DEBUG)) {
135 ATH_MSG_DEBUG("Number of channels in detectors: ");
136 for (int8_t det : partitions()) {
137 auto n = m_det_to_nchannels[det];
138 ATH_MSG_DEBUG(det2str[det] << " has " << n << "channels ");
139 }
140 }
141
143
144 auto toolmap = Monitored::buildToolMap<int>(
145 m_tools, "LArRawChannelMon", det2str);
146 for (int8_t det : partitions()) {
147 m_monitoring_tool_index[det] = toolmap.at(det2str[det]);
148 }
149
150 ATH_MSG_DEBUG("===> end " << name() << "::initialize, "
151 "will now initialize base class <=== ");
153}
154
155
156StatusCode LArRawChannelMonAlg::fillHistograms(const EventContext &ctx) const
157{
158 ATH_MSG_DEBUG("===> start " << name() << "::fillHistograms() <=== ");
159
160 // Retrieve raw channels
163 if (!raw_channels.isValid()) {
164 ATH_MSG_WARNING("Cannot retrieve LArRawChannelContainer with key: "
166 return StatusCode::SUCCESS;
167 }
168
169 const bool is_atlas_ready = std::all_of(
170 m_atlasReady_tools.begin(),
171 m_atlasReady_tools.end(),
172 [&ctx](auto &f) { return f->accept(ctx); });
173
175 int bcid{0}, lumi_block{0};
176 bool larNoisyROAlg_flag{false};
177 bool larNoisyROAlgInTimeW_flag{false};
178 bool larNoisyROAlg_W_flag{false};
179 bool noisy_event{false};
180 if (event_info.isValid()) {
181 auto checkEventFlag = [&](auto bitinfo, const char *txt) {
182 bool flag = event_info->isEventFlagBitSet(
183 xAOD::EventInfo::LAr, bitinfo);
184 if (flag) ATH_MSG_DEBUG(" !!! Noisy event found " << txt << " !!!");
185 return flag; };
186 larNoisyROAlg_flag = checkEventFlag(
187 LArEventBitInfo::BADFEBS, "from LArNoisyROAlg");
188 larNoisyROAlg_W_flag = checkEventFlag(
189 LArEventBitInfo::BADFEBS_W, "from LArNoisyROAlg_W");
190 larNoisyROAlgInTimeW_flag = checkEventFlag(
191 3, "by LArNoisyROAlg in Time window of 500ms");
192 bcid = event_info->bcid();
193 lumi_block = event_info->lumiBlock();
194
195 const auto &tags = event_info->streamTags();
196 auto inSet = [&](auto &x){ return m_noise_streams_set.count(x.name()); };
197 noisy_event = m_noise_streams_set.empty()
198 || std::any_of(tags.begin(), tags.end(), inSet);
199 } else {
200 ATH_MSG_DEBUG("Cannot retrieve EventInfo");
201 }
202
203 std::array<uint32_t, ::NDETECTORS> det_n_noisy_channels{};
204 std::array<uint32_t, ::NDETECTORS> det_n_noisy_channels_Neg{};
205 std::array<uint32_t, ::NDETECTORS> det_n_badQ_channels{};
206 using wsum_t = std::complex<double>;
207 wsum_t event_mean_time{};
208 std::array<wsum_t, ::NDETECTORS> mean_detector_times{};
209 std::vector<wsum_t> mean_feb_times(m_feb_hash_to_detector.size(), 0.);
210 std::array<double, ::NDETECTORS> per_detector_total_energy{};
211 int8_t lastdet{::UNDEF};
212 ToolHandle<GenericMonitoringTool> monitoring{nullptr};
216 ATH_CHECK(noiseH.isValid());
217 ATH_CHECK(bcContH.isValid());
218 ATH_CHECK(cablingH.isValid());
219
220 Monitored::Scalar<int> dqm_superslot{"S", -1};
221 Monitored::Scalar<int> dqm_channel{"C", -1};
222 Monitored::Scalar<float> dqm_posn{"posn", 0};
223 Monitored::Scalar<float> dqm_negn{"negn", 0};
224 Monitored::Scalar<float> dqm_qual{"Q4k", 0};
225 Monitored::Scalar<float> dqm_energy{"E", 0};
226 Monitored::Scalar<int> dqm_gain{"G", 0};
227 Monitored::Scalar<bool> dqmf_occ{"occ", false};
228 Monitored::Scalar<bool> dqmf_sig{"sig", false};
229
230 for (const LArRawChannel &chan : *raw_channels) {
231 HWIdentifier hardware_id{chan.hardwareID()};
232 Identifier offline_id{0};
233 HWIdentifier feb_id{0};
234 IdentifierHash feb_hash{0};
235 int channel{-1};
236 int slot_number{-1}, feedthrough_number{-1};
237 int8_t det{::UNDEF};
238 float energy, time, noise, significance;
239 int gain{-1};
240 bool bad_quality;
241
242 // Skip unconnected channels
243 if (!cablingH->isOnlineConnected(hardware_id)) continue;
244 // Skip masked channels
245 if (m_bcMask.cellShouldBeMasked(*bcContH, hardware_id)) continue;
246
247 // Monitor properly reconstructed channels only:
248 // - provenance&0x00ff == 0x00a5:
249 // raw channels from OFC iteration, all calib constants found in DB
250 // - provenance&0x1000 == 0x1000:
251 // raw channels from DSP. If no constant loaded in DSP, energy==0
253 && !LArProv::test(chan.provenance(), LArProv::DEFAULTRECO)
254 && !LArProv::test(chan.provenance(), LArProv::DSPCALC)) continue;
255
256 try {
257 feb_id = m_lar_online_id_ptr->feb_Id(hardware_id);
258 feb_hash = m_lar_online_id_ptr->feb_Hash(feb_id);
259 det = m_feb_hash_to_detector.at(feb_hash);
260 if (det != lastdet) {
261 if (det >= 0 && det < ::NDETECTORS) {
262 monitoring = m_tools[m_monitoring_tool_index[det]];
263 } else {
264 monitoring = nullptr;
265 }
266 lastdet = det;
267 }
268
269 slot_number = m_lar_online_id_ptr->slot(hardware_id);
270 feedthrough_number = m_lar_online_id_ptr->feedthrough(hardware_id);
271 channel = m_lar_online_id_ptr->channel(hardware_id);
272 energy = chan.energy() * Athena::Units::MeV; // fixed in MeV by DSP
273 time = chan.time() * Athena::Units::picosecond; // fixed in ps by DSP
274 gain = chan.gain();
275 noise = noiseH->getNoise(offline_id, gain);
276 significance = energy / noise;
277 bad_quality = (energy > 0.1 * Athena::Units::MeV)
278 && (chan.quality() > m_quality_threshold);
279 }
280 catch (const LArOnlID_Exception &) {
281 continue; // skip this channel
282 }
283 catch (const std::out_of_range &err) {
284 ATH_MSG_WARNING("FEB hash out of range. Detector undefined"
285 << err.what());
286 continue; // skip this channel
287 }
288 catch (const LArID_Exception &) {
289 ATH_MSG_WARNING("channel offline id undefined ... skipping");
290 continue; // skip this channel
291 }
292 if ( det <0 or det >= NDETECTORS)[[unlikely]]{
293 ATH_MSG_WARNING(std::format("Subdetector index {} out of range of the arrays.", det));
294 continue; // skip this channel
295 }
296 // Fill per-detector histograms ---
297 if (m_monitor_detectors && monitoring ) {
298 bool noisy_pos{significance > m_pos_noise_thresholds[det]};
299 bool noisy_neg{-significance > m_neg_noise_thresholds[det]};
300 per_detector_total_energy[det] += energy;
301 det_n_noisy_channels[det] += noisy_pos;
302 det_n_noisy_channels_Neg[det] += noisy_neg;
303 det_n_badQ_channels[det] += bad_quality;
304 dqm_superslot = feedthrough_number * ::numberOfSlotsPerFeedthrough(det)
305 + slot_number;
306 dqm_channel = channel;
307 dqmf_occ = noisy_event && (energy > m_occupancy_thresholds[det]);
308 dqmf_sig = noisy_event && (energy > m_signal_thresholds[det])
309 && is_atlas_ready && !larNoisyROAlgInTimeW_flag;
310 dqm_energy = energy;
311 dqm_gain = gain;
312 dqm_posn = 100 * (noisy_event && !larNoisyROAlgInTimeW_flag
313 && noisy_pos && is_atlas_ready);
314 dqm_negn = 100 * (noisy_event && !larNoisyROAlgInTimeW_flag
315 && noisy_neg && is_atlas_ready);
316 dqm_qual = 100 * (bad_quality && is_atlas_ready
317 && !larNoisyROAlgInTimeW_flag);
318 fill(monitoring, dqm_superslot, dqm_channel,
319 dqmf_occ, dqmf_sig, dqm_energy, dqm_gain,
320 dqm_posn, dqm_negn, dqm_qual);
321
322 if (m_monitor_time && significance > m_time_threshold) {
323 // The time resolution is \sigma_t = \frac{a}{E/\sigma_{E}} \oplus b
324 // where a and b depend on the channel position (partition + layer);
325 // in practice a = 30ns and b = 1ns are assumed.
326 if (significance != 0.) {
327 double weight = 1. + 900. / std::pow(significance, 2);
328 // <t> = ( \sum w*t ) / ( \sum w )
329 // -> num. & den. are accumulated separately using a complex variable
330 wsum_t datapoint{weight * (double(time) + 1i)};
331 event_mean_time += datapoint;
332 mean_detector_times[det] += datapoint;
333 mean_feb_times[feb_hash] += datapoint;
334 }
335 }
336 }
337 }
338
339 Monitored::Scalar<int> dqm_lb{"lb", lumi_block};
340 Monitored::Scalar<int> dqm_bcid{"bc", bcid};
341 Monitored::Scalar<int> dqm_qnChan{"nQ4k", 0};
342 Monitored::Scalar<float> dqm_percent_noisy{"%noisy", 0};
343 Monitored::Scalar<float> dqm_percent_neg_noisy{"%noisy_neg", 0};
344 Monitored::Scalar<bool> dqmf_burst{"burst", false};
345 Monitored::Scalar<bool> dqmf_burst_timevetoed{"burst_quietW", false};
346 Monitored::Scalar<float> dqm_total_energy{"detE", 0};
347 Monitored::Scalar<bool> dqmf_qburst{"qburst", false};
348 Monitored::Scalar<bool> dqmf_noNoisyRO{"quiet", !larNoisyROAlg_flag};
349 Monitored::Scalar<bool> dqmf_noNoisyRO_W{"quietW", !larNoisyROAlg_W_flag};
350 Monitored::Scalar<bool> dqmf_noNoisyRO_ITW{
351 "quietITW", !larNoisyROAlgInTimeW_flag};
352 for (int8_t det : ::partitions()) {
353 float scaling = 100.f / m_det_to_nchannels[det];
354 float percent_noisy = scaling * det_n_noisy_channels[det];
355 float percent_bad_quality = scaling * det_n_badQ_channels[det];
356 dqmf_burst = percent_noisy > m_noise_burst_percent_thresholds[det];
357 dqmf_burst_timevetoed = dqmf_burst && !larNoisyROAlgInTimeW_flag;
358 dqm_percent_noisy = percent_noisy;
359 dqm_percent_neg_noisy = scaling * det_n_noisy_channels_Neg[det];
360 dqm_qnChan = det_n_badQ_channels[det];
361 dqmf_qburst = percent_bad_quality > m_noise_burst_percent_thresholds[det];
362 dqm_total_energy = per_detector_total_energy[det];
363
364 if (m_monitor_burst) {
365 fill(m_tools[m_monitoring_tool_index[det]], dqm_lb, dqm_bcid,
366 dqm_percent_noisy, dqm_percent_neg_noisy,
367 dqmf_noNoisyRO, dqmf_noNoisyRO_W, dqmf_noNoisyRO_ITW,
368 dqmf_burst, dqmf_burst_timevetoed, dqmf_qburst,
369 dqm_qnChan);
370 }
371 if (m_monitor_signal) {
372 fill(m_tools[m_monitoring_tool_index[det]], dqm_lb, dqm_bcid,
373 dqmf_noNoisyRO_W, dqm_total_energy);
374 }
375 }
376
377 Monitored::Scalar<float> dqm_time{"T", 0};
379 // sum w*t and sum w are accumulated respectively in Re(z) and Im(z)
380 double t{event_mean_time.real() / event_mean_time.imag()};
381 for (uint32_t h = 0; h < mean_feb_times.size(); ++h) {
382 wsum_t w{mean_feb_times[h]};
383 if (w.imag() == 0.) continue;
384 int8_t det = m_feb_hash_to_detector.at(h);
385 double t_feb = w.real() / w.imag();
386 dqm_time = (t_feb - t) / Athena::Units::nanosecond;
387 fill(m_tools[m_monitoring_tool_index[det]], dqm_time);
388 }
389 }
390
391 return StatusCode::SUCCESS;
392}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
Definition of CaloDetDescrManager.
Wrapper to avoid constant divisions when using units.
#define x
const ServiceHandle< StoreGateSvc > & detStore() const
bool msgLvl(const MSG::Level lvl) const
virtual StatusCode initialize() override
initialize
SG::ReadHandle< xAOD::EventInfo > GetEventInfo(const EventContext &) const
Return a ReadHandle for an EventInfo object (get run/event numbers, etc.).
AthMonitorAlgorithm(const std::string &name, ISvcLocator *pSvcLocator)
Constructor.
ToolHandleArray< GenericMonitoringTool > m_tools
Array of Generic Monitoring Tools.
This is a "hash" representation of an Identifier.
Exception class for LAr Identifiers.
Exception class for LAr online Identifiers.
SG::ReadHandleKey< LArRawChannelContainer > m_LArRawChannel_container_key
Gaudi::Property< std::vector< int > > m_pos_noise_thresholds
virtual StatusCode initialize() override
initialize
SG::ReadCondHandleKey< LArOnOffIdMapping > m_cablingKey
const LArOnlineID * m_lar_online_id_ptr
Gaudi::Property< short > m_time_threshold
ToolHandleArray< IDQFilterTool > m_atlasReady_tools
Gaudi::Property< bool > m_db_and_ofc_only
LArRawChannelMonAlg(const std::string &, ISvcLocator *)
SG::ReadCondHandleKey< CaloNoise > m_noiseKey
Gaudi::Property< bool > m_monitor_signal
std::vector< int8_t > m_feb_hash_to_detector
std::array< uint32_t, 8 > m_det_to_nchannels
Gaudi::Property< bool > m_monitor_burst
Gaudi::Property< std::vector< std::string > > m_problemsToMask
Gaudi::Property< unsigned short > m_quality_threshold
std::set< std::string > m_noise_streams_set
SG::ReadCondHandleKey< LArBadChannelCont > m_bcContKey
Gaudi::Property< bool > m_monitor_time
Gaudi::Property< std::vector< std::string > > m_noise_streams
SG::ReadDecorHandleKey< xAOD::EventInfo > m_larFlagKey
virtual StatusCode fillHistograms(const EventContext &ctx) const override
adds event to the monitoring histograms
std::array< int, 8 > m_monitoring_tool_index
Gaudi::Property< std::vector< double > > m_occupancy_thresholds
Gaudi::Property< bool > m_monitor_detectors
Gaudi::Property< std::vector< double > > m_noise_burst_percent_thresholds
Gaudi::Property< std::vector< int > > m_neg_noise_thresholds
Gaudi::Property< std::vector< double > > m_signal_thresholds
LArBadChannelMask m_bcMask
Liquid Argon ROD output object base class.
Declare a monitored scalar variable.
virtual bool isValid() override final
Can the handle be successfully dereferenced?
@ LAr
The LAr calorimeter.
std::vector< std::string > tags
Definition hcg.cxx:107
dict partitions
Definition DeMoScan.py:65
bool test(const uint16_t prov, const LArProvenance check)
std::vector< V > buildToolMap(const ToolHandleArray< GenericMonitoringTool > &tools, const std::string &baseName, int nHist)
Builds an array of indices (base case).
#define unlikely(x)
void fill(H5::Group &out_file, size_t iterations)