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