ATLAS Offline Software
Loading...
Searching...
No Matches
CaloGPUClusterAndCellDataMonitor.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
7
8#ifndef CALORECGPU_DATA_MONITOR_EXTRA_PRINTOUTS
9
10 #define CALORECGPU_DATA_MONITOR_EXTRA_PRINTOUTS 0
11
12#endif
13
15#include "CaloRecGPU/Helpers.h"
20#include "CaloRecUtilities.h"
22
23#include "CLHEP/Units/SystemOfUnits.h"
24
25#include <map>
26#include <numeric>
27#include <algorithm>
28#include <string_view>
29
30using namespace CaloRecGPU;
31
32CaloGPUClusterAndCellDataMonitor::CaloGPUClusterAndCellDataMonitor(const std::string & type, const std::string & name, const IInterface * parent):
33 base_class(type, name, parent),
35{
36}
37
39{
40 ATH_CHECK( m_cellsKey.initialize() );
41
42 ATH_CHECK( detStore()->retrieve(m_calo_id, "CaloCell_ID") );
43
44 const std::string this_name = this->name();
45
46 const std::string algorithm_name_prefix = this_name.substr(0, this_name.rfind('.'));
47 //This is so we take into account the fact that tools
48 //are prefixed with the parent algorithm's name.
49
50 auto final_string = [& algorithm_name_prefix](const std::string & unpref_str) -> std::string
51 {
52 return algorithm_name_prefix + "." + unpref_str;
53 };
54
56
57 m_min_similarity = opts.min_similarity;
58 m_seed_weight = opts.seed_w;
59 m_grow_weight = opts.grow_w;
60 m_terminal_weight = opts.term_w;
61
62 for (const auto & tool : m_toolsToPlot)
63 {
64 const std::string tool_name = final_string(tool.tool);
65 m_toolToIdMap[tool_name] = tool.plot_id;
66 m_toolsToCheckFor[tool_name] = -1;
67 }
68
69 auto add_tool_from_pair = [this](const std::string & name) -> int
70 {
71 if (!m_toolsToCheckFor.count(name))
72 {
74 m_toolToIdMap[name] = "";
75 return m_numToolsToKeep++;
76 }
77 else
78 {
79 const int current = m_toolsToCheckFor[name];
80 if (current >= 0)
81 {
82 return current;
83 }
84 else
85 {
87 return m_numToolsToKeep++;
88 }
89 }
90 };
91
92 for (const auto & pair : m_pairsToPlot)
93 {
94 const int first_index = add_tool_from_pair(final_string(pair.tool_ref));
95 const int second_index = add_tool_from_pair(final_string(pair.tool_test));
96 m_toolCombinations.emplace_back(pair_to_plot{first_index, second_index, pair.plot_id,
97 pair.match_in_energy,
98 pair.match_without_shared,
99 pair.match_perfectly});
100 }
101
102 ATH_CHECK( m_moniTool.retrieve() );
103
104 return StatusCode::SUCCESS;
105}
106
108{
109 //Well, not do plots, just monitor the number of events and the total number of clusters...
110
111 auto mon_num_events = Monitored::Scalar("num_events", m_numEvents);
112
113 for (const auto & k_v : m_toolToIdMap)
114 {
115 auto mon_num_clust = Monitored::Scalar(k_v.second + "_num_total_clusters", m_numClustersPerTool.at(k_v.first).load());
116 }
117
118 return StatusCode::SUCCESS;
119}
120
121StatusCode CaloGPUClusterAndCellDataMonitor::update_plots_start(const EventContext & /*ctx*/,
122 const ConstantDataHolder & /*constant_data*/,
123 const xAOD::CaloClusterContainer * /*cluster_collection_ptr*/) const
124{
126 {
127 std::lock_guard<std::mutex> lock_guard(m_mutex);
129 {
131 //We have the mutex.
132 //It's safe.
133 ATH_CHECK( dhis->initialize_plotted_variables() );
135 }
136 }
137 if (m_numToolsToKeep > 0)
138 {
139 m_storageHolder.get_one().resize(m_numToolsToKeep);
140 }
141 //Allocate a vector of data holders for this thread and resize it to the necessary size.
142
143 return StatusCode::SUCCESS;
144}
145
146StatusCode CaloGPUClusterAndCellDataMonitor::update_plots_end(const EventContext & ctx,
147 const ConstantDataHolder & constant_data,
148 const xAOD::CaloClusterContainer * /*cluster_collection_ptr*/) const
149{
150 ATH_MSG_INFO("");
151
152 for (const auto & combination : m_toolCombinations)
153 {
154 if (combination.index_ref < 0 || combination.index_test < 0)
155 {
156 ATH_MSG_WARNING("Invalid tool combination, please check your configuration! " << combination.prefix);
157 continue;
158 }
159 ATH_CHECK( add_combination(ctx, constant_data, combination.index_ref, combination.index_test, combination.prefix,
160 combination.match_in_energy, combination.match_without_shared, combination.match_perfectly) );
161 }
162
163 ATH_MSG_INFO("");
164
165 if (m_numToolsToKeep > 0)
166 {
167 m_storageHolder.release_one();
168 //Release the tool storage.
169 }
170
171 return StatusCode::SUCCESS;
172}
173
174StatusCode CaloGPUClusterAndCellDataMonitor::update_plots(const EventContext & ctx,
175 const ConstantDataHolder & constant_data,
176 const xAOD::CaloClusterContainer * cluster_collection_ptr,
177 const CaloClusterCollectionProcessor * tool) const
178{
179 if (filter_tool_by_name(tool->name()))
180 {
181 SG::ReadHandle<CaloCellContainer> cell_collection(m_cellsKey, ctx);
182
184
185 ed.allocate(false);
186
187 ed.importCells(static_cast<const CaloCellContainer *>(&(*cell_collection)), m_missingCellsToFill);
188
189 ed.importClusters(cluster_collection_ptr, MomentsOptionsArray::all(), false, true, true, true, m_missingCellsToFill);
190
191 std::vector<int> cells_prefix_sum;
192
193 ATH_CHECK(update_cell_representation(ctx, constant_data, ed.m_cell_info, ed.m_clusters, cells_prefix_sum));
194
195 return add_data(ctx, constant_data, ed.m_cell_info, ed.m_clusters, cells_prefix_sum, tool->name());
196 }
197 else
198 {
199 return StatusCode::SUCCESS;
200 }
201}
202
203StatusCode CaloGPUClusterAndCellDataMonitor::update_plots(const EventContext & ctx,
204 const ConstantDataHolder & constant_data,
205 const xAOD::CaloClusterContainer * /*cluster_collection_ptr*/,
206 const EventDataHolder & event_data,
207 const ICaloClusterGPUInputTransformer * tool) const
208{
209 if (filter_tool_by_name(tool->name()))
210 {
213
214 std::vector<int> cells_prefix_sum;
215
216 ATH_CHECK(update_cell_representation(ctx, constant_data, cell_info, clusters, cells_prefix_sum));
217
218 return add_data(ctx, constant_data, cell_info, clusters, cells_prefix_sum, tool->name());
219 }
220 else
221 {
222 return StatusCode::SUCCESS;
223 }
224}
225
226StatusCode CaloGPUClusterAndCellDataMonitor::update_plots(const EventContext & ctx,
227 const ConstantDataHolder & constant_data,
228 const xAOD::CaloClusterContainer * /*cluster_collection_ptr*/,
229 const EventDataHolder & event_data,
230 const CaloClusterGPUProcessor * tool) const
231{
232 if (filter_tool_by_name(tool->name()))
233 {
236
237 std::vector<int> cells_prefix_sum;
238
239 ATH_CHECK(update_cell_representation(ctx, constant_data, cell_info, clusters, cells_prefix_sum));
240
241 return add_data(ctx, constant_data, cell_info, clusters, cells_prefix_sum, tool->name());
242 }
243 else
244 {
245 return StatusCode::SUCCESS;
246 }
247}
248
249StatusCode CaloGPUClusterAndCellDataMonitor::update_plots(const EventContext & ctx,
250 const ConstantDataHolder & constant_data,
251 const xAOD::CaloClusterContainer * cluster_collection_ptr,
252 const EventDataHolder & /*event_data*/,
253 const ICaloClusterGPUOutputTransformer * tool) const
254{
255 if (filter_tool_by_name(tool->name()))
256 {
257 SG::ReadHandle<CaloCellContainer> cell_collection(m_cellsKey, ctx);
258
260
261 ed.allocate(false);
262
263 ed.importCells(static_cast<const CaloCellContainer *>(&(*cell_collection)), m_missingCellsToFill);
264
265 ed.importClusters(cluster_collection_ptr, MomentsOptionsArray::all(), false, true, true, true, m_missingCellsToFill);
266
267 std::vector<int> cells_prefix_sum;
268
269 ATH_CHECK(update_cell_representation(ctx, constant_data, ed.m_cell_info, ed.m_clusters, cells_prefix_sum));
270
271 return add_data(ctx, constant_data, ed.m_cell_info, ed.m_clusters, cells_prefix_sum, tool->name());
272 }
273 else
274 {
275 return StatusCode::SUCCESS;
276 }
277}
278
279bool CaloGPUClusterAndCellDataMonitor::filter_tool_by_name(const std::string & tool_name) const
280{
281 ATH_MSG_DEBUG("Checking : '" << tool_name << "': " << m_toolsToCheckFor.count(tool_name));
282 return m_toolsToCheckFor.count(tool_name) > 0;
283}
284
285
286
287
290 const CaloRecGPU::CellInfoArr * /*cell_info*/,
292 std::vector<int> & cells_prefix_sum) const
293{
294 if (!clusters->has_cells_per_cluster())
295 {
296 for (int i = 0; i <= clusters->number; ++i)
297 {
298 clusters->cellsPrefixSum[i] = 0;
299 }
300
301 for (int i = 0; i < clusters->number_cells; ++i)
302 {
303 clusters->get_extra_cell_info(i) = clusters->cells.tags[i];
304 //We overwrite some non-calculated moments that we shouldn't even have
305 //at this point...
306 }
307
308 const int old_num_cells = clusters->number_cells;
309 clusters->number_cells = 0;
310 for (int i = 0; i < old_num_cells; ++i)
311 {
312 ClusterTag this_tag = clusters->get_extra_cell_info(i);
313
314 const int first_cluster = this_tag.is_part_of_cluster() ? this_tag.cluster_index() : -1;
315
316 const int second_cluster = this_tag.is_part_of_cluster() && this_tag.is_shared_between_clusters() ? this_tag.secondary_cluster_index() : -1;
317
318 const float secondary_cluster_weight = (this_tag.is_part_of_cluster() && this_tag.is_shared_between_clusters() ?
319 float_unhack(this_tag.secondary_cluster_weight()) : 0.f);
320
321 if (second_cluster >= 0)
322 {
323 if (second_cluster >= clusters->number)
324 {
325 ATH_MSG_WARNING("Impossible cell assignment: " << i << " " << second_cluster << " (" << std::hex << this_tag << std::dec << ")");
326 }
327 clusters->cells.indices[clusters->number_cells] = i;
328 clusters->cellWeights[clusters->number_cells] = secondary_cluster_weight;
329 clusters->clusterIndices[clusters->number_cells] = second_cluster;
330 clusters->number_cells += 1;
331 clusters->cellsPrefixSum[second_cluster + 1] += 1;
332 }
333
334 if (first_cluster >= 0)
335 {
336 if (second_cluster >= clusters->number)
337 {
338 ATH_MSG_WARNING("Impossible cell assignment: " << i << " " << first_cluster << " (" << std::hex << this_tag << std::dec << ")");
339 }
340 clusters->cells.indices[clusters->number_cells] = i;
341 clusters->cellWeights[clusters->number_cells] = 1.0f - secondary_cluster_weight;
342 clusters->clusterIndices[clusters->number_cells] = first_cluster;
343 clusters->number_cells += 1;
344 clusters->cellsPrefixSum[first_cluster + 1] += 1;
345 }
346 }
347 int prefix = 0;
348 for (int i = 0; i <= clusters->number; ++i)
349 {
350 prefix += clusters->cellsPrefixSum[i];
351
352 clusters->cellsPrefixSum[i] = prefix;
353 }
354 }
355
356 //Do note that, from here on, the assignment between cells and clusters is broken:
357 //we simply have a list of cells in clusters...
358
359 std::vector<int> cell_orderer(clusters->number_cells);
360
361 std::iota(cell_orderer.begin(), cell_orderer.end(), 0);
362
363 std::sort(cell_orderer.begin(), cell_orderer.end(), [&](const int a, const int b)
364 {
365 if (clusters->cells.indices[a] == clusters->cells.indices[b])
366 {
367 return clusters->cellWeights[a] < clusters->cellWeights[b];
368 }
369 else
370 {
371 return clusters->cells.indices[a] < clusters->cells.indices[b];
372 }
373 } );
374
375 auto order_array = [&](auto * ptr)
376 {
377 std::vector<std::decay_t<decltype(ptr[0])>> prev(clusters->number_cells);
378
379 for (int i = 0; i < clusters->number_cells; ++i)
380 {
381 prev[i] = ptr[i];
382 }
383
384 for (int i = 0; i < clusters->number_cells; ++i)
385 {
386 ptr[i] = prev[cell_orderer[i]];
387 }
388 };
389
390 order_array(clusters->cells.indices);
391 order_array(clusters->cellWeights);
392 order_array(clusters->clusterIndices);
393
394 cells_prefix_sum.clear();
395 cells_prefix_sum.resize(NCaloCells + 1, 0);
396
397 int prev_cell = -1;
398 int cell_count = 0;
399
400 for (int i = 0; i < clusters->number_cells; ++i)
401 {
402 const int this_cell = clusters->cells.indices[i];
403
404 if (this_cell != prev_cell)
405 {
406 for (int j = prev_cell + 1; j <= this_cell; ++j)
407 {
408 cells_prefix_sum[j] = cell_count;
409 }
410 }
411
412 ++cell_count;
413
414 prev_cell = this_cell;
415 }
416
417 if (clusters->number_cells > 0)
418 {
419 for (int i = clusters->cells.indices[clusters->number_cells - 1]; i <= NCaloCells; ++i)
420 {
421 cells_prefix_sum[i + 1] = cell_count;
422 }
423 }
424
425 return StatusCode::SUCCESS;
426}
427
428
429//WeighMatch takes as arguments the cell index
430//and two vectors of pairs of cluster index and cell weight,
431//for the reference and test assignments.
432//WeighMatch takes as arguments a bool that indicates
433//whether this assignment is for test (false for reference),
434//and a vector of pairs of cluster index and cell weight.
435template <class WeighMatch, class WeighNotMatch>
437 const CaloRecGPU::ClusterInfoArr & cluster_info_2,
438 WeighMatch match,
439 WeighNotMatch not_match)
440{
441 int it_1 = 0, it_2 = 0;
442
443 std::vector<std::pair<int, float>> cluster_weights_1, cluster_weights_2;
444 int this_index_1 = -1;
445 int this_index_2 = -1;
446
447 while (it_1 < cluster_info_1.number_cells || it_2 < cluster_info_2.number_cells)
448 {
449 if (it_1 < cluster_info_1.number_cells)
450 {
451 this_index_1 = cluster_info_1.cells.indices[it_1];
452 }
453 else
454 {
455 this_index_1 = -1;
456 }
457
458 if (it_2 < cluster_info_2.number_cells)
459 {
460 this_index_2 = cluster_info_2.cells.indices[it_2];
461 }
462 else
463 {
464 this_index_2 = -1;
465 }
466
467 if (cluster_weights_1.size() == 0)
468 {
469 while (it_1 < cluster_info_1.number_cells && cluster_info_1.cells.indices[it_1] == this_index_1)
470 {
471 cluster_weights_1.push_back({cluster_info_1.clusterIndices[it_1], cluster_info_1.cellWeights[it_1] + 1e-8});
472 ++it_1;
473 }
474 }
475
476 if (cluster_weights_2.size() == 0)
477 {
478 while (it_2 < cluster_info_2.number_cells && cluster_info_2.cells.indices[it_2] == this_index_2)
479 {
480 cluster_weights_2.push_back({cluster_info_2.clusterIndices[it_2], cluster_info_2.cellWeights[it_2] + 1e-8});
481 ++it_2;
482 }
483 }
484
485 if (this_index_1 == this_index_2 and this_index_1 >= 0)
486 {
487 match(this_index_1, cluster_weights_1, cluster_weights_2);
488 cluster_weights_1.clear();
489 cluster_weights_2.clear();
490 }
491 else if ((this_index_1 > this_index_2) and this_index_2 >= 0)
492 {
493 not_match(true, this_index_2, cluster_weights_2);
494 cluster_weights_2.clear();
495 }
496 else if ((this_index_2 > this_index_1) and this_index_1 >= 0 )
497 {
498 not_match(false, this_index_1, cluster_weights_1);
499 cluster_weights_1.clear();
500 }
501 else
502 {
503 cluster_weights_1.clear();
504 cluster_weights_2.clear();
505 }
506 }
507}
508
510 const CaloRecGPU::ConstantDataHolder & constant_data,
511 const CaloRecGPU::CellInfoArr & cell_info,
512 const CaloRecGPU::ClusterInfoArr & cluster_info_1,
513 const CaloRecGPU::ClusterInfoArr & cluster_info_2,
514 const bool match_in_energy,
515 const bool match_without_shared) const
516{
517 sch.r2t_table.clear();
518 sch.r2t_table.resize(cluster_info_1.number, -1);
519
520 sch.t2r_table.clear();
521 sch.t2r_table.resize(cluster_info_2.number, -1);
522
523 std::vector<double> similarity_map(cluster_info_1.number * cluster_info_2.number, 0.);
524
525 std::vector<double> ref_normalization(cluster_info_1.number, 0.);
526 std::vector<double> test_normalization(cluster_info_2.number, 0.);
527
528 auto calculate_weight = [&](const int cell)
529 {
530 double SNR = 0.00001;
531
532 if (!cell_info.is_bad(cell))
533 {
534 const int gain = cell_info.gain[cell];
535
536 const double cellNoise = constant_data.m_cell_noise->get_noise(cell_info.get_hash_ID(cell), gain);
537 if (std::isfinite(cellNoise) && cellNoise > 0.0f)
538 {
539 SNR = std::abs(cell_info.energy[cell] / cellNoise);
540 }
541 }
542
543 const double quantity = ( match_in_energy ? std::abs(cell_info.energy[cell]) : SNR );
544 const double weight = (quantity + 1e-7) *
545 ( SNR > m_seedThreshold ? (match_in_energy ? 1000 : m_seed_weight) :
546 (
547 SNR > m_growThreshold ? (match_in_energy ? 950 : m_grow_weight) :
548 (
549 SNR > m_termThreshold ? (match_in_energy ? 900 : m_terminal_weight) : (match_in_energy ? 100 : 1e-8)
550 )
551 )
552 );
553
554 return weight + 1e-8;
555 };
556
557 auto matched_clusters = [&](const int cell, const std::vector<std::pair<int, float>> & v1, const std::vector<std::pair<int, float>> & v2)
558 {
559#if CALORECGPU_DATA_MONITOR_EXTRA_PRINTOUTS
560 msg(MSG::INFO) << "MATCH: " << cell << " " << calculate_weight(cell) << " |";
561 for (const auto & p : v1)
562 {
563 msg() << " (" << p.first << ", " << p.second << ")";
564 }
565 msg() << " |";
566 for (const auto & p : v2)
567 {
568 msg() << " (" << p.first << ", " << p.second << ")";
569 }
570 msg() << endmsg;
571#endif
572
573 if (match_without_shared && (v1.size() > 1 || v2.size() > 1))
574 {
575 return;
576 }
577
578 const float weight = calculate_weight(cell);
579
580 for (const auto & p1 : v1)
581 {
582 for (const auto & p2 : v2)
583 {
584 similarity_map[p2.first * cluster_info_1.number + p1.first] += weight * p1.second * p2.second;
585 }
586 }
587
588 for (const auto & p1 : v1)
589 {
590 ref_normalization[p1.first] += weight * p1.second * p1.second;
591 }
592
593 for (const auto & p2 : v2)
594 {
595 test_normalization[p2.first] += weight * p2.second * p2.second;
596 }
597 };
598
599 auto unmatched_clusters = [&](const bool is_test, const int cell, const std::vector<std::pair<int, float>> & v)
600 {
601#if CALORECGPU_DATA_MONITOR_EXTRA_PRINTOUTS
602 msg(MSG::INFO) << "UNMATCH: " << cell << " " << calculate_weight(cell) << " | " << is_test << " |";
603 for (const auto & p : v)
604 {
605 msg() << " (" << p.first << ", " << p.second << ")";
606 }
607 msg() << endmsg;
608#endif
609
610 if (match_without_shared && v.size() > 1)
611 {
612 return;
613 }
614
615 const float weight = calculate_weight(cell);
616
617 std::vector<double> & normalization = (is_test ? test_normalization : ref_normalization);
618
619 for (const auto & p : v)
620 {
621 normalization[p.first] += weight * p.second * p.second;
622 }
623 };
624
625 build_similarity_map_helper(cluster_info_1, cluster_info_2, matched_clusters, unmatched_clusters);
626
627 for (int testc = 0; testc < cluster_info_2.number; ++testc)
628 {
629 const double test_norm = test_normalization[testc] + double(test_normalization[testc] == 0.);
630 for (int refc = 0; refc < cluster_info_1.number; ++refc)
631 {
632 const double ref_norm = ref_normalization[refc] + double(ref_normalization[refc] == 0.);
633 similarity_map[testc * cluster_info_1.number + refc] /= std::sqrt(ref_norm * test_norm);
634 }
635 }
636
637 //In essence, the Gale-Shapley Algorithm
638
639 std::vector<std::vector<int>> sorted_GPU_matches;
640
641 sorted_GPU_matches.reserve(cluster_info_2.number);
642
643 for (int testc = 0; testc < cluster_info_2.number; ++testc)
644 {
645 std::vector<int> sorter(cluster_info_1.number);
646 std::iota(sorter.begin(), sorter.end(), 0);
647
648 std::sort(sorter.begin(), sorter.end(),
649 [&](const int a, const int b)
650 {
651 const double a_weight = similarity_map[testc * cluster_info_1.number + a];
652 const double b_weight = similarity_map[testc * cluster_info_1.number + b];
653 return a_weight > b_weight;
654 }
655 );
656
657 size_t wanted_size = 0;
658
659 for (; wanted_size < sorter.size(); ++wanted_size)
660 {
661 const double match_weight = similarity_map[testc * cluster_info_1.number + sorter[wanted_size]];
662 if (match_weight < m_min_similarity)
663 {
664 break;
665 }
666 }
667
668 //Yeah, we could do a binary search for best worst-case complexity,
669 //but we are expecting 1~2 similar clusters and the rest garbage,
670 //so we're expecting only 1~2 iterations.
671 //This actually means all that sorting is way way overkill,
672 //but we must make sure in the most general case that this works...
673
674 sorter.resize(wanted_size);
675
676 sorted_GPU_matches.push_back(std::move(sorter));
677 }
678
679 int num_iter = 0;
680
681 constexpr int max_iter = 32;
682
683 std::vector<double> matched_weights(cluster_info_1.number, -1.);
684
685 std::vector<size_t> skipped_matching(cluster_info_2.number, 0);
686
687 for (int stop_counter = 0; stop_counter < cluster_info_2.number && num_iter < max_iter; ++num_iter)
688 {
689 stop_counter = 0;
690 for (int testc = 0; testc < int(sorted_GPU_matches.size()); ++testc)
691 {
692 if (skipped_matching[testc] < sorted_GPU_matches[testc].size())
693 {
694 const int match_c = sorted_GPU_matches[testc][skipped_matching[testc]];
695 const double match_weight = similarity_map[testc * cluster_info_1.number + match_c];
696 if (match_weight >= m_min_similarity && match_weight > matched_weights[match_c])
697 {
698 const int prev_match = sch.r2t_table[match_c];
699 if (prev_match >= 0)
700 {
701 ++skipped_matching[prev_match];
702 --stop_counter;
703 }
704 sch.r2t_table[match_c] = testc;
705 matched_weights[match_c] = match_weight;
706 ++stop_counter;
707 }
708 else
709 {
710 ++skipped_matching[testc];
711 }
712 }
713 else
714 {
715 ++stop_counter;
716 }
717 }
718 }
719
720 sch.unmatched_ref_list.clear();
721 sch.unmatched_test_list.clear();
722
723 for (size_t i = 0; i < sch.r2t_table.size(); ++i)
724 {
725 const int match = sch.r2t_table[i];
726 if (match < 0)
727 {
728 sch.unmatched_ref_list.push_back(i);
729 }
730 else
731 {
732 sch.t2r_table[match] = i;
733 }
734 }
735
736 for (size_t i = 0; i < sch.t2r_table.size(); ++i)
737 {
738 if (sch.t2r_table[i] < 0)
739 {
740 sch.unmatched_test_list.push_back(i);
741 }
742 }
743
744 {
745 char message_buffer[256];
746 snprintf(message_buffer, 256,
747 "%2d: %5d / %5d || %5d / %5d || %3d || %5d | %5d || %5d",
748 num_iter,
749 int(sch.r2t_table.size()) - int(sch.unmatched_ref_list.size()), int(sch.r2t_table.size()),
750 int(sch.t2r_table.size()) - int(sch.unmatched_test_list.size()), int(sch.t2r_table.size()),
751 int(sch.r2t_table.size()) - int(sch.t2r_table.size()),
752 int(sch.unmatched_ref_list.size()),
753 int(sch.unmatched_test_list.size()),
754 int(sch.unmatched_ref_list.size()) - int(sch.unmatched_test_list.size())
755 );
756 ATH_MSG_INFO(message_buffer);
757 }
758
759 return StatusCode::SUCCESS;
760
761}
762
764 const CaloRecGPU::ConstantDataHolder & /*constant_data*/,
765 const CaloRecGPU::CellInfoArr & /*cell_info*/,
766 const CaloRecGPU::ClusterInfoArr & cluster_info_1,
767 const CaloRecGPU::ClusterInfoArr & cluster_info_2,
768 const bool match_without_shared) const
769{
770 sch.r2t_table.clear();
771 sch.r2t_table.resize(cluster_info_1.number, -1);
772
773 sch.t2r_table.clear();
774 sch.t2r_table.resize(cluster_info_2.number, -1);
775
776 std::vector<char> match_possibilities(cluster_info_1.number * cluster_info_2.number, 1);
777
778 auto matched_clusters = [&]([[maybe_unused]] const int cell, const std::vector<std::pair<int, float>> & v1, const std::vector<std::pair<int, float>> & v2)
779 {
780#if CALORECGPU_DATA_MONITOR_EXTRA_PRINTOUTS
781 msg(MSG::INFO) << "MATCH: " << cell << " <> |";
782 for (const auto & p : v1)
783 {
784 msg() << " (" << p.first << ", " << p.second << ")";
785 }
786 msg() << " |";
787 for (const auto & p : v2)
788 {
789 msg() << " (" << p.first << ", " << p.second << ")";
790 }
791 msg() << endmsg;
792#endif
793
794 if (match_without_shared && (v1.size() > 1 || v2.size() > 1))
795 {
796 return;
797 }
798
799 for (const auto & t_p : v2)
800 {
801 for (int rc = 0; rc < cluster_info_1.number; ++rc)
802 {
803 bool is_possibility = false;
804
805 for (const auto & p: v1)
806 {
807 if (p.first == rc)
808 {
809 is_possibility = true;
810 break;
811 }
812 }
813
814 if (is_possibility)
815 {
816 continue;
817 }
818
819 match_possibilities[t_p.first * cluster_info_1.number + rc] = 0;
820 }
821 }
822
823 for (const auto & r_p : v1)
824 {
825 for (int tc = 0; tc < cluster_info_2.number; ++tc)
826 {
827 bool is_possibility = false;
828
829 for (const auto & p: v2)
830 {
831 if (p.first == tc)
832 {
833 is_possibility = true;
834 break;
835 }
836 }
837
838 if (is_possibility)
839 {
840 continue;
841 }
842
843 match_possibilities[tc * cluster_info_1.number + r_p.first] = 0;
844 }
845 }
846 };
847
848 auto unmatched_clusters = [&](const bool is_test, [[maybe_unused]] const int cell, const std::vector<std::pair<int, float>> & v)
849 {
850#if CALORECGPU_DATA_MONITOR_EXTRA_PRINTOUTS
851 msg(MSG::INFO) << "UNMATCH: " << cell << " <> | " << is_test << " |";
852 for (const auto & p : v)
853 {
854 msg() << " (" << p.first << ", " << p.second << ")";
855 }
856 msg() << endmsg;
857#endif
858
859 if (match_without_shared && v.size() > 1)
860 {
861 return;
862 }
863
864 const int cluster_number = is_test ? cluster_info_2.number : cluster_info_1.number;
865
866 for (const auto & p : v)
867 {
868 for (int i = 0; i < cluster_number; ++i)
869 {
870 const int this_index = (is_test ? p.first * cluster_info_1.number + i : i * cluster_info_1.number + p.first);
871 match_possibilities[this_index] = 0;
872 }
873 }
874 };
875
876 build_similarity_map_helper(cluster_info_1, cluster_info_2, matched_clusters, unmatched_clusters);
877
878 for (int testc = 0; testc < cluster_info_2.number; ++testc)
879 {
880 for (int refc = 0; refc < cluster_info_1.number; ++refc)
881 {
882 if (match_possibilities[testc * cluster_info_1.number + refc] > 0)
883 {
884 sch.r2t_table[refc] = testc;
885 sch.t2r_table[testc] = refc;
886 }
887 }
888 }
889
890 for (int refc = 0; refc < cluster_info_1.number; ++refc)
891 {
892 if (sch.r2t_table[refc] < 0)
893 {
894 sch.unmatched_ref_list.push_back(refc);
895 }
896 }
897
898 for (int testc = 0; testc < cluster_info_2.number; ++testc)
899 {
900 if (sch.t2r_table[testc] < 0)
901 {
902 sch.unmatched_test_list.push_back(testc);
903 }
904 }
905
906 {
907 char message_buffer[256];
908 snprintf(message_buffer, 256,
909 "%2d: %5d / %5d || %5d / %5d || %3d || %5d | %5d || %5d",
910 0,
911 int(sch.r2t_table.size()) - int(sch.unmatched_ref_list.size()), int(sch.r2t_table.size()),
912 int(sch.t2r_table.size()) - int(sch.unmatched_test_list.size()), int(sch.t2r_table.size()),
913 int(sch.r2t_table.size()) - int(sch.t2r_table.size()),
914 int(sch.unmatched_ref_list.size()),
915 int(sch.unmatched_test_list.size()),
916 int(sch.unmatched_ref_list.size()) - int(sch.unmatched_test_list.size())
917 );
918 ATH_MSG_INFO(message_buffer);
919 }
920
921 return StatusCode::SUCCESS;
922
923}
924
925namespace
926{
927
929 {
930#define CALORECGPU_BASIC_CLUSTER_PROPERTY(NAME, ...) \
931 struct clusters_ ## NAME \
932 { \
933 static std::string name() \
934 { \
935 return # NAME; \
936 } \
937 static double get_property([[maybe_unused]] const ConstantDataHolder & constant_data, \
938 [[maybe_unused]] const CaloRecGPU::CellInfoArr & cell_info, \
939 [[maybe_unused]] const CaloRecGPU::ClusterInfoArr & cluster_info, \
940 [[maybe_unused]] const std::vector<int> & cells_prefix_sum, \
941 [[maybe_unused]] const int cluster_index ) \
942 { \
943 __VA_ARGS__ \
944 } \
945 };
946
947 CALORECGPU_BASIC_CLUSTER_PROPERTY(index, return cluster_index;)
948
949 CALORECGPU_BASIC_CLUSTER_PROPERTY(E, return cluster_info.clusterEnergy[cluster_index] / CLHEP::MeV;)
950
951 CALORECGPU_BASIC_CLUSTER_PROPERTY(abs_E, return std::abs(cluster_info.clusterEnergy[cluster_index]) / CLHEP::MeV;)
952
953 CALORECGPU_BASIC_CLUSTER_PROPERTY(Et, return cluster_info.clusterEt[cluster_index] / CLHEP::MeV;)
954
955 CALORECGPU_BASIC_CLUSTER_PROPERTY(eta, return cluster_info.clusterEta[cluster_index];)
956
957 CALORECGPU_BASIC_CLUSTER_PROPERTY(phi, return cluster_info.clusterPhi[cluster_index];)
958
959 CALORECGPU_BASIC_CLUSTER_PROPERTY(number_cells, return cluster_info.cellsPrefixSum[cluster_index + 1] - cluster_info.cellsPrefixSum[cluster_index];)
960
961 //CALORECGPU_BASIC_CLUSTER_PROPERTY(time, return cluster_moments.time[cluster_index] / CLHEP::us;)
962
963#define CALORECGPU_CLUSTER_MOMENT(...) CALORECGPU_CLUSTER_MOMENT_INNER(__VA_ARGS__, 1, 1)
964#define CALORECGPU_CLUSTER_MOMENT_INNER(NAME, PROPERTY, UNIT, ...) CALORECGPU_BASIC_CLUSTER_PROPERTY(moments_ ## NAME, return cluster_info.moments. PROPERTY [cluster_index] / UNIT;)
965
966 CALORECGPU_CLUSTER_MOMENT(time, time, CLHEP::us)
967 CALORECGPU_CLUSTER_MOMENT(FIRST_PHI, firstPhi)
968 CALORECGPU_CLUSTER_MOMENT(FIRST_ETA, firstEta)
969 CALORECGPU_CLUSTER_MOMENT(SECOND_R, secondR)
970 CALORECGPU_CLUSTER_MOMENT(SECOND_LAMBDA, secondLambda)
972 CALORECGPU_CLUSTER_MOMENT(DELTA_THETA, deltaTheta)
973 CALORECGPU_CLUSTER_MOMENT(DELTA_ALPHA, deltaAlpha)
974 CALORECGPU_CLUSTER_MOMENT(CENTER_X, centerX)
975 CALORECGPU_CLUSTER_MOMENT(CENTER_Y, centerY)
976 CALORECGPU_CLUSTER_MOMENT(CENTER_Z, centerZ)
977 CALORECGPU_CLUSTER_MOMENT(CENTER_MAG, centerMag)
978 CALORECGPU_CLUSTER_MOMENT(CENTER_LAMBDA, centerLambda)
979 CALORECGPU_CLUSTER_MOMENT(LATERAL, lateral)
980 CALORECGPU_CLUSTER_MOMENT(LONGITUDINAL, longitudinal)
981 CALORECGPU_CLUSTER_MOMENT(ENG_FRAC_EM, engFracEM)
982 CALORECGPU_CLUSTER_MOMENT(ENG_FRAC_MAX, engFracMax)
983 CALORECGPU_CLUSTER_MOMENT(ENG_FRAC_CORE, engFracCore)
984 CALORECGPU_CLUSTER_MOMENT(FIRST_ENG_DENS, firstEngDens)
985 CALORECGPU_CLUSTER_MOMENT(SECOND_ENG_DENS, secondEngDens)
986 CALORECGPU_CLUSTER_MOMENT(ISOLATION, isolation)
987 CALORECGPU_CLUSTER_MOMENT(ENG_BAD_CELLS, engBadCells)
988 CALORECGPU_CLUSTER_MOMENT(N_BAD_CELLS, nBadCells)
989 CALORECGPU_CLUSTER_MOMENT(N_BAD_CELLS_CORR, nBadCellsCorr)
990 CALORECGPU_CLUSTER_MOMENT(BAD_CELLS_CORR_E, badCellsCorrE)
991 CALORECGPU_CLUSTER_MOMENT(BADLARQ_FRAC, badLArQFrac)
992 CALORECGPU_CLUSTER_MOMENT(ENG_POS, engPos)
993 CALORECGPU_CLUSTER_MOMENT(SIGNIFICANCE, significance)
994 CALORECGPU_CLUSTER_MOMENT(CELL_SIGNIFICANCE, cellSignificance)
995 CALORECGPU_CLUSTER_MOMENT(CELL_SIG_SAMPLING, cellSigSampling)
996 CALORECGPU_CLUSTER_MOMENT(AVG_LAR_Q, avgLArQ)
997 CALORECGPU_CLUSTER_MOMENT(AVG_TILE_Q, avgTileQ)
998 CALORECGPU_CLUSTER_MOMENT(ENG_BAD_HV_CELLS, engBadHVCells)
999 CALORECGPU_CLUSTER_MOMENT(N_BAD_HV_CELLS, nBadHVCells)
1001 CALORECGPU_CLUSTER_MOMENT(MASS, mass)
1002 CALORECGPU_CLUSTER_MOMENT(EM_PROBABILITY, EMProbability)
1003 CALORECGPU_CLUSTER_MOMENT(HAD_WEIGHT, hadWeight)
1004 CALORECGPU_CLUSTER_MOMENT(OOC_WEIGHT, OOCweight)
1005 CALORECGPU_CLUSTER_MOMENT(DM_WEIGHT, DMweight)
1006 CALORECGPU_CLUSTER_MOMENT(TILE_CONFIDENCE_LEVEL, tileConfidenceLevel)
1007 CALORECGPU_CLUSTER_MOMENT(SECOND_TIME, secondTime)
1008 CALORECGPU_CLUSTER_MOMENT(VERTEX_FRACTION, vertexFraction)
1009 CALORECGPU_CLUSTER_MOMENT(NVERTEX_FRACTION, nVertexFraction)
1010 CALORECGPU_CLUSTER_MOMENT(ETACALOFRAME, etaCaloFrame)
1011 CALORECGPU_CLUSTER_MOMENT(PHICALOFRAME, phiCaloFrame)
1012 CALORECGPU_CLUSTER_MOMENT(ETA1CALOFRAME, eta1CaloFrame)
1013 CALORECGPU_CLUSTER_MOMENT(PHI1CALOFRAME, phi1CaloFrame)
1014 CALORECGPU_CLUSTER_MOMENT(ETA2CALOFRAME, eta2CaloFrame)
1015 CALORECGPU_CLUSTER_MOMENT(PHI2CALOFRAME, phi2CaloFrame)
1016 CALORECGPU_CLUSTER_MOMENT(ENG_CALIB_TOT, engCalibTot)
1017 CALORECGPU_CLUSTER_MOMENT(ENG_CALIB_OUT_L, engCalibOutL)
1018 CALORECGPU_CLUSTER_MOMENT(ENG_CALIB_OUT_M, engCalibOutM)
1019 CALORECGPU_CLUSTER_MOMENT(ENG_CALIB_OUT_T, engCalibOutT)
1020 CALORECGPU_CLUSTER_MOMENT(ENG_CALIB_DEAD_L, engCalibDeadL)
1021 CALORECGPU_CLUSTER_MOMENT(ENG_CALIB_DEAD_M, engCalibDeadM)
1022 CALORECGPU_CLUSTER_MOMENT(ENG_CALIB_DEAD_T, engCalibDeadT)
1023 CALORECGPU_CLUSTER_MOMENT(ENG_CALIB_EMB0, engCalibEMB0)
1024 CALORECGPU_CLUSTER_MOMENT(ENG_CALIB_EME0, engCalibEME0)
1025 CALORECGPU_CLUSTER_MOMENT(ENG_CALIB_TILEG3, engCalibTileG3)
1026 CALORECGPU_CLUSTER_MOMENT(ENG_CALIB_DEAD_TOT, engCalibDeadTot)
1027 CALORECGPU_CLUSTER_MOMENT(ENG_CALIB_DEAD_EMB0, engCalibDeadEMB0)
1028 CALORECGPU_CLUSTER_MOMENT(ENG_CALIB_DEAD_TILE0, engCalibDeadTile0)
1029 CALORECGPU_CLUSTER_MOMENT(ENG_CALIB_DEAD_TILEG3, engCalibDeadTileG3)
1030 CALORECGPU_CLUSTER_MOMENT(ENG_CALIB_DEAD_EME0, engCalibDeadEME0)
1031 CALORECGPU_CLUSTER_MOMENT(ENG_CALIB_DEAD_HEC0, engCalibDeadHEC0)
1032 CALORECGPU_CLUSTER_MOMENT(ENG_CALIB_DEAD_FCAL, engCalibDeadFCAL)
1033 CALORECGPU_CLUSTER_MOMENT(ENG_CALIB_DEAD_LEAKAGE, engCalibDeadLeakage)
1034 CALORECGPU_CLUSTER_MOMENT(ENG_CALIB_DEAD_UNCLASS, engCalibDeadUnclass)
1035 CALORECGPU_CLUSTER_MOMENT(ENG_CALIB_FRAC_EM, engCalibFracEM)
1036 CALORECGPU_CLUSTER_MOMENT(ENG_CALIB_FRAC_HAD, engCalibFracHad)
1037 CALORECGPU_CLUSTER_MOMENT(ENG_CALIB_FRAC_REST, engCalibFracRest)
1038
1039 using BasicClusterProperties = multi_class_holder <
1040
1041 clusters_index,
1042 clusters_E,
1043 clusters_abs_E,
1044 clusters_Et,
1045 clusters_eta,
1046 clusters_phi,
1047 clusters_number_cells,
1048 clusters_moments_time,
1049 clusters_moments_FIRST_PHI,
1050 clusters_moments_FIRST_ETA,
1051 clusters_moments_SECOND_R,
1052 clusters_moments_SECOND_LAMBDA,
1053 clusters_moments_DELTA_PHI,
1054 clusters_moments_DELTA_THETA,
1055 clusters_moments_DELTA_ALPHA,
1056 clusters_moments_CENTER_X,
1057 clusters_moments_CENTER_Y,
1058 clusters_moments_CENTER_Z,
1059 clusters_moments_CENTER_MAG,
1060 clusters_moments_CENTER_LAMBDA,
1061 clusters_moments_LATERAL,
1062 clusters_moments_LONGITUDINAL,
1063 clusters_moments_ENG_FRAC_EM,
1064 clusters_moments_ENG_FRAC_MAX,
1065 clusters_moments_ENG_FRAC_CORE,
1066 clusters_moments_FIRST_ENG_DENS,
1067 clusters_moments_SECOND_ENG_DENS,
1068 clusters_moments_ISOLATION,
1069 clusters_moments_ENG_BAD_CELLS,
1070 clusters_moments_N_BAD_CELLS,
1071 clusters_moments_N_BAD_CELLS_CORR,
1072 clusters_moments_BAD_CELLS_CORR_E,
1073 clusters_moments_BADLARQ_FRAC,
1074 clusters_moments_ENG_POS,
1075 clusters_moments_SIGNIFICANCE,
1076 clusters_moments_CELL_SIGNIFICANCE,
1077 clusters_moments_CELL_SIG_SAMPLING,
1078 clusters_moments_AVG_LAR_Q,
1079 clusters_moments_AVG_TILE_Q,
1080 clusters_moments_ENG_BAD_HV_CELLS,
1081 clusters_moments_N_BAD_HV_CELLS,
1082 clusters_moments_PTD,
1083 clusters_moments_MASS,
1084 clusters_moments_EM_PROBABILITY,
1085 clusters_moments_HAD_WEIGHT,
1086 clusters_moments_OOC_WEIGHT,
1087 clusters_moments_DM_WEIGHT,
1088 clusters_moments_TILE_CONFIDENCE_LEVEL,
1089 clusters_moments_SECOND_TIME,
1090 clusters_moments_VERTEX_FRACTION,
1091 clusters_moments_NVERTEX_FRACTION,
1092 clusters_moments_ETACALOFRAME,
1093 clusters_moments_PHICALOFRAME,
1094 clusters_moments_ETA1CALOFRAME,
1095 clusters_moments_PHI1CALOFRAME,
1096 clusters_moments_ETA2CALOFRAME,
1097 clusters_moments_PHI2CALOFRAME,
1098 clusters_moments_ENG_CALIB_TOT,
1099 clusters_moments_ENG_CALIB_OUT_L,
1100 clusters_moments_ENG_CALIB_OUT_M,
1101 clusters_moments_ENG_CALIB_OUT_T,
1102 clusters_moments_ENG_CALIB_DEAD_L,
1103 clusters_moments_ENG_CALIB_DEAD_M,
1104 clusters_moments_ENG_CALIB_DEAD_T,
1105 clusters_moments_ENG_CALIB_EMB0,
1106 clusters_moments_ENG_CALIB_EME0,
1107 clusters_moments_ENG_CALIB_TILEG3,
1108 clusters_moments_ENG_CALIB_DEAD_TOT,
1109 clusters_moments_ENG_CALIB_DEAD_EMB0,
1110 clusters_moments_ENG_CALIB_DEAD_TILE0,
1111 clusters_moments_ENG_CALIB_DEAD_TILEG3,
1112 clusters_moments_ENG_CALIB_DEAD_EME0,
1113 clusters_moments_ENG_CALIB_DEAD_HEC0,
1114 clusters_moments_ENG_CALIB_DEAD_FCAL,
1115 clusters_moments_ENG_CALIB_DEAD_LEAKAGE,
1116 clusters_moments_ENG_CALIB_DEAD_UNCLASS,
1117 clusters_moments_ENG_CALIB_FRAC_EM,
1118 clusters_moments_ENG_CALIB_FRAC_HAD,
1119 clusters_moments_ENG_CALIB_FRAC_REST
1120
1121 >;
1122
1123 }
1124
1125 using ClusterProperties::BasicClusterProperties;
1126
1128 {
1129#define CALORECGPU_COMPARED_CLUSTER_PROPERTY(NAME, ...) \
1130 struct clusters_ ## NAME \
1131 { \
1132 static std::string name() \
1133 { \
1134 return # NAME; \
1135 } \
1136 static double get_property([[maybe_unused]] const ConstantDataHolder & constant_data, \
1137 [[maybe_unused]] const CaloRecGPU::CellInfoArr & cell_info_1, \
1138 [[maybe_unused]] const CaloRecGPU::ClusterInfoArr & cluster_info_1, \
1139 [[maybe_unused]] const std::vector<int> & cells_prefix_sum_1, \
1140 [[maybe_unused]] const int cluster_index_1, \
1141 [[maybe_unused]] const CaloRecGPU::CellInfoArr & cell_info_2, \
1142 [[maybe_unused]] const CaloRecGPU::ClusterInfoArr & cluster_info_2, \
1143 [[maybe_unused]] const std::vector<int> & cells_prefix_sum_2, \
1144 [[maybe_unused]] const int cluster_index_2) \
1145 { \
1146 __VA_ARGS__ \
1147 } \
1148 };
1149
1150 CALORECGPU_COMPARED_CLUSTER_PROPERTY(delta_phi_in_range,
1151 return Helpers::regularize_angle( Helpers::regularize_angle(cluster_info_2.clusterPhi[cluster_index_2]) -
1152 Helpers::regularize_angle(cluster_info_1.clusterPhi[cluster_index_1]) );
1153 )
1154
1156 const double delta_eta = cluster_info_2.clusterEta[cluster_index_2] - cluster_info_1.clusterEta[cluster_index_1];
1157 const double delta_phi = Helpers::regularize_angle( Helpers::regularize_angle(cluster_info_2.clusterPhi[cluster_index_2]) -
1158 Helpers::regularize_angle(cluster_info_1.clusterPhi[cluster_index_1]) );
1159 return std::sqrt(delta_eta * delta_eta + delta_phi * delta_phi);
1160
1161 )
1162
1163
1164 using ComparedClusterProperties = multi_class_holder <
1165
1166 clusters_delta_phi_in_range,
1167 clusters_delta_R
1168
1169 >;
1170
1171 }
1172
1173 using ExtraClusterComparisons::ComparedClusterProperties;
1174
1176 {
1177#define CALORECGPU_BASIC_CELL_PROPERTY(NAME, ...) \
1178 struct cells_ ## NAME \
1179 { \
1180 static std::string name() \
1181 { \
1182 return # NAME; \
1183 } \
1184 static double get_property([[maybe_unused]] const ConstantDataHolder & constant_data, \
1185 [[maybe_unused]] const CaloRecGPU::CellInfoArr & cell_info, \
1186 [[maybe_unused]] const CaloRecGPU::ClusterInfoArr & cluster_info, \
1187 [[maybe_unused]] const std::vector<int> & cells_prefix_sum, \
1188 [[maybe_unused]] const int cell ) \
1189 { \
1190 __VA_ARGS__ \
1191 } \
1192 };
1193
1194 CALORECGPU_BASIC_CELL_PROPERTY(E, return cell_info.energy[cell] / CLHEP::MeV;)
1195 CALORECGPU_BASIC_CELL_PROPERTY(abs_E, return std::abs(cell_info.energy[cell]) / CLHEP::MeV; )
1196
1197 CALORECGPU_BASIC_CELL_PROPERTY(gain, return cell_info.gain[cell];)
1198
1199 CALORECGPU_BASIC_CELL_PROPERTY(noise, return constant_data.m_cell_noise->get_noise(cell_info.get_hash_ID(cell), cell_info.gain[cell]);)
1200
1201 CALORECGPU_BASIC_CELL_PROPERTY(SNR, return cell_info.energy[cell] /
1202 protect_from_zero(constant_data.m_cell_noise->get_noise(cell_info.get_hash_ID(cell), cell_info.gain[cell]));
1203 )
1204
1205 CALORECGPU_BASIC_CELL_PROPERTY(abs_SNR, return std::abs(cell_info.energy[cell] /
1206 protect_from_zero(constant_data.m_cell_noise->get_noise(cell_info.get_hash_ID(cell), cell_info.gain[cell])));)
1207
1208 CALORECGPU_BASIC_CELL_PROPERTY(time, return cell_info.time[cell] / CLHEP::us;)
1209
1211
1212 CALORECGPU_BASIC_CELL_PROPERTY(hash_ID, return cell_info.get_hash_ID(cell);)
1213
1214 CALORECGPU_BASIC_CELL_PROPERTY(sampling, return constant_data.m_geometry->sampling(cell_info.get_hash_ID(cell));)
1215
1216
1217 CALORECGPU_BASIC_CELL_PROPERTY(x, return constant_data.m_geometry->x[cell_info.get_hash_ID(cell)] / CLHEP::cm; )
1218 CALORECGPU_BASIC_CELL_PROPERTY(y, return constant_data.m_geometry->y[cell_info.get_hash_ID(cell)] / CLHEP::cm; )
1219 CALORECGPU_BASIC_CELL_PROPERTY(z, return constant_data.m_geometry->z[cell_info.get_hash_ID(cell)] / CLHEP::cm; )
1220
1221 CALORECGPU_BASIC_CELL_PROPERTY(phi, return constant_data.m_geometry->phi[cell_info.get_hash_ID(cell)];)
1222
1223 CALORECGPU_BASIC_CELL_PROPERTY(eta, return constant_data.m_geometry->eta[cell_info.get_hash_ID(cell)];)
1224
1225 CALORECGPU_BASIC_CELL_PROPERTY(number_of_clusters, return (cells_prefix_sum[cell + 1] - cells_prefix_sum[cell]);)
1226
1227 CALORECGPU_BASIC_CELL_PROPERTY(primary_cluster_index,
1228 {
1229 float max_weight = 0.f;
1230 int max_index = 0;
1231 for (int i = cells_prefix_sum[cell]; i < cells_prefix_sum[cell + 1]; ++i)
1232 {
1233 const float this_weight = cluster_info.cellWeights[i];
1234 const int this_index = cluster_info.clusterIndices[i];
1235 if (this_weight > max_weight || (this_weight == max_weight && this_index > max_index))
1236 {
1237 max_weight = this_weight;
1238 max_index = this_index;
1239 }
1240 }
1241 return max_index;
1242 }
1243 )
1244
1245 CALORECGPU_BASIC_CELL_PROPERTY(secondary_cluster_index,
1246 {
1247 float max_weight = 0.f, second_max_weight = 0.f;
1248 int max_index = 0, second_max_index = 0;
1249 for (int i = cells_prefix_sum[cell]; i < cells_prefix_sum[cell + 1]; ++i)
1250 {
1251 const float this_weight = cluster_info.cellWeights[i];
1252 const int this_index = cluster_info.clusterIndices[i];
1253 if (this_weight > max_weight || (this_weight == max_weight && this_index > max_index))
1254 {
1255 second_max_weight = max_weight;
1256 second_max_index = max_index;
1257 max_weight = this_weight;
1258 max_index = this_index;
1259 }
1260 else if (this_weight > second_max_weight || (this_weight == second_max_weight && this_index > second_max_index))
1261 {
1262 second_max_weight = this_weight;
1263 second_max_index = this_index;
1264 }
1265 }
1266 return second_max_index;
1267 }
1268 )
1269
1270 CALORECGPU_BASIC_CELL_PROPERTY(primary_weight,
1271 {
1272 float max_weight = 0.f;
1273 int max_index = 0;
1274 for (int i = cells_prefix_sum[cell]; i < cells_prefix_sum[cell + 1]; ++i)
1275 {
1276 const float this_weight = cluster_info.cellWeights[i];
1277 const int this_index = cluster_info.clusterIndices[i];
1278 if (this_weight > max_weight || (this_weight == max_weight && this_index > max_index))
1279 {
1280 max_weight = this_weight;
1281 max_index = this_index;
1282 }
1283 }
1284 return max_weight;
1285 }
1286 )
1287
1288
1289 CALORECGPU_BASIC_CELL_PROPERTY(secondary_weight,
1290 {
1291 float max_weight = 0.f, second_max_weight = 0.f;
1292 int max_index = 0, second_max_index = 0;
1293 for (int i = cells_prefix_sum[cell]; i < cells_prefix_sum[cell + 1]; ++i)
1294 {
1295 const float this_weight = cluster_info.cellWeights[i];
1296 const int this_index = cluster_info.clusterIndices[i];
1297 if (this_weight > max_weight || (this_weight == max_weight && this_index > max_index))
1298 {
1299 second_max_weight = max_weight;
1300 second_max_index = max_index;
1301 max_weight = this_weight;
1302 max_index = this_index;
1303 }
1304 else if (this_weight > second_max_weight || (this_weight == second_max_weight && this_index > second_max_index))
1305 {
1306 second_max_weight = this_weight;
1307 second_max_index = this_index;
1308 }
1309 }
1310 return second_max_weight;
1311 }
1312 )
1313
1314 using BasicCellProperties = multi_class_holder <
1315
1316 cells_E,
1317 cells_abs_E,
1318 cells_gain,
1319 cells_noise,
1320 cells_SNR,
1321 cells_abs_SNR,
1322 cells_time,
1323 cells_index,
1324 cells_hash_ID,
1325 cells_sampling,
1326 cells_x,
1327 cells_y,
1328 cells_z,
1329 cells_phi,
1330 cells_eta,
1331 cells_number_of_clusters,
1332 cells_primary_cluster_index,
1333 cells_secondary_cluster_index,
1334 cells_primary_weight,
1335 cells_secondary_weight
1336
1337 >;
1338
1339 }
1340
1341 using CellProperties::BasicCellProperties;
1342
1343 namespace CellTypes
1344 {
1345#define CALORECGPU_BASIC_CELL_TYPE(NAME, ...) \
1346 struct cell_type_ ## NAME \
1347 { \
1348 static std::string name() \
1349 { \
1350 return # NAME; \
1351 } \
1352 static bool is_type([[maybe_unused]] const ConstantDataHolder & constant_data, \
1353 [[maybe_unused]] const CaloRecGPU::CellInfoArr & cell_info, \
1354 [[maybe_unused]] const CaloRecGPU::ClusterInfoArr & cluster_info, \
1355 [[maybe_unused]] const std::vector<int> & cells_prefix_sum, \
1356 [[maybe_unused]] const int cell ) \
1357 { \
1358 __VA_ARGS__ \
1359 } \
1360 };
1361
1362 CALORECGPU_BASIC_CELL_TYPE(intracluster, return (cells_prefix_sum[cell + 1] > cells_prefix_sum[cell]);)
1363
1364 CALORECGPU_BASIC_CELL_TYPE(extracluster, return (cells_prefix_sum[cell + 1] == cells_prefix_sum[cell]);)
1365
1366 CALORECGPU_BASIC_CELL_TYPE(shared, return (cells_prefix_sum[cell + 1] > cells_prefix_sum[cell] + 1);)
1367
1368 using BasicCellTypes = multi_class_holder <
1369
1370 cell_type_intracluster,
1371 cell_type_extracluster,
1372 cell_type_shared
1373
1374 >;
1375
1376 }
1377
1378 using CellTypes::BasicCellTypes;
1379
1380
1381 enum ExtraThingsToCalculate
1382 {
1383 ClusterSize = 0,
1384 ClusterComparedSize,
1385 DiffCells,
1386 SameECells,
1387 SameECellsCombined,
1388 SameSNRCells,
1389 SameSNRCellsCombined,
1390 ExtraThingsSize
1391 };
1392}
1393
1395{
1396 const std::vector<std::string> histo_strings = m_moniTool->histogramService()->getHists();
1397 //Small problem: other histograms with matching names.
1398 //Mitigated by the fact that we use cell_<property> and cluster_<property>...
1399
1400 m_clusterPropertiesToDo.resize(BasicClusterProperties::size(), false);
1401 m_comparedClusterPropertiesToDo.resize(BasicClusterProperties::size(), false);
1402 m_extraComparedClusterPropertiesToDo.resize(ComparedClusterProperties::size(), false);
1403 m_cellPropertiesToDo.resize(BasicCellProperties::size(), false);
1404 m_comparedCellPropertiesToDo.resize(BasicCellProperties::size(), false);
1405 m_cellTypesToDo.resize(BasicCellTypes::size(), false);
1406 m_comparedCellTypesToDo.resize(BasicCellTypes::size(), false);
1407 m_extraThingsToDo.resize(ExtraThingsSize, false);
1408
1409 auto string_contains = [](std::string_view container, std::string_view contained) -> bool
1410 {
1411 return container.find(contained) != std::string::npos;
1412 };
1413
1414 auto search_lambda = [&](const auto & prop, const size_t count, bool & check,
1415 const std::string & str, std::vector<bool> & to_do,
1416 std::string_view prefix = "", std::string_view suffix = "")
1417 {
1418 if (string_contains(str, std::string(prefix) + prop.name() + std::string(suffix)))
1419 {
1420 to_do[count] = true;
1421 check = true;
1422 }
1423 };
1424
1425 for (const auto & str : histo_strings)
1426 {
1427 bool found = false;
1428
1429 apply_to_multi_class(search_lambda, BasicCellProperties{}, found, str, m_cellPropertiesToDo, "_cell_");
1430 apply_to_multi_class(search_lambda, BasicCellTypes{}, found, str, m_cellTypesToDo, "_", "_cells");
1431
1432 if (found)
1433 {
1434 m_doCells = true;
1435 }
1436
1437 found = false;
1438
1439 apply_to_multi_class(search_lambda, BasicClusterProperties{}, found, str, m_clusterPropertiesToDo, "_cluster_");
1440
1441 if (found)
1442 {
1443 m_doClusters = true;
1444 }
1445
1446 found = false;
1447
1448 apply_to_multi_class(search_lambda, BasicCellProperties{}, found, str, m_comparedCellPropertiesToDo, "_cell_delta_");
1449 apply_to_multi_class(search_lambda, BasicCellProperties{}, found, str, m_comparedCellPropertiesToDo, "_cell_", "_ref");
1450 apply_to_multi_class(search_lambda, BasicCellProperties{}, found, str, m_comparedCellPropertiesToDo, "_cell_", "_test");
1451 apply_to_multi_class(search_lambda, BasicCellTypes{}, found, str, m_comparedCellTypesToDo, "num_ref_", "_cells");
1452 apply_to_multi_class(search_lambda, BasicCellTypes{}, found, str, m_comparedCellTypesToDo, "num_test_", "_cells");
1453 apply_to_multi_class(search_lambda, BasicCellTypes{}, found, str, m_comparedCellTypesToDo, "delta_", "_cells");
1454
1455 if (found)
1456 {
1457 m_doCombinedCells = true;
1458 }
1459
1460 found = false;
1461
1462 apply_to_multi_class(search_lambda, BasicClusterProperties{}, found, str, m_comparedClusterPropertiesToDo, "_cluster_delta_", "");
1463 apply_to_multi_class(search_lambda, BasicClusterProperties{}, found, str, m_comparedClusterPropertiesToDo, "_cluster_", "_ref");
1464 apply_to_multi_class(search_lambda, BasicClusterProperties{}, found, str, m_comparedClusterPropertiesToDo, "_cluster_", "_test");
1465 apply_to_multi_class(search_lambda, ComparedClusterProperties{}, found, str, m_extraComparedClusterPropertiesToDo);
1466
1467 if (found)
1468 {
1469 m_doCombinedClusters = true;
1470 }
1471
1472 if ( string_contains(str, "cluster_size_ref") ||
1473 string_contains(str, "cluster_size_test") ||
1474 string_contains(str, "cluster_delta_size") ||
1475 string_contains(str, "cluster_weighted_size_ref") ||
1476 string_contains(str, "cluster_weighted_size_test") ||
1477 string_contains(str, "cluster_delta_weighted_size") )
1478 {
1479 m_extraThingsToDo[ClusterComparedSize] = true;
1480 m_doCombinedCells = true;
1481 m_doCombinedClusters = true;
1482 }
1483 else if ( string_contains(str, "cluster_size") ||
1484 string_contains(str, "cluster_weighted_size") )
1485 {
1486 m_extraThingsToDo[ClusterSize] = true;
1487 m_doCells = true;
1488 m_doClusters = true;
1489 }
1490
1491 if (string_contains(str, "cluster_diff_cells"))
1492 {
1493 m_extraThingsToDo[DiffCells] = true;
1494 m_doCombinedCells = true;
1495 m_doCombinedClusters = true;
1496 }
1497
1498 if ( string_contains(str, "_num_same_E_cells_ref") ||
1499 string_contains(str, "_num_same_E_cells_test") ||
1500 string_contains(str, "delta_num_same_E_cells") ||
1501 string_contains(str, "_num_same_abs_E_cells_ref") ||
1502 string_contains(str, "_num_same_abs_E_cells_test") ||
1503 string_contains(str, "delta_num_same_abs_E_cells") )
1504 {
1505 m_extraThingsToDo[SameECellsCombined] = true;
1506 m_doCombinedCells = true;
1507 }
1508 else if ( string_contains(str, "_num_same_E_cells") ||
1509 string_contains(str, "_num_same_abs_E_cells") )
1510 {
1511 m_extraThingsToDo[SameECells] = true;
1512 m_doCells = true;
1513 }
1514
1515 if ( string_contains(str, "_num_same_SNR_cells_ref") ||
1516 string_contains(str, "_num_same_SNR_cells_test") ||
1517 string_contains(str, "delta_num_same_SNR_cells") ||
1518 string_contains(str, "_num_same_abs_SNR_cells_ref") ||
1519 string_contains(str, "_num_same_abs_SNR_cells_test") ||
1520 string_contains(str, "delta_num_same_abs_SNR_cells") )
1521 {
1522 m_extraThingsToDo[SameSNRCellsCombined] = true;
1523 m_doCombinedCells = true;
1524 }
1525 else if ( string_contains(str, "_num_same_SNR_cells") ||
1526 string_contains(str, "_num_same_abs_SNR_cells") )
1527 {
1528 m_extraThingsToDo[SameSNRCells] = true;
1529 m_doCells = true;
1530 }
1531 }
1532
1533 return StatusCode::SUCCESS;
1534
1535}
1536
1537
1538StatusCode CaloGPUClusterAndCellDataMonitor::add_data(const EventContext & /*ctx*/,
1539 const ConstantDataHolder & constant_data,
1540 const CaloRecGPU::CellInfoArr * cell_info,
1541 const CaloRecGPU::ClusterInfoArr * clusters,
1542 const std::vector<int> & cells_prefix_sum,
1543 const std::string & tool_name) const
1544{
1545 m_numClustersPerTool[tool_name].fetch_add(clusters->number);
1546
1547 const std::string prefix = m_toolToIdMap.at(tool_name);
1548
1549 const int index = m_toolsToCheckFor.at(tool_name);
1550
1551 if (index >= 0 && m_numToolsToKeep > 0)
1552 {
1553 std::vector<per_tool_storage> & store_vec = m_storageHolder.get_for_thread();
1554 store_vec[index].cell_info = *cell_info;
1555 store_vec[index].clusters = *clusters;
1556 store_vec[index].cells_prefix_sum = cells_prefix_sum;
1557 }
1558
1559 if (prefix != "")
1560 //Tools that are not meant to be plotted individually
1561 //have the empty string as a prefix.
1562 {
1563 std::unordered_map<std::string, std::vector<double>> cluster_properties, cell_properties;
1564
1565 std::unordered_map<std::string, long long int> cell_counts;
1566
1567 cluster_properties["size"].resize(clusters->number, 0.);
1568 cluster_properties["weighted_size"].resize(clusters->number, 0.);
1569
1570 long long int same_energy = 0, same_abs_energy = 0, same_snr = 0, same_abs_snr = 0;
1571
1572 std::set<double> energies, snrs;
1573
1574 if (m_doCells)
1575 {
1576
1577 for (int cell = 0; cell < cell_info->number; ++cell)
1578 {
1579 if (!cell_info->is_valid(cell))
1580 {
1581 continue;
1582 }
1583
1584 apply_to_multi_class([&](const auto & prop, const size_t i)
1585 {
1586 if (m_cellPropertiesToDo[i])
1587 {
1588 cell_properties[prop.name()].push_back(prop.get_property(constant_data, *cell_info, *clusters, cells_prefix_sum, cell));
1589 }
1590 }, BasicCellProperties{});
1591
1592 apply_to_multi_class([&](const auto & prop, const size_t i)
1593 {
1594 if (m_cellTypesToDo[i])
1595 {
1596 cell_counts[prop.name()] += prop.is_type(constant_data, *cell_info, *clusters, cells_prefix_sum, cell);
1597 }
1598 }, BasicCellTypes{});
1599
1600 const float this_energy = cell_info->energy[cell];
1601
1602 if (m_extraThingsToDo[SameECells])
1603 {
1604
1605 if (energies.count(this_energy))
1606 {
1607 ++same_energy;
1608 ++same_abs_energy;
1609 }
1610 else if (energies.count(-this_energy))
1611 {
1612 ++same_abs_energy;
1613 }
1614 energies.insert(this_energy);
1615 }
1616
1617 if (m_extraThingsToDo[SameSNRCells])
1618 {
1619
1620 const float this_snr = this_energy / protect_from_zero(constant_data.m_cell_noise->get_noise(cell_info->get_hash_ID(cell), cell_info->gain[cell]));
1621
1622 if (snrs.count(this_snr))
1623 {
1624 ++same_snr;
1625 ++same_abs_snr;
1626 }
1627 else if (snrs.count(-this_snr))
1628 {
1629 ++same_abs_snr;
1630 }
1631 snrs.insert(this_snr);
1632 }
1633 }
1634
1635 if (m_extraThingsToDo[ClusterSize])
1636 {
1637 for (int i = 0; i < clusters->number_cells; ++i)
1638 {
1639 const int this_cluster = clusters->clusterIndices[i];
1640 const float this_weight = clusters->cellWeights[i];
1641
1642 cluster_properties["size"][this_cluster] += 1;
1643 cluster_properties["weighted_size"][this_cluster] += this_weight;
1644 }
1645 }
1646 }
1647
1648 if (m_doClusters)
1649 {
1650 for (int cluster = 0; cluster < clusters->number; ++cluster)
1651 {
1652 apply_to_multi_class([&](const auto & prop, const size_t i)
1653 {
1655 {
1656 cluster_properties[prop.name()].push_back(prop.get_property(constant_data, *cell_info, *clusters, cells_prefix_sum, cluster));
1657 }
1658 }, BasicClusterProperties{});
1659 }
1660 }
1661
1662 using coll_type = decltype(Monitored::Collection("", std::declval<std::vector<double> &>()));
1663 using scalar_type = decltype(Monitored::Scalar("", std::declval<long long int>()));
1664
1665 std::vector<coll_type> collections;
1666 std::vector<scalar_type> count_scalars;
1667 std::vector<std::reference_wrapper<Monitored::IMonitoredVariable>> cluster_group, cell_group, counts_group;
1668
1669 collections.reserve(cluster_properties.size() + cell_properties.size());
1670 count_scalars.reserve(cell_counts.size());
1671 cluster_group.reserve(cluster_properties.size());
1672 cell_group.reserve(cell_properties.size());
1673 counts_group.reserve(cell_counts.size() + 5);
1674
1675 auto mon_clus_num = Monitored::Scalar(prefix + "_num_clusters", clusters->number);
1676 auto mon_same_energy = Monitored::Scalar(prefix + "_num_same_E_cells", same_energy);
1677 auto mon_same_abs_energy = Monitored::Scalar(prefix + "_num_same_abs_E_cells", same_abs_energy);
1678 auto mon_same_snr = Monitored::Scalar(prefix + "_num_same_SNR_cells", same_snr);
1679 auto mon_same_abs_snr = Monitored::Scalar(prefix + "_num_same_abs_SNR_cells", same_abs_snr);
1680
1681 counts_group.push_back(std::ref(mon_clus_num));
1682 counts_group.push_back(std::ref(mon_same_energy));
1683 counts_group.push_back(std::ref(mon_same_abs_energy));
1684 counts_group.push_back(std::ref(mon_same_snr));
1685 counts_group.push_back(std::ref(mon_same_abs_snr));
1686
1687 //If we're not doing these plots,
1688 //we're still saving,
1689 //which is slightly inefficient, but.. let's not complicate.
1690
1691 for (const auto & k_v : cluster_properties)
1692 {
1693 collections.emplace_back(Monitored::Collection(prefix + "_cluster_" + k_v.first, k_v.second));
1694 cluster_group.push_back(std::ref(collections.back()));
1695 }
1696
1697 for (const auto & k_v : cell_properties)
1698 {
1699 collections.emplace_back(Monitored::Collection(prefix + "_cell_" + k_v.first, k_v.second));
1700 cell_group.push_back(std::ref(collections.back()));
1701 }
1702
1703 for (const auto & k_v : cell_counts)
1704 {
1705 count_scalars.emplace_back(Monitored::Scalar(prefix + "_num_" + k_v.first + "_cells", k_v.second));
1706 counts_group.push_back(std::ref(count_scalars.back()));
1707 }
1708
1709 // Taking a std::ref of the back() iterator above is safe because the vector
1710 // has been reserved with the correct number of elements.
1711 // cppcheck-suppress invalidContainer
1712 auto monitor_clusters = Monitored::Group(m_moniTool, cluster_group);
1713 auto monitor_cells = Monitored::Group(m_moniTool, cell_group);
1714 auto monitor_counts = Monitored::Group(m_moniTool, counts_group);
1715
1716 }
1717 return StatusCode::SUCCESS;
1718}
1719
1720
1721StatusCode CaloGPUClusterAndCellDataMonitor::add_combination(const EventContext & /*ctx*/,
1722 const CaloRecGPU::ConstantDataHolder & constant_data,
1723 const int index_1,
1724 const int index_2,
1725 const std::string & prefix,
1726 const bool match_in_energy,
1727 const bool match_without_shared,
1728 const bool match_perfectly) const
1729{
1730
1731 //Note: Part of the work here is superfluous in the case
1732 // where we are monitoring the tools individually too,
1733 // but in the most generic case that is not guaranteed.
1734 // Partially wasted work, but it's cleaner than the alternative...
1735
1736 std::vector<per_tool_storage> & store_vec = m_storageHolder.get_for_thread();
1737
1738 const CaloRecGPU::CellInfoArr & cell_info_1 = store_vec[index_1].cell_info;
1739 const CaloRecGPU::ClusterInfoArr & clusters_1 = store_vec[index_1].clusters;
1740 const std::vector<int> & cells_prefix_sum_1 = store_vec[index_1].cells_prefix_sum;
1741
1742 const CaloRecGPU::CellInfoArr & cell_info_2 = store_vec[index_2].cell_info;
1743 const CaloRecGPU::ClusterInfoArr & clusters_2 = store_vec[index_2].clusters;
1744 const std::vector<int> & cells_prefix_sum_2 = store_vec[index_2].cells_prefix_sum;
1745
1747
1748 if (match_perfectly)
1749 {
1750 ATH_CHECK( match_clusters_perfectly(sch, constant_data, cell_info_1, clusters_1, clusters_2, match_without_shared) );
1751 }
1752 else
1753 {
1754 ATH_CHECK( match_clusters(sch, constant_data, cell_info_1, clusters_1, clusters_2, match_in_energy, match_without_shared) );
1755 }
1756
1757 std::unordered_map<std::string, std::vector<double>> cluster_properties, cell_properties;
1758
1759 std::unordered_map<std::string, long long int> cell_counts;
1760
1761 std::vector<double> ref_size_vec(clusters_1.number, 0.), test_size_vec(clusters_2.number, 0.),
1762 ref_weighted_size_vec(clusters_1.number, 0.), test_weighted_size_vec(clusters_2.number, 0.),
1763 ref_diff_cells(clusters_1.number, 0.), test_diff_cells(clusters_2.number, 0.),
1764 ref_diff_cells_weight(clusters_1.number, 0.), test_diff_cells_weight(clusters_2.number, 0.);
1765 //We can store integers up to 2^53 on a double...
1766
1767 long long int same_energy_1 = 0, same_energy_2 = 0,
1768 same_abs_energy_1 = 0, same_abs_energy_2 = 0,
1769 same_snr_1 = 0, same_snr_2 = 0,
1770 same_abs_snr_1 = 0, same_abs_snr_2 = 0,
1771 same_cluster_cells_count = 0, diff_cluster_cells_count = 0;
1772
1773 std::set<double> energies_1, energies_2, snrs_1, snrs_2;
1774
1776 {
1777 for (int cell = 0; cell < NCaloCells; ++cell)
1778 //The way the validity is checked means we will
1779 //simply continue for all cells beyond cell_info_N->number.
1780 {
1781 if (!cell_info_1.is_valid(cell) || !cell_info_2.is_valid(cell) || cell >= cell_info_1.number || cell >= cell_info_2.number)
1782 {
1783 continue;
1784 }
1785
1786 apply_to_multi_class([&](const auto & prop, const size_t i)
1787 {
1789 {
1790 const auto prop_1 = prop.get_property(constant_data, cell_info_1, clusters_1, cells_prefix_sum_1, cell);
1791 const auto prop_2 = prop.get_property(constant_data, cell_info_2, clusters_2, cells_prefix_sum_2, cell);
1792
1793 cell_properties[prop.name() + "_ref"].push_back(prop_1);
1794 cell_properties[prop.name() + "_test"].push_back(prop_2);
1795
1796 cell_properties["delta_" + prop.name()].push_back(prop_2 - prop_1);
1797 cell_properties["delta_" + prop.name() + "_rel_ref"].push_back((prop_2 - prop_1) / protect_from_zero(std::abs(prop_1)));
1798 cell_properties["delta_" + prop.name() + "_rel_test"].push_back((prop_2 - prop_1) / protect_from_zero(std::abs(prop_2)));
1799 }
1800 }, BasicCellProperties{});
1801
1802 apply_to_multi_class([&](const auto & prop, const size_t i)
1803 {
1805 {
1806 const auto is_1 = prop.is_type(constant_data, cell_info_1, clusters_1, cells_prefix_sum_1, cell);
1807 const auto is_2 = prop.is_type(constant_data, cell_info_2, clusters_2, cells_prefix_sum_2, cell);
1808
1809 cell_counts["num_" + prop.name() + "_cells_ref"] += is_1;
1810 cell_counts["num_" + prop.name() + "_cells_test"] += is_2;
1811 cell_counts["delta_num_" + prop.name() + "_cells"] += is_2 - is_1;
1812 }
1813 }, BasicCellTypes{});
1814
1815 const float this_energy_1 = cell_info_1.energy[cell];
1816 const float this_energy_2 = cell_info_2.energy[cell];
1817
1818 if (m_extraThingsToDo[SameECellsCombined])
1819 {
1820 if (energies_1.count(this_energy_1))
1821 {
1822 ++same_energy_1;
1823 ++same_abs_energy_1;
1824 }
1825 else if (energies_1.count(-this_energy_1))
1826 {
1827 ++same_abs_energy_1;
1828 }
1829 energies_1.insert(this_energy_1);
1830
1831 if (energies_2.count(this_energy_2))
1832 {
1833 ++same_energy_2;
1834 ++same_abs_energy_2;
1835 }
1836 else if (energies_2.count(-this_energy_2))
1837 {
1838 ++same_abs_energy_2;
1839 }
1840 energies_2.insert(this_energy_2);
1841 }
1842
1843 if (m_extraThingsToDo[SameSNRCellsCombined])
1844 {
1845 const float this_snr_1 = this_energy_1 / protect_from_zero(constant_data.m_cell_noise->get_noise(cell_info_1.get_hash_ID(cell), cell_info_1.gain[cell]));
1846
1847 if (snrs_1.count(this_snr_1))
1848 {
1849 ++same_snr_1;
1850 ++same_abs_snr_1;
1851 }
1852 else if (snrs_1.count(-this_snr_1))
1853 {
1854 ++same_abs_snr_1;
1855 }
1856 snrs_1.insert(this_snr_1);
1857
1858
1859 const float this_snr_2 = this_energy_2 / protect_from_zero(constant_data.m_cell_noise->get_noise(cell_info_2.get_hash_ID(cell), cell_info_2.gain[cell]));
1860
1861 if (snrs_2.count(this_snr_2))
1862 {
1863 ++same_snr_2;
1864 ++same_abs_snr_2;
1865 }
1866 else if (snrs_2.count(-this_snr_2))
1867 {
1868 ++same_abs_snr_2;
1869 }
1870 snrs_2.insert(this_snr_2);
1871 }
1872
1873 if (m_extraThingsToDo[DiffCells] || m_extraThingsToDo[ClusterComparedSize])
1874 {
1875 bool cell_is_diff = false;
1876
1877 for (int i = cells_prefix_sum_1[cell]; i < cells_prefix_sum_1[cell + 1] && i < cell_info_1.number; ++i)
1878 {
1879 const int this_index = clusters_1.clusterIndices[i];
1880 const float this_weight = clusters_1.cellWeights[i];
1881 bool found_match = false;
1882 for (int j = cells_prefix_sum_2[cell]; j < cells_prefix_sum_2[cell + 1] && j < cell_info_2.number; ++j)
1883 {
1884 if (this_index == sch.t2r(clusters_2.clusterIndices[j]))
1885 {
1886 found_match = true;
1887 break;
1888 }
1889 }
1890 ref_size_vec[this_index] += 1;
1891 ref_weighted_size_vec[this_index] += this_weight;
1892 if (!found_match)
1893 {
1894 ref_diff_cells[this_index] += 1;
1895 ref_diff_cells_weight[this_index] += this_weight;
1896 cell_is_diff = true;
1897 }
1898 }
1899
1900 for (int i = cells_prefix_sum_2[cell]; i < cells_prefix_sum_2[cell + 1] && i < cell_info_2.number; ++i)
1901 {
1902 const int this_index = clusters_2.clusterIndices[i];
1903 const float this_weight = clusters_2.cellWeights[i];
1904 bool found_match = false;
1905 for (int j = cells_prefix_sum_1[cell]; j < cells_prefix_sum_1[cell + 1] && j < cell_info_1.number; ++j)
1906 {
1907 if (this_index == sch.r2t(clusters_1.clusterIndices[j]))
1908 {
1909 found_match = true;
1910 break;
1911 }
1912 }
1913 test_size_vec[this_index] += 1;
1914 test_weighted_size_vec[this_index] += this_weight;
1915 if (!found_match)
1916 {
1917 test_diff_cells[this_index] += 1;
1918 test_diff_cells_weight[this_index] += this_weight;
1919 cell_is_diff = true;
1920 }
1921 }
1922
1923 if (cell_is_diff)
1924 {
1925#if CALORECGPU_DATA_MONITOR_EXTRA_PRINTOUTS
1926 msg(MSG::INFO) << "Diff: " << cell << " |";
1927 for (int i = cells_prefix_sum_1[cell]; i < cells_prefix_sum_1[cell + 1] && i < cell_info_1.number; ++i)
1928 {
1929 msg() << " {" << clusters_1.cellWeights[i] << ", " << clusters_1.clusterIndices[i] << " (" << sch.r2t(clusters_1.clusterIndices[i]) << ")}";
1930 }
1931 msg() << " |";
1932 for (int i = cells_prefix_sum_2[cell]; i < cells_prefix_sum_2[cell + 1] && i < cell_info_2.number; ++i)
1933 {
1934 msg() << " {" << clusters_2.cellWeights[i] << ", " << clusters_2.clusterIndices[i] << " (" << sch.t2r(clusters_2.clusterIndices[i]) << ")}";
1935 }
1936 msg() << endmsg;
1937#endif
1938
1939 ++diff_cluster_cells_count;
1940 }
1941 else if ((cells_prefix_sum_1[cell + 1] > cells_prefix_sum_1[cell]) || (cells_prefix_sum_2[cell + 1] > cells_prefix_sum_2[cell]))
1942 {
1943 ++same_cluster_cells_count;
1944 }
1945 }
1946 }
1947 }
1948
1950 {
1951
1952 for (int cluster = 0; cluster < clusters_1.number; ++cluster)
1953 {
1954 const int match = sch.r2t(cluster);
1955 if (match < 0)
1956 //The cluster isn't matched.
1957 {
1958 continue;
1959 }
1960
1961 apply_to_multi_class([&](const auto & prop, const size_t i)
1962 {
1964 {
1965 const auto prop_1 = prop.get_property(constant_data, cell_info_1, clusters_1, cells_prefix_sum_1, cluster);
1966 const auto prop_2 = prop.get_property(constant_data, cell_info_2, clusters_2, cells_prefix_sum_2, match);
1967
1968 cluster_properties[prop.name() + "_ref"].push_back(prop_1);
1969 cluster_properties[prop.name() + "_test"].push_back(prop_2);
1970
1971 cluster_properties["delta_" + prop.name()].push_back(prop_2 - prop_1);
1972 cluster_properties["delta_" + prop.name() + "_rel_ref"].push_back((prop_2 - prop_1) / protect_from_zero(std::abs(prop_1)));
1973 cluster_properties["delta_" + prop.name() + "_rel_test"].push_back((prop_2 - prop_1) / protect_from_zero(std::abs(prop_2)));
1974 }
1975 }, BasicClusterProperties{});
1976
1977 apply_to_multi_class([&](const auto & prop, const size_t i)
1978 {
1980 {
1981 cluster_properties[prop.name()].push_back(prop.get_property(constant_data, cell_info_1, clusters_1, cells_prefix_sum_1, cluster,
1982 cell_info_2, clusters_2, cells_prefix_sum_2, match));
1983 }
1984 }, ComparedClusterProperties{});
1985
1986 if (m_extraThingsToDo[ClusterComparedSize])
1987 {
1988 cluster_properties["size_ref"].push_back(ref_size_vec[cluster]);
1989 cluster_properties["size_test"].push_back(test_size_vec[match]);
1990 cluster_properties["delta_size"].push_back(ref_size_vec[cluster] - test_size_vec[match]);
1991 cluster_properties["delta_size_rel_ref"].push_back((ref_size_vec[cluster] - test_size_vec[match]) / protect_from_zero(ref_size_vec[cluster]));
1992 cluster_properties["delta_size_rel_test"].push_back((ref_size_vec[cluster] - test_size_vec[match]) / protect_from_zero(test_size_vec[match]));
1993
1994 cluster_properties["weighted_size_ref"].push_back(ref_weighted_size_vec[cluster]);
1995 cluster_properties["weighted_size_test"].push_back(test_weighted_size_vec[match]);
1996 cluster_properties["delta_weighted_size"].push_back(ref_weighted_size_vec[cluster] - test_weighted_size_vec[match]);
1997 cluster_properties["delta_weighted_size_rel_ref"].push_back((ref_weighted_size_vec[cluster] - test_weighted_size_vec[match]) / protect_from_zero(ref_weighted_size_vec[cluster]));
1998 cluster_properties["delta_weighted_size_rel_test"].push_back((ref_weighted_size_vec[cluster] - test_weighted_size_vec[match]) / protect_from_zero(test_weighted_size_vec[match]));
1999 }
2000
2001 if (m_extraThingsToDo[DiffCells])
2002 {
2003 cluster_properties["diff_cells_ref"].push_back(ref_diff_cells[cluster]);
2004 cluster_properties["diff_cells_ref_rel_size"].push_back(ref_diff_cells[cluster] / protect_from_zero(ref_size_vec[cluster]));
2005 cluster_properties["diff_cells_test"].push_back(test_diff_cells[match]);
2006 cluster_properties["diff_cells_test_rel_size"].push_back(test_diff_cells[match] / protect_from_zero(test_size_vec[match]));
2007 cluster_properties["diff_cells"].push_back(ref_diff_cells[cluster] + test_diff_cells[match]);
2008
2009 cluster_properties["weighted_diff_cells_ref"].push_back(ref_diff_cells_weight[cluster]);
2010 cluster_properties["weighted_diff_cells_ref_rel_size"].push_back(ref_diff_cells_weight[cluster] / protect_from_zero(ref_weighted_size_vec[cluster]));
2011 cluster_properties["weighted_diff_cells_test"].push_back(test_diff_cells_weight[match]);
2012 cluster_properties["weighted_diff_cells_test_rel_size"].push_back(test_diff_cells_weight[match] / protect_from_zero(test_weighted_size_vec[match]));
2013 cluster_properties["weighted_diff_cells"].push_back(ref_diff_cells_weight[cluster] + test_diff_cells_weight[match]);
2014 }
2015 }
2016 }
2017
2018 using coll_type = decltype(Monitored::Collection("", std::declval<std::vector<double> &>()));
2019 using scalar_type = decltype(Monitored::Scalar("", std::declval<long long int>()));
2020
2021 std::vector<coll_type> collections;
2022 std::vector<scalar_type> count_scalars;
2023 std::vector<std::reference_wrapper<Monitored::IMonitoredVariable>> cluster_group, cell_group, counts_group;
2024
2025 collections.reserve(cluster_properties.size() + cell_properties.size());
2026 count_scalars.reserve(cell_counts.size() + 6 * 3);
2027 cluster_group.reserve(cluster_properties.size());
2028 cell_group.reserve(cell_properties.size());
2029 counts_group.reserve(cell_counts.size() + 3 + 6 * 3);
2030
2031 auto add_count_vars = [&](const std::string & name, const long long int ref_num, const long long int test_num)
2032 {
2033 count_scalars.emplace_back(Monitored::Scalar(prefix + "_" + name + "_ref", ref_num));
2034 counts_group.push_back(std::ref(count_scalars.back()));
2035
2036 count_scalars.emplace_back(Monitored::Scalar(prefix + "_" + name + "_test", test_num));
2037 counts_group.push_back(std::ref(count_scalars.back())); // cppcheck-suppress invalidContainer; reserve used
2038
2039 count_scalars.emplace_back(Monitored::Scalar(prefix + "_delta_" + name, test_num - ref_num));
2040 counts_group.push_back(std::ref(count_scalars.back())); // cppcheck-suppress invalidContainer; reserve used
2041 };
2042
2043 add_count_vars("num_clusters", clusters_1.number, clusters_2.number);
2044 add_count_vars("num_unmatched_clusters", sch.ref_unmatched(), sch.test_unmatched());
2045
2046 add_count_vars("num_same_E_cells", same_energy_1, same_energy_2);
2047 add_count_vars("num_same_abs_E_cells", same_abs_energy_1, same_abs_energy_2);
2048 add_count_vars("num_same_SNR_cells", same_snr_1, same_snr_2);
2049 add_count_vars("num_same_abs_SNR_cells", same_abs_snr_1, same_abs_snr_2);
2050
2051 auto mon_total_unmatched = Monitored::Scalar(prefix + "_num_unmatched_clusters", sch.ref_unmatched() + sch.test_unmatched());
2052 auto mon_same_cluster_cell = Monitored::Scalar(prefix + "_same_cluster_cells", same_cluster_cells_count);
2053 auto mon_diff_cluster_cell = Monitored::Scalar(prefix + "_diff_cluster_cells", diff_cluster_cells_count);
2054
2055 if(m_extraThingsToDo[DiffCells])
2056 {
2057 ATH_MSG_INFO("Different cells: " << diff_cluster_cells_count);
2058 }
2059
2060 counts_group.push_back(std::ref(mon_total_unmatched));
2061 counts_group.push_back(std::ref(mon_same_cluster_cell));
2062 counts_group.push_back(std::ref(mon_diff_cluster_cell));
2063
2064 for (const auto & k_v : cluster_properties)
2065 {
2066 collections.emplace_back(Monitored::Collection(prefix + "_cluster_" + k_v.first, k_v.second));
2067 cluster_group.push_back(std::ref(collections.back()));
2068 }
2069
2070 for (const auto & k_v : cell_properties)
2071 {
2072 collections.emplace_back(Monitored::Collection(prefix + "_cell_" + k_v.first, k_v.second));
2073 cell_group.push_back(std::ref(collections.back()));
2074 }
2075
2076 for (const auto & k_v : cell_counts)
2077 {
2078 count_scalars.emplace_back(Monitored::Scalar(prefix + "_" + k_v.first, k_v.second));
2079 counts_group.push_back(std::ref(count_scalars.back()));
2080 }
2081
2082 // Taking a std::ref of the back() iterator above is safe because the vector
2083 // has been reserved with the correct number of elements.
2084 // cppcheck-suppress invalidContainer
2085 auto monitor_clusters = Monitored::Group(m_moniTool, cluster_group);
2086 auto monitor_cells = Monitored::Group(m_moniTool, cell_group);
2087 auto monitor_counts = Monitored::Group(m_moniTool, counts_group);
2088
2089 return StatusCode::SUCCESS;
2090}
Scalar eta() const
pseudorapidity method
Scalar deltaPhi(const MatrixBase< Derived > &vec) const
Scalar phi() const
phi method
#define endmsg
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_INFO(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
Base class for cluster processing tools called from CaloClusterMaker.
static void build_similarity_map_helper(const CaloRecGPU::ClusterInfoArr &cluster_info_1, const CaloRecGPU::ClusterInfoArr &cluster_info_2, WeighMatch match, WeighNotMatch not_match)
#define CALORECGPU_BASIC_CLUSTER_PROPERTY(NAME,...)
#define CALORECGPU_BASIC_CELL_PROPERTY(NAME,...)
#define CALORECGPU_CLUSTER_MOMENT(...)
#define CALORECGPU_BASIC_CELL_TYPE(NAME,...)
#define CALORECGPU_COMPARED_CLUSTER_PROPERTY(NAME,...)
defines an "iterator" over instances of a given type in StoreGateSvc
static Double_t a
static Double_t tc
static Double_t rc
Header file to be included by clients of the Monitored infrastructure.
#define y
#define x
#define z
Container class for CaloCell.
Base class for GPU-accelerated cluster processing tools to be called from CaloGPUHybridClusterProcess...
Gaudi::Property< float > m_seedThreshold
Seed threshold to use for cluster matching.
Gaudi::Property< std::vector< SimpleSingleTool > > m_toolsToPlot
Tools to plot individually.
StatusCode match_clusters_perfectly(sample_comparisons_holder &sch, const CaloRecGPU::ConstantDataHolder &constant_data, const CaloRecGPU::CellInfoArr &cell_info, const CaloRecGPU::ClusterInfoArr &cluster_info_1, const CaloRecGPU::ClusterInfoArr &cluster_info_2, const bool match_without_shared) const
Gaudi::Property< std::vector< SimpleToolPair > > m_pairsToPlot
Pairs of tools to compare.
std::atomic< bool > m_plottedVariablesInitialized
A flag to signal that the variables to be monitored have been detected based on the booked histograms...
StatusCode add_data(const EventContext &ctx, const CaloRecGPU::ConstantDataHolder &constant_data, const CaloRecGPU::CellInfoArr *cell_info, const CaloRecGPU::ClusterInfoArr *clusters, const std::vector< int > &cells_prefix_sum, const std::string &tool_name) const
virtual StatusCode update_plots_start(const EventContext &ctx, const CaloRecGPU::ConstantDataHolder &constant_data, const xAOD::CaloClusterContainer *cluster_collection_ptr) const override
Gaudi::Property< float > m_termThreshold
Cell (terminal) threshold to use for cluster matching.
virtual StatusCode update_plots_end(const EventContext &ctx, const CaloRecGPU::ConstantDataHolder &constant_data, const xAOD::CaloClusterContainer *cluster_collection_ptr) const override
std::vector< bool > m_clusterPropertiesToDo
Control which properties will actually be calculated and stored.
bool m_doCells
If no properties are asked for, skip the relevant loops entirely...
ToolHandle< GenericMonitoringTool > m_moniTool
Monitoring tool.
int m_numToolsToKeep
The number of tools that will actually need to be kept in memory for combined plotting.
StatusCode add_combination(const EventContext &ctx, const CaloRecGPU::ConstantDataHolder &constant_data, const int index_1, const int index_2, const std::string &prefix, const bool match_in_energy, const bool match_without_shared, const bool match_perfectly) const
std::map< std::string, std::atomic< size_t > > m_numClustersPerTool ATLAS_THREAD_SAFE
Counts the total number of clusters per tool.
std::map< std::string, std::string > m_toolToIdMap
Maps tools to their respective identifying prefix for variables.
Gaudi::Property< float > m_growThreshold
Neighbor (growing) threshold to use for cluster matching.
Gaudi::Property< MatchingOptions > m_matchingOptions
Option for adjusting the parameters for the cluster matching algorithm.
StatusCode match_clusters(sample_comparisons_holder &sch, const CaloRecGPU::ConstantDataHolder &constant_data, const CaloRecGPU::CellInfoArr &cell_info, const CaloRecGPU::ClusterInfoArr &cluster_info_1, const CaloRecGPU::ClusterInfoArr &cluster_info_2, const bool match_in_energy, const bool match_without_shared) const
Gaudi::Property< std::vector< int > > m_missingCellsToFill
Cell indices to fill as disabled cells (useful if the cell vector is always missing the same cells).
SG::ReadHandleKey< CaloCellContainer > m_cellsKey
vector of names of the cell containers to use as input.
std::map< std::string, int > m_toolsToCheckFor
Map of the strings corresponding to all the tools that will be relevant for plotting (individually or...
size_t m_numEvents
Counts the number of events.
bool filter_tool_by_name(const std::string &tool_name) const
Returns true if this tool should be plotted for.
const CaloCell_ID * m_calo_id
Pointer to Calo ID Helper.
double m_min_similarity
Parameters for the cluster matching algorithm, for easier access.
virtual StatusCode update_plots(const EventContext &ctx, const CaloRecGPU::ConstantDataHolder &constant_data, const xAOD::CaloClusterContainer *cluster_collection_ptr, const CaloClusterCollectionProcessor *tool) const override
CaloGPUClusterAndCellDataMonitor(const std::string &type, const std::string &name, const IInterface *parent)
StatusCode update_cell_representation(const EventContext &ctx, const CaloRecGPU::ConstantDataHolder &constant_data, const CaloRecGPU::CellInfoArr *cell_info, CaloRecGPU::ClusterInfoArr *clusters, std::vector< int > &cells_prefix_sum) const
Update the cell representation so the cells-in-clusters are ordered by index and have a prefix sum.
std::mutex m_mutex
This mutex is locked to ensure only one thread detects the monotired variables.
virtual StatusCode finalize_plots() const override
Holds CPU and GPU versions of the geometry and cell noise information, which are assumed to be consta...
Definition DataHolders.h:27
CaloRecGPU::Helpers::CPU_object< CaloRecGPU::CellNoiseArr > m_cell_noise
Definition DataHolders.h:34
Holds the mutable per-event information (clusters and cells) and provides utilities to convert betwee...
Definition DataHolders.h:73
CaloRecGPU::Helpers::CUDA_pinned_CPU_object< CaloRecGPU::CellInfoArr > m_cell_info
CaloRecGPU::Helpers::CUDA_object< CaloRecGPU::CellInfoArr > m_cell_info_dev
CaloRecGPU::Helpers::CUDA_pinned_CPU_object< CaloRecGPU::ClusterInfoArr > m_clusters
void allocate(const bool also_GPU=true)
CaloRecGPU::Helpers::CUDA_object< CaloRecGPU::ClusterInfoArr > m_clusters_dev
Base class for tools that convert event information from the Athena structures to the GPU-friendly fo...
Base class for tools that convert event information from the GPU-friendly format used in CaloGPUHybri...
Group of local monitoring quantities and retain correlation when filling histograms
Declare a monitored scalar variable.
STL class.
STL class.
int count(std::string s, const std::string &regx)
count how many occurances of a regx are in a string
Definition hcg.cxx:146
bool match(std::string s1, std::string s2)
match the individual directories of two strings
Definition hcg.cxx:357
static CUDA_HOS_DEV float regularize_angle(const float b, const float a=0.f)
SimpleHolder< T, MemoryContext::CPU, true > CPU_object
Holds an object of type T in CPU memory.
Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration.
double protect_from_zero(const double x)
constexpr int NCaloCells
void apply_to_multi_class(F &&f, multi_class_holder< Types... >, Args &... args)
float float_unhack(const unsigned int bits)
ValuesCollection< T > Collection(std::string name, const T &collection)
Declare a monitored (double-convertible) collection.
void * ptr(T *p)
Definition SGImplSvc.cxx:74
Definition index.py:1
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.
int test_unmatched() const
Returns the number of unmatched clusters in the test (second) tool.
int ref_unmatched() const
Returns the number of unmatched clusters in the reference (first) tool.
int t2r(const int i) const
Converts a cluster index from the test tool (second) to the reference tool (first).
int r2t(const int i) const
Converts a cluster index from the reference tool (first) to the test tool (second).
Contains the per-event cell information: energy, timing, gain, quality and provenance.
constexpr int get_hash_ID(const int cell, const bool is_complete=false) const
constexpr bool is_bad(const int cell, const bool treat_L1_predicted_as_good=false, const bool is_complete=false) const
GPU version of CaloBadCellHelper::isBad.
unsigned char gain[NCaloCells]
constexpr bool is_valid(const int hash_ID, const bool is_complete=false, const bool all_cells_are_valid=false) const
union CaloRecGPU::ClusterInfoArr::@223264020040143137243361055037275307306030322160 cells
The class that actually expresses the cluster assignment.
constexpr int32_t secondary_cluster_index() const
constexpr int32_t secondary_cluster_weight() const
constexpr bool is_shared_between_clusters() const
constexpr bool is_part_of_cluster() const
constexpr int32_t cluster_index() const
static MomentsOptionsArray all()
A convenient way to handle a compile-time list of types, useful for several metaprogramming technique...
MsgStream & msg
Definition testRead.cxx:32