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;
36 //cppcheck-suppress syntaxError
39 prevInPhi,
40 nextInPhi,
41 prevInEta,
42 nextInEta,
43 faces2D,
44 corners2D,
45 all2D,
46 prevInSamp,
47 nextInSamp,
48 upAndDown,
49 prevSubDet,
50 nextSubDet,
51 all3D,
52 corners3D,
53 all3DwithCorners,
54 prevSuperCalo,
55 nextSuperCalo,
56 super3D
57 )
58 )
59 else
60 {
61 failed = true;
63 }
64 };
65
66 bool neigh_failed = false;
67 m_growNeighborOption = get_neighbour_option_from_string(m_growNeighborOptionString, neigh_failed);
68
69 if (neigh_failed)
70 {
71 ATH_MSG_ERROR("Invalid Grow Neighbour Option: " << m_growNeighborOptionString);
72 }
73
74 neigh_failed = false;
75 m_growNeighborOption = get_neighbour_option_from_string(m_splitNeighborOptionString, neigh_failed);
76
77 if (neigh_failed)
78 {
79 ATH_MSG_ERROR("Invalid Split Neighbour Option: " << m_splitNeighborOption);
80 }
81
82 return StatusCode::SUCCESS;
83}
84
85StatusCode CaloPerformancePropertiesOutput::execute(const EventContext & ctx, xAOD::CaloClusterContainer * cluster_collection) const
86{
88 if ( !cell_collection.isValid() )
89 {
90 ATH_MSG_ERROR( " Cannot retrieve CaloCellContainer: " << cell_collection.name() );
91 return StatusCode::FAILURE;
92 }
93
95 const CaloNoise * noise_tool = *noise_handle;
96
97 std::vector<unsigned char> cell_classification(NCaloCells, 0);
98 //0 for invalid, 1 for terminal, 2 for growing, 3 for seed...
99
100
101 EventPerformanceInfo ev_perf_info;
102
103 ev_perf_info.num_clusters = cluster_collection->size();
104
105 for (const auto & cell_ptr : *cell_collection)
106 {
107 const float energy = cell_ptr->energy();
108 const float noise = m_twoGaussianNoise ?
109 noise_tool->getEffectiveSigma(cell_ptr->ID(), cell_ptr->gain(), energy) :
110 noise_tool->getNoise(cell_ptr->ID(), cell_ptr->gain());
111 const float s_n_r = (noise > 0 ? energy / noise : 0.00001f);
112 const float abs_s_n_r = fabsf(s_n_r);
113
114 const int this_hash_ID = cell_ptr->caloDDE()->calo_hash();
115
116 if (s_n_r >= m_seedThreshold || (m_seedCutsInAbsE && abs_s_n_r >= m_seedThreshold))
117 {
118 cell_classification[this_hash_ID] = 3;
119 ++ev_perf_info.total_seed;
120 }
121 else if (s_n_r >= m_growThreshold || (m_neighborCutsInAbsE && abs_s_n_r >= m_growThreshold))
122 {
123 cell_classification[this_hash_ID] = 2;
124 ++ev_perf_info.total_grow;
125 }
126 else if (s_n_r >= m_cellThreshold || (m_cellCutsInAbsE && abs_s_n_r >= m_cellThreshold))
127 {
128 cell_classification[this_hash_ID] = 1;
129 ++ev_perf_info.total_term;
130 }
131 else
132 {
133 cell_classification[this_hash_ID] = 0;
134 ++ev_perf_info.total_invalid;
135 }
136 }
137
138 auto accumulate_stats = [&](EventPerformanceInfo::Stats & s, const double v)
139 {
140 s.min = std::min(s.min, v);
141 s.max = std::max(s.max, v);
142 s.avg += v;
143 s.stddev += v * v;
144 };
145
146 int cluster_count = 0;
147
148 std::vector<int> cluster_assignment(NCaloCells, -1);
149
150 constexpr int check_seed_flag = 0x40000000;
151 constexpr int check_first_flag = 0x20000000;
152 //Since we only have 187652 cells, this works...
153
154 constexpr int check_mask = ~(check_seed_flag | check_first_flag);
155
156 std::vector<int> cells_to_check;
157 std::vector<int> new_cells_to_check;
158 std::vector<IdentifierHash> neighs;
159
160 auto check_for_restrict = [&] (const auto & hash_ID, const bool restrict_HECIWandFCal, const bool restrict_PS)
161 {
162 const Identifier cell_identifier = m_calo_id->cell_id(hash_ID);
163
164 const auto sub_calo = m_calo_id->sub_calo(cell_identifier);
165 const auto region = m_calo_id->region(cell_identifier);
166 const auto intra_calo_sampling = m_calo_id->sampling(cell_identifier);
167
168 const bool is_PS = (sub_calo == CaloCell_ID::LAREM && intra_calo_sampling == 0);
169
170 const bool is_HECIW_or_FCAL = ( (sub_calo == CaloCell_ID::LARHEC && region == 1 ) ||
171 (sub_calo == CaloCell_ID::LARFCAL && intra_calo_sampling > 1 ) );
172
173 return (is_HECIW_or_FCAL && restrict_HECIWandFCal) || (is_PS && restrict_PS);
174 };
175
176 for (const auto & cluster_ptr : *cluster_collection)
177 {
178 int this_seed = 0, this_grow = 0, this_term = 0;
179
180 cells_to_check.clear();
181
182 bool first = true;
183
184 for (const CaloCell * cell_ptr : *cluster_ptr)
185 {
186 const int this_hash_ID = cell_ptr->caloDDE()->calo_hash();
187
188 cluster_assignment[this_hash_ID] = cluster_count | check_seed_flag | (first ? 0 : check_first_flag);
189
190 const unsigned char this_classification = cell_classification[this_hash_ID];
191
192 switch (this_classification)
193 {
194 case 3:
195 ++this_seed;
196 cluster_assignment[this_hash_ID] = cluster_count | (first ? 0 : check_first_flag);
197 cells_to_check.push_back(this_hash_ID);
198 break;
199 case 2:
200 ++this_grow;
201 break;
202 case 1:
203 ++this_term;
204 break;
205 default:
206 ATH_MSG_WARNING("Invalid cell " << this_hash_ID << " in cluster " << cluster_count << ".");
207 break;
208 }
209
210 first = false;
211 }
212
213 ev_perf_info.seed_in_cluster += this_seed;
214 ev_perf_info.grow_in_cluster += this_grow;
215 ev_perf_info.term_in_cluster += this_term;
216
217 accumulate_stats(ev_perf_info.cluster_size, cluster_ptr->size());
218 accumulate_stats(ev_perf_info.cluster_num_seed, this_seed);
219 accumulate_stats(ev_perf_info.cluster_num_grow, this_grow);
220 accumulate_stats(ev_perf_info.cluster_num_term, this_term);
221
222 //Now for the complicated part: the radius...
223
224
225 int min_radius = -1, radius = -1;
226
227 while (cells_to_check.size() > 0)
228 {
229 ++radius;
230
231 new_cells_to_check.clear();
232
233 for (const auto & hash_ID : cells_to_check)
234 {
235 if (min_radius < 0 && cell_classification[hash_ID] <= 1)
236 {
237 min_radius = radius;
238 //We have reached a terminal cell: this is the minimum radius.
239 }
240
241 const bool restrict = check_for_restrict(hash_ID,
244
245 neighs.clear();
246
248 {
249 m_calo_id->get_neighbours(hash_ID, LArNeighbours::nextInSamp, neighs);
250 }
251 else
252 {
253 m_calo_id->get_neighbours(hash_ID, m_growNeighborOption, neighs);
254 }
255
256 for (const auto & neigh_hash : neighs)
257 {
258 int & neigh_assignment = cluster_assignment[neigh_hash];
259 if ((neigh_assignment & check_seed_flag) && ((neigh_assignment & check_mask) == cluster_count))
260 {
261 new_cells_to_check.push_back(neigh_hash) ;
262 neigh_assignment = (neigh_assignment & ~check_seed_flag);
263 }
264 }
265 }
266
267 cells_to_check.swap(new_cells_to_check);
268 }
269
270 accumulate_stats(ev_perf_info.cluster_seed_min_radius, min_radius);
271 accumulate_stats(ev_perf_info.cluster_seed_max_radius, radius);
272
273 //And now the radius from the first cell
274 //(for split clusters)
275
276 if (cluster_ptr->size() > 0)
277 {
278 cells_to_check.clear();
279 cells_to_check.push_back(cluster_ptr->begin()->caloDDE()->calo_hash());
280
281 radius = -1;
282 min_radius = -1;
283
284 while (cells_to_check.size() > 0)
285 {
286 ++radius;
287
288 new_cells_to_check.clear();
289
290 for (const auto & hash_ID : cells_to_check)
291 {
292 const bool restrict = check_for_restrict(hash_ID,
295
296
297 neighs.clear();
298
300 {
301 m_calo_id->get_neighbours(hash_ID, LArNeighbours::nextInSamp, neighs);
302 }
303 else
304 {
305 m_calo_id->get_neighbours(hash_ID, m_splitNeighborOption, neighs);
306 }
307
308 for (const auto & neigh_hash : neighs)
309 {
310 int & neigh_assignment = cluster_assignment[neigh_hash];
311 if ((neigh_assignment & check_first_flag) && ((neigh_assignment & check_mask) == cluster_count))
312 {
313 new_cells_to_check.push_back(neigh_hash) ;
314 neigh_assignment = (neigh_assignment & ~check_first_flag);
315 }
316 }
317 }
318
319 cells_to_check.swap(new_cells_to_check);
320 }
321
322 accumulate_stats(ev_perf_info.cluster_first_max_radius, radius);
323 }
324
325 ++cluster_count;
326 }
327
328
329 auto finalize_stats = [&](EventPerformanceInfo::Stats & s)
330 {
331 s.avg /= cluster_collection->size();
332 s.stddev /= cluster_collection->size();
333 s.stddev -= s.avg * s.avg;
334 //Sure, floating point accuracy issues may ensue,
335 //but not the most relevant anyway...
336 };
337
338 finalize_stats(ev_perf_info.cluster_size);
339 finalize_stats(ev_perf_info.cluster_num_seed);
340 finalize_stats(ev_perf_info.cluster_num_grow);
341 finalize_stats(ev_perf_info.cluster_num_term);
342 finalize_stats(ev_perf_info.cluster_seed_min_radius);
343 finalize_stats(ev_perf_info.cluster_seed_max_radius);
344 finalize_stats(ev_perf_info.cluster_first_max_radius);
345
346 {
347 std::lock_guard<std::mutex> lock_guard(m_mutex);
348
349 m_eventInfo.push_back(ev_perf_info);
350 m_eventNumbers.push_back(ctx.evt());
351 }
352
353 return StatusCode::SUCCESS;
354}
355
356
358{
359 if (m_fileName.size() > 0)
360 {
361 std::ofstream out(m_fileName);
362
363 std::vector<size_t> indices(m_eventNumbers.size());
364
365 std::iota(indices.begin(), indices.end(), 0);
366 std::sort(indices.begin(), indices.end(), [&](size_t a, size_t b)
367 {
368 return m_eventNumbers[a] < m_eventNumbers[b];
369 }
370 );
371
372 out << "Event_Number Number_Clusters "
373 << "Total_Seed Total_Grow Total_Term Total_Invalid "
374 << "Seed_In_Cluster Term_In_Cluster";
375
376 auto print_stat_name = [&](const auto & name)
377 {
378 out << " " << name << "_Min " << name << "_Max " << name << "_Avg " << name << "_Stddev";
379 };
380
381 print_stat_name("Size");
382 print_stat_name("Num_Seed");
383 print_stat_name("Num_Grow");
384 print_stat_name("Num_Term");
385 print_stat_name("Radius_Seed_Min");
386 print_stat_name("Radius_Seed_Max");
387 print_stat_name("Radius_First");
388
389 out << "\n";
390
391 auto print_stat = [&](const EventPerformanceInfo::Stats & s)
392 {
393 out << " " << s.min << " " << s.max << " " << s.avg << " " << s.stddev;
394 };
395
396 for (const auto & idx : indices)
397 {
398 out << m_eventNumbers[idx] << " ";
399
400 const auto & info = m_eventInfo[idx];
401
402 out << info.num_clusters << " " << info.total_seed << " " << info.total_grow << " "
403 << info.total_term << " " << info.total_invalid << " " << info.seed_in_cluster << " "
404 << info.grow_in_cluster << " " << info.term_in_cluster;
405
406 print_stat(info.cluster_size);
407 print_stat(info.cluster_num_seed);
408 print_stat(info.cluster_num_grow);
409 print_stat(info.cluster_num_term);
410 print_stat(info.cluster_seed_min_radius);
411 print_stat(info.cluster_seed_max_radius);
412 print_stat(info.cluster_first_max_radius);
413
414 out << "\n";
415 }
416
417 out << std::endl;
418
419 out.close();
420
421 }
422
423 return StatusCode::SUCCESS;
424}
#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.