ATLAS Offline Software
Loading...
Searching...
No Matches
CaloPerformancePropertiesOutput.cxx
Go to the documentation of this file.
1//
2// Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3//
4// Dear emacs, this is -*- c++ -*-
5//
6
8
10
12
13#include "MacroHelpers.h"
14
15#include <fstream>
16
17using namespace CaloRecGPU;
18
19CaloPerformancePropertiesOutput::CaloPerformancePropertiesOutput(const std::string & type, const std::string & name, const IInterface * parent):
20 base_class(type, name, parent)
21{
22}
23
24
26{
27 ATH_CHECK( m_cellsKey.initialize() );
28
29 ATH_CHECK( m_noiseCDOKey.initialize() );
30
31 ATH_CHECK( detStore()->retrieve(m_calo_id, "CaloCell_ID") );
32
33 auto get_neighbour_option_from_string = [](const std::string & str, bool & failed)
34 {
35 failed = false;
38 prevInPhi,
39 nextInPhi,
40 prevInEta,
41 nextInEta,
42 faces2D,
43 corners2D,
44 all2D,
45 prevInSamp,
46 nextInSamp,
47 upAndDown,
48 prevSubDet,
49 nextSubDet,
50 all3D,
51 corners3D,
52 all3DwithCorners,
53 prevSuperCalo,
54 nextSuperCalo,
55 super3D
56 )
57 )
58 else
59 {
60 failed = true;
62 }
63 };
64
65 bool neigh_failed = false;
66 m_growNeighborOption = get_neighbour_option_from_string(m_growNeighborOptionString, neigh_failed);
67
68 if (neigh_failed)
69 {
70 ATH_MSG_ERROR("Invalid Grow Neighbour Option: " << m_growNeighborOptionString);
71 }
72
73 neigh_failed = false;
74 m_growNeighborOption = get_neighbour_option_from_string(m_splitNeighborOptionString, neigh_failed);
75
76 if (neigh_failed)
77 {
78 ATH_MSG_ERROR("Invalid Split Neighbour Option: " << m_splitNeighborOption);
79 }
80
81 return StatusCode::SUCCESS;
82}
83
84StatusCode CaloPerformancePropertiesOutput::execute(const EventContext & ctx, xAOD::CaloClusterContainer * cluster_collection) const
85{
87 if ( !cell_collection.isValid() )
88 {
89 ATH_MSG_ERROR( " Cannot retrieve CaloCellContainer: " << cell_collection.name() );
90 return StatusCode::FAILURE;
91 }
92
94 const CaloNoise * noise_tool = *noise_handle;
95
96 std::vector<unsigned char> cell_classification(NCaloCells, 0);
97 //0 for invalid, 1 for terminal, 2 for growing, 3 for seed...
98
99
100 EventPerformanceInfo ev_perf_info;
101
102 ev_perf_info.num_clusters = cluster_collection->size();
103
104 for (const auto & cell_ptr : *cell_collection)
105 {
106 const float energy = cell_ptr->energy();
107 const float noise = m_twoGaussianNoise ?
108 noise_tool->getEffectiveSigma(cell_ptr->ID(), cell_ptr->gain(), energy) :
109 noise_tool->getNoise(cell_ptr->ID(), cell_ptr->gain());
110 const float s_n_r = (noise > 0 ? energy / noise : 0.00001f);
111 const float abs_s_n_r = fabsf(s_n_r);
112
113 const int this_hash_ID = cell_ptr->caloDDE()->calo_hash();
114
115 if (s_n_r >= m_seedThreshold || (m_seedCutsInAbsE && abs_s_n_r >= m_seedThreshold))
116 {
117 cell_classification[this_hash_ID] = 3;
118 ++ev_perf_info.total_seed;
119 }
120 else if (s_n_r >= m_growThreshold || (m_neighborCutsInAbsE && abs_s_n_r >= m_growThreshold))
121 {
122 cell_classification[this_hash_ID] = 2;
123 ++ev_perf_info.total_grow;
124 }
125 else if (s_n_r >= m_cellThreshold || (m_cellCutsInAbsE && abs_s_n_r >= m_cellThreshold))
126 {
127 cell_classification[this_hash_ID] = 1;
128 ++ev_perf_info.total_term;
129 }
130 else
131 {
132 cell_classification[this_hash_ID] = 0;
133 ++ev_perf_info.total_invalid;
134 }
135 }
136
137 auto accumulate_stats = [&](EventPerformanceInfo::Stats & s, const double v)
138 {
139 s.min = std::min(s.min, v);
140 s.max = std::max(s.max, v);
141 s.avg += v;
142 s.stddev += v * v;
143 };
144
145 int cluster_count = 0;
146
147 std::vector<int> cluster_assignment(NCaloCells, -1);
148
149 constexpr int check_seed_flag = 0x40000000;
150 constexpr int check_first_flag = 0x20000000;
151 //Since we only have 187652 cells, this works...
152
153 constexpr int check_mask = ~(check_seed_flag | check_first_flag);
154
155 std::vector<int> cells_to_check;
156 std::vector<int> new_cells_to_check;
157 std::vector<IdentifierHash> neighs;
158
159 auto check_for_restrict = [&] (const auto & hash_ID, const bool restrict_HECIWandFCal, const bool restrict_PS)
160 {
161 const Identifier cell_identifier = m_calo_id->cell_id(hash_ID);
162
163 const auto sub_calo = m_calo_id->sub_calo(cell_identifier);
164 const auto region = m_calo_id->region(cell_identifier);
165 const auto intra_calo_sampling = m_calo_id->sampling(cell_identifier);
166
167 const bool is_PS = (sub_calo == CaloCell_ID::LAREM && intra_calo_sampling == 0);
168
169 const bool is_HECIW_or_FCAL = ( (sub_calo == CaloCell_ID::LARHEC && region == 1 ) ||
170 (sub_calo == CaloCell_ID::LARFCAL && intra_calo_sampling > 1 ) );
171
172 return (is_HECIW_or_FCAL && restrict_HECIWandFCal) || (is_PS && restrict_PS);
173 };
174
175 for (const auto & cluster_ptr : *cluster_collection)
176 {
177 int this_seed = 0, this_grow = 0, this_term = 0;
178
179 cells_to_check.clear();
180
181 bool first = true;
182
183 for (const CaloCell * cell_ptr : *cluster_ptr)
184 {
185 const int this_hash_ID = cell_ptr->caloDDE()->calo_hash();
186
187 cluster_assignment[this_hash_ID] = cluster_count | check_seed_flag | (first ? 0 : check_first_flag);
188
189 const unsigned char this_classification = cell_classification[this_hash_ID];
190
191 switch (this_classification)
192 {
193 case 3:
194 ++this_seed;
195 cluster_assignment[this_hash_ID] = cluster_count | (first ? 0 : check_first_flag);
196 cells_to_check.push_back(this_hash_ID);
197 break;
198 case 2:
199 ++this_grow;
200 break;
201 case 1:
202 ++this_term;
203 break;
204 default:
205 ATH_MSG_WARNING("Invalid cell " << this_hash_ID << " in cluster " << cluster_count << ".");
206 break;
207 }
208
209 first = false;
210 }
211
212 ev_perf_info.seed_in_cluster += this_seed;
213 ev_perf_info.grow_in_cluster += this_grow;
214 ev_perf_info.term_in_cluster += this_term;
215
216 accumulate_stats(ev_perf_info.cluster_size, cluster_ptr->size());
217 accumulate_stats(ev_perf_info.cluster_num_seed, this_seed);
218 accumulate_stats(ev_perf_info.cluster_num_grow, this_grow);
219 accumulate_stats(ev_perf_info.cluster_num_term, this_term);
220
221 //Now for the complicated part: the radius...
222
223
224 int min_radius = -1, radius = -1;
225
226 while (cells_to_check.size() > 0)
227 {
228 ++radius;
229
230 new_cells_to_check.clear();
231
232 for (const auto & hash_ID : cells_to_check)
233 {
234 if (min_radius < 0 && cell_classification[hash_ID] <= 1)
235 {
236 min_radius = radius;
237 //We have reached a terminal cell: this is the minimum radius.
238 }
239
240 const bool restrict = check_for_restrict(hash_ID,
243
244 neighs.clear();
245
247 {
248 m_calo_id->get_neighbours(hash_ID, LArNeighbours::nextInSamp, neighs);
249 }
250 else
251 {
252 m_calo_id->get_neighbours(hash_ID, m_growNeighborOption, neighs);
253 }
254
255 for (const auto & neigh_hash : neighs)
256 {
257 int & neigh_assignment = cluster_assignment[neigh_hash];
258 if ((neigh_assignment & check_seed_flag) && ((neigh_assignment & check_mask) == cluster_count))
259 {
260 new_cells_to_check.push_back(neigh_hash) ;
261 neigh_assignment = (neigh_assignment & ~check_seed_flag);
262 }
263 }
264 }
265
266 cells_to_check.swap(new_cells_to_check);
267 }
268
269 accumulate_stats(ev_perf_info.cluster_seed_min_radius, min_radius);
270 accumulate_stats(ev_perf_info.cluster_seed_max_radius, radius);
271
272 //And now the radius from the first cell
273 //(for split clusters)
274
275 if (cluster_ptr->size() > 0)
276 {
277 cells_to_check.clear();
278 cells_to_check.push_back(cluster_ptr->begin()->caloDDE()->calo_hash());
279
280 radius = -1;
281 min_radius = -1;
282
283 while (cells_to_check.size() > 0)
284 {
285 ++radius;
286
287 new_cells_to_check.clear();
288
289 for (const auto & hash_ID : cells_to_check)
290 {
291 const bool restrict = check_for_restrict(hash_ID,
294
295
296 neighs.clear();
297
299 {
300 m_calo_id->get_neighbours(hash_ID, LArNeighbours::nextInSamp, neighs);
301 }
302 else
303 {
304 m_calo_id->get_neighbours(hash_ID, m_splitNeighborOption, neighs);
305 }
306
307 for (const auto & neigh_hash : neighs)
308 {
309 int & neigh_assignment = cluster_assignment[neigh_hash];
310 if ((neigh_assignment & check_first_flag) && ((neigh_assignment & check_mask) == cluster_count))
311 {
312 new_cells_to_check.push_back(neigh_hash) ;
313 neigh_assignment = (neigh_assignment & ~check_first_flag);
314 }
315 }
316 }
317
318 cells_to_check.swap(new_cells_to_check);
319 }
320
321 accumulate_stats(ev_perf_info.cluster_first_max_radius, radius);
322 }
323
324 ++cluster_count;
325 }
326
327
328 auto finalize_stats = [&](EventPerformanceInfo::Stats & s)
329 {
330 s.avg /= cluster_collection->size();
331 s.stddev /= cluster_collection->size();
332 s.stddev -= s.avg * s.avg;
333 //Sure, floating point accuracy issues may ensue,
334 //but not the most relevant anyway...
335 };
336
337 finalize_stats(ev_perf_info.cluster_size);
338 finalize_stats(ev_perf_info.cluster_num_seed);
339 finalize_stats(ev_perf_info.cluster_num_grow);
340 finalize_stats(ev_perf_info.cluster_num_term);
341 finalize_stats(ev_perf_info.cluster_seed_min_radius);
342 finalize_stats(ev_perf_info.cluster_seed_max_radius);
343 finalize_stats(ev_perf_info.cluster_first_max_radius);
344
345 {
346 std::lock_guard<std::mutex> lock_guard(m_mutex);
347
348 m_eventInfo.push_back(ev_perf_info);
349 m_eventNumbers.push_back(ctx.evt());
350 }
351
352 return StatusCode::SUCCESS;
353}
354
355
357{
358 if (m_fileName.size() > 0)
359 {
360 std::ofstream out(m_fileName);
361
362 std::vector<size_t> indices(m_eventNumbers.size());
363
364 std::iota(indices.begin(), indices.end(), 0);
365 std::sort(indices.begin(), indices.end(), [&](size_t a, size_t b)
366 {
367 return m_eventNumbers[a] < m_eventNumbers[b];
368 }
369 );
370
371 out << "Event_Number Number_Clusters "
372 << "Total_Seed Total_Grow Total_Term Total_Invalid "
373 << "Seed_In_Cluster Term_In_Cluster";
374
375 auto print_stat_name = [&](const auto & name)
376 {
377 out << " " << name << "_Min " << name << "_Max " << name << "_Avg " << name << "_Stddev";
378 };
379
380 print_stat_name("Size");
381 print_stat_name("Num_Seed");
382 print_stat_name("Num_Grow");
383 print_stat_name("Num_Term");
384 print_stat_name("Radius_Seed_Min");
385 print_stat_name("Radius_Seed_Max");
386 print_stat_name("Radius_First");
387
388 out << "\n";
389
390 auto print_stat = [&](const EventPerformanceInfo::Stats & s)
391 {
392 out << " " << s.min << " " << s.max << " " << s.avg << " " << s.stddev;
393 };
394
395 for (const auto & idx : indices)
396 {
397 out << m_eventNumbers[idx] << " ";
398
399 const auto & info = m_eventInfo[idx];
400
401 out << info.num_clusters << " " << info.total_seed << " " << info.total_grow << " "
402 << info.total_term << " " << info.total_invalid << " " << info.seed_in_cluster << " "
403 << info.grow_in_cluster << " " << info.term_in_cluster;
404
405 print_stat(info.cluster_size);
406 print_stat(info.cluster_num_seed);
407 print_stat(info.cluster_num_grow);
408 print_stat(info.cluster_num_term);
409 print_stat(info.cluster_seed_min_radius);
410 print_stat(info.cluster_seed_max_radius);
411 print_stat(info.cluster_first_max_radius);
412
413 out << "\n";
414 }
415
416 out << std::endl;
417
418 out.close();
419
420 }
421
422 return StatusCode::SUCCESS;
423}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_WARNING(x)
static Double_t a
Contains some helpful macros to help with repetitive code...
#define CRGPU_RECURSIVE_MACRO(...)
Expands recursive macros.
#define CRGPU_CHEAP_STRING_TO_ENUM(VAR, PREFIX, ONE,...)
Checks a string variable, VAR, for matching enum identifiers (ONE and the remaining variadic argument...
Data object for each calorimeter readout cell.
Definition CaloCell.h:57
float getNoise(const IdentifierHash h, const int gain) const
Accessor by IdentifierHash and gain.
Definition CaloNoise.h:35
float getEffectiveSigma(const Identifier id, const int gain, const float energy) const
Definition CaloNoise.h:56
Gaudi::Property< float > m_growThreshold
Value to consider for the seed threshold.
Gaudi::Property< bool > m_growRestrictHECIWandFCalNeighbors
if set to true limit the neighbors in HEC IW and FCal2&3 during growing.
Gaudi::Property< bool > m_seedCutsInAbsE
if set to true seed cuts are on and .
Gaudi::Property< bool > m_cellCutsInAbsE
if set to true cell cuts are on and .
Gaudi::Property< bool > m_growRestrictPSNeighbors
if set to true limit the neighbors in presampler Barrel and Endcap during growing.
Gaudi::Property< bool > m_neighborCutsInAbsE
if set to true neighbor cuts are on and .
Gaudi::Property< bool > m_splitRestrictPSNeighbors
if set to true limit the neighbors in presampler Barrel and Endcap during splitting.
LArNeighbours::neighbourOption m_splitNeighborOption
std::mutex m_mutex
Mutex that is locked when recording info.
Gaudi::Property< std::string > m_splitNeighborOptionString
type of neighbor relations to use for cluster splitting.
const CaloCell_ID * m_calo_id
Pointer to Calo ID Helper.
CaloPerformancePropertiesOutput(const std::string &type, const std::string &name, const IInterface *parent)
SG::ReadCondHandleKey< CaloNoise > m_noiseCDOKey
Key of the CaloNoise Conditions data object.
Gaudi::Property< bool > m_twoGaussianNoise
if set to true use 2-gaussian noise description for TileCal
Gaudi::Property< std::string > m_growNeighborOptionString
type of neighbor relations to use for cluster growing.
Gaudi::Property< std::string > m_fileName
The path specifying the folder to which the files should be saved.
LArNeighbours::neighbourOption m_growNeighborOption
Gaudi::Property< float > m_seedThreshold
Value to consider for the seed threshold.
virtual StatusCode execute(const EventContext &ctx, xAOD::CaloClusterContainer *cluster_collection) const override
Gaudi::Property< bool > m_splitRestrictHECIWandFCalNeighbors
if set to true limit the neighbors in HEC IW and FCal2&3 during splitting.
Gaudi::Property< float > m_cellThreshold
Value to consider for the seed threshold.
SG::ReadHandleKey< CaloCellContainer > m_cellsKey
vector of names of the cell containers to use as input.
size_type size() const noexcept
Returns the number of elements in the collection.
virtual bool isValid() override final
Can the handle be successfully dereferenced?
const std::string & name() const
Return the StoreGate ID for the referenced object.
STL class.
Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration.
constexpr int NCaloCells
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.
CaloClusterContainer_v1 CaloClusterContainer
Define the latest version of the calorimeter cluster container.