ATLAS Offline Software
Loading...
Searching...
No Matches
CaloGPUOutput Class Reference

Standard tool to output the GPU data representation to the non-standard file format that we have been using for plotting and validation purposes. More...

#include <CaloGPUOutput.h>

Inheritance diagram for CaloGPUOutput:
Collaboration diagram for CaloGPUOutput:

Public Member Functions

 CaloGPUOutput (const std::string &type, const std::string &name, const IInterface *parent)
virtual StatusCode execute (const EventContext &ctx, const CaloRecGPU::ConstantDataHolder &constant_data, CaloRecGPU::EventDataHolder &event_data, void *temporary_buffer) const override
virtual ~CaloGPUOutput ()=default

Private Attributes

Gaudi::Property< std::string > m_savePath {this, "SavePath", "./saved_clusters", "Path to where the files should be saved"}
 The path specifying the folder to which the files should be saved.
Gaudi::Property< std::string > m_filePrefix {this, "FilePrefix", "", "Prefix of the saved files"}
 The prefix of the saved files.
Gaudi::Property< std::string > m_fileSuffix {this, "FileSuffix", "", "Suffix of the saved files"}
 The suffix of the saved files.
Gaudi::Property< unsigned int > m_numWidth {this, "NumberWidth", 9, "The number of digits to reserve for the events"}
 The number of digits to reserve for the events.
Gaudi::Property< bool > m_sortedAndCutClusters {this, "UseSortedAndCutClusters", true, "Sort the clusters by transverse energy, apply a cut and ensure contiguous tags"}
 If true, sort the clusters by transverse energy and compactify the tags to ensure sequentiality.
Gaudi::Property< bool > m_outputTags {this, "OutputCellsAsTags", true, "Whether to output cell assignment as tags instead of a list of indices per cluster."}
 Whether to output cell assignment as tags instead of a list of indices per cluster.
Gaudi::Property< bool > m_onlyCellInfo {this, "OnlyOutputCellInfo", false, "Only output cell info"}
 If true, only output cell info (useful for reducing disk usage when running the full standalone version of the algorithms).
std::atomic< bool > m_constantDataSaved
 A flag to signal that the constant data has been adequately saved.
std::mutex m_mutex
 This mutex is locked when saving the constant data on the first event to ensure thread safety.

Detailed Description

Standard tool to output the GPU data representation to the non-standard file format that we have been using for plotting and validation purposes.

Author
Nuno Fernandes nuno..nosp@m.dos..nosp@m.santo.nosp@m.s.fe.nosp@m.rnand.nosp@m.es@c.nosp@m.ern.c.nosp@m.h
Date
30 May 2022

There are likely more elegant/general/generic/portable solutions, some of which might even avoid Root too, but our workflow was built around this one...

Definition at line 27 of file CaloGPUOutput.h.

Constructor & Destructor Documentation

◆ CaloGPUOutput()

CaloGPUOutput::CaloGPUOutput ( const std::string & type,
const std::string & name,
const IInterface * parent )

Definition at line 15 of file CaloGPUOutput.cxx.

15 :
16 base_class(type, name, parent),
18{
19}
std::atomic< bool > m_constantDataSaved
A flag to signal that the constant data has been adequately saved.

◆ ~CaloGPUOutput()

virtual CaloGPUOutput::~CaloGPUOutput ( )
virtualdefault

Member Function Documentation

◆ execute()

StatusCode CaloGPUOutput::execute ( const EventContext & ctx,
const CaloRecGPU::ConstantDataHolder & constant_data,
CaloRecGPU::EventDataHolder & event_data,
void * temporary_buffer ) const
overridevirtual

Definition at line 21 of file CaloGPUOutput.cxx.

22{
23 if (!m_constantDataSaved.load())
24 {
25 std::lock_guard<std::mutex> lock_guard(m_mutex);
26 if (!m_constantDataSaved.load())
27 {
28 const auto err1 = StandaloneDataIO::save_constants_to_folder(std::string(m_savePath), constant_data.m_geometry_dev,
31 {
32 return StatusCode::FAILURE;
33 }
34 m_constantDataSaved.store(true);
35 }
36 }
37
39
41 {
42 const auto err2 = StandaloneDataIO::save_cell_info_to_folder(ctx.evt(), std::string(m_savePath), cell_info, m_filePrefix, m_fileSuffix, m_numWidth);
43
45 {
46 return StatusCode::FAILURE;
47 }
48
49 return StatusCode::SUCCESS;
50 }
51
52
53 Helpers::CPU_object<ClusterInfoArr> clusters(event_data.m_clusters_dev), final_clusters(true);
54
55 //For every new index, the corresponding old index
56 std::vector<int> cluster_order(clusters->number);
57
58 //For every old index, the corresponding new index (if any)
59 std::vector<int> reverse_cluster_order(clusters->number, -1);
60
61 std::iota(cluster_order.begin(), cluster_order.end(), 0);
62
64 {
65 std::sort(cluster_order.begin(), cluster_order.end(), [&](const int a, const int b)
66 {
67 if (clusters->seedCellIndex[a] < 0)
68 {
69 return false;
70 //This means that clusters with no cells
71 //(marked as invalid) always compare lower,
72 //so they appear in the end.
73 }
74 else if (clusters->seedCellIndex[b] < 0)
75 {
76 return true;
77 }
78 return clusters->clusterEt[a] > clusters->clusterEt[b];
79 } );
80
81 unsigned int real_cluster_number = 0;
82
83 for (real_cluster_number = 0; real_cluster_number < cluster_order.size(); ++real_cluster_number)
84 {
85 if (clusters->seedCellIndex[real_cluster_number] < 0)
86 {
87 break;
88 }
89 else
90 {
91 reverse_cluster_order[cluster_order[real_cluster_number]] = real_cluster_number;
92 }
93 }
94
95 cluster_order.resize(real_cluster_number);
96
97 final_clusters->number = real_cluster_number;
98 final_clusters->has_deleted_clusters = false;
99 }
100
101 if (m_outputTags)
102 {
103 if (clusters->has_cells_per_cluster())
104 {
105 for (int i = 0; i < cell_info->number; ++i)
106 {
107 final_clusters->cells.tags[i] = 0;
108 }
109 for (int i = 0; i < clusters->number_cells; ++i)
110 {
111 const int real_cluster_index = reverse_cluster_order[clusters->clusterIndices[i]];
112
113 if (real_cluster_index < 0)
114 {
115 continue;
116 }
117
118 const int this_cell_index = clusters->cells.indices[i];
119
120 const float weight = clusters->cellWeights[i];
121
122 uint32_t weight_as_int = 0;
123 std::memcpy(&weight_as_int, &weight, sizeof(float));
124 //On the platforms we expect to be running this, it should be fine.
125 //Still UB.
126 //With C++20, we could do that bit-cast thing.
127
128 if (weight_as_int == 0)
129 {
130 weight_as_int = 1;
131 //Subnormal,
132 //but just to distinguish from
133 //a non-shared cluster.
134 }
135
136 const ClusterTag this_tag = final_clusters->cells.tags[this_cell_index];
137
138 const int other_index = this_tag.is_part_of_cluster() ? this_tag.cluster_index() : -1;
139
140 if (other_index < 0)
141 {
142 if (weight < 0.5f)
143 {
144 final_clusters->cells.tags[this_cell_index] = ClusterTag::make_tag(real_cluster_index, weight_as_int, 0);
145 }
146 else
147 {
148 final_clusters->cells.tags[this_cell_index] = ClusterTag::make_tag(real_cluster_index);
149 }
150 }
151 else if (weight > 0.5f)
152 {
153 final_clusters->cells.tags[this_cell_index] = ClusterTag::make_tag(real_cluster_index, this_tag.secondary_cluster_weight(), other_index);
154 }
155 else if (weight == 0.5f)
156 //Unlikely, but...
157 {
158 const int max_cluster = real_cluster_index > other_index ? real_cluster_index : other_index;
159 const int min_cluster = real_cluster_index > other_index ? other_index : real_cluster_index;
160 final_clusters->cells.tags[this_cell_index] = ClusterTag::make_tag(max_cluster, weight_as_int, min_cluster);
161 }
162 else /*if (weight < 0.5f)*/
163 {
164 final_clusters->cells.tags[this_cell_index] = ClusterTag::make_tag(other_index, weight_as_int, real_cluster_index);
165 }
166
167 //All of this logic assumes a cell is shared by at most two clusters
168 //with weights such that w_1 + w_2 = 1.
169 //This is not necessarily valid with local calibrations.
170
171 }
172 }
173 else
174 {
175 for (int i = 0; i < clusters->number_cells; ++i)
176 {
177 const ClusterTag this_tag = clusters->cells.tags[i];
178
179 const int primary_index = ( this_tag.is_part_of_cluster() ?
180 reverse_cluster_order[this_tag.cluster_index()] :
181 -1
182 );
183 const int secondary_index = ( this_tag.is_part_of_cluster() && this_tag.is_shared_between_clusters() ?
184 reverse_cluster_order[this_tag.secondary_cluster_index()] :
185 -1
186 );
187
188 const unsigned int weight = this_tag.secondary_cluster_weight();
189
190 if (primary_index >= 0 && secondary_index >= 0)
191 {
192 final_clusters->cells.tags[i] = ClusterTag::make_tag(primary_index, weight, secondary_index);
193 }
194 else if (primary_index >= 0)
195 {
196 final_clusters->cells.tags[i] = ClusterTag::make_tag(primary_index);
197 }
198 else if (secondary_index >= 0)
199 {
200 final_clusters->cells.tags[i] = ClusterTag::make_tag(secondary_index);
201 }
202 else
203 {
204 final_clusters->cells.tags[i] = 0;
205 }
206 }
207 }
208
209 final_clusters->number_cells = cell_info->number;
210 final_clusters->state = ClusterInformationState::TagsWithBasicInfo;
211 }
212 else
213 {
214 auto update_prefix_sum = [&]()
215 {
216 int running_count = 0;
217 for (int i = 1; i <= final_clusters->number; ++i)
218 {
219 running_count += final_clusters->cellsPrefixSum[i];
220 final_clusters->cellsPrefixSum[i] = running_count;
221 }
222 final_clusters->number_cells = running_count;
223 };
224
225 for (int i = 0; i <= final_clusters->number; ++i)
226 {
227 final_clusters->cellsPrefixSum[i] = 0;
228 if (i < final_clusters->number)
229 {
230 clusters->clusterIndices[i] = 0;
231 //Temporary storage for the index within the cluster.
232 }
233 }
234
235 if (!clusters->has_cells_per_cluster())
236 {
237 for (int i = 0; i < clusters->number_cells; ++i)
238 {
239 const ClusterTag this_tag = clusters->cells.tags[i];
240
241 const int primary_index = ( this_tag.is_part_of_cluster() ?
242 reverse_cluster_order[this_tag.cluster_index()] :
243 -1
244 );
245 const int secondary_index = ( this_tag.is_part_of_cluster() && this_tag.is_shared_between_clusters() ?
246 reverse_cluster_order[this_tag.secondary_cluster_index()] :
247 -1
248 );
249
250 if (primary_index >= 0)
251 {
252 final_clusters->cellsPrefixSum[primary_index + 1] += 1;
253 }
254
255 if (secondary_index >= 0)
256 {
257 final_clusters->cellsPrefixSum[secondary_index + 1] += 1;
258 }
259 }
260
261 update_prefix_sum();
262
263 for (int i = 0; i < clusters->number_cells; ++i)
264 {
265 const ClusterTag this_tag = clusters->cells.tags[i];
266
267 const int primary_index = ( this_tag.is_part_of_cluster() ?
268 reverse_cluster_order[this_tag.cluster_index()] :
269 -1
270 );
271 const int secondary_index = ( this_tag.is_part_of_cluster() && this_tag.is_shared_between_clusters() ?
272 reverse_cluster_order[this_tag.secondary_cluster_index()] :
273 -1
274 );
275
276 const unsigned int weight_as_int = this_tag.secondary_cluster_weight();
277
278 float weight;
279
280 std::memcpy(&weight, &weight_as_int, sizeof(float));
281
282 if (primary_index >= 0)
283 {
284 const int this_cell_start = final_clusters->cellsPrefixSum[primary_index];
285
286 int & this_cell_count = clusters->clusterIndices[primary_index];
287
288 ++this_cell_count;
289
290 const bool is_seed_cell = (i == clusters->seedCellIndex[this_tag.cluster_index()]);
291
292 const int this_index_in_cluster = this_cell_start + this_cell_count * is_seed_cell;
293
294 final_clusters->cells.indices[this_index_in_cluster] = i;
295 final_clusters->cellWeights[this_index_in_cluster] = 1.0f - weight;
296 final_clusters->clusterIndices[this_index_in_cluster] = primary_index;
297 }
298
299 if (secondary_index >= 0)
300 {
301 const int this_cell_start = final_clusters->cellsPrefixSum[secondary_index];
302
303 int & this_cell_count = clusters->clusterIndices[secondary_index];
304
305 ++this_cell_count;
306
307 const bool is_seed_cell = (i == clusters->seedCellIndex[this_tag.secondary_cluster_index()]);
308
309 const int this_index_in_cluster = this_cell_start + this_cell_count * is_seed_cell;
310
311 final_clusters->cells.indices[this_index_in_cluster] = i;
312 final_clusters->cellWeights[this_index_in_cluster] = weight;
313 final_clusters->clusterIndices[this_index_in_cluster] = primary_index;
314 }
315 }
316 }
317 else
318 {
319 final_clusters->cellsPrefixSum[0] = 0;
320
321 for (unsigned int i = 0; i < cluster_order.size(); ++i)
322 {
323 const int original_cluster = cluster_order[i];
324 final_clusters->cellsPrefixSum[i + 1] = clusters->cellsPrefixSum[original_cluster + 1] - clusters->cellsPrefixSum[original_cluster];
325 }
326
327 update_prefix_sum();
328
329 for (unsigned int i = 0; i < cluster_order.size(); ++i)
330 {
331 const int original_cluster = cluster_order[i];
332
333 int new_index = final_clusters->cellsPrefixSum[i];
334
335 for (int j = clusters->cellsPrefixSum[original_cluster]; j < clusters->cellsPrefixSum[original_cluster + 1]; ++j, ++new_index)
336 {
337 final_clusters->cells.indices[new_index] = clusters->cells.indices[j];
338 final_clusters->cellWeights[new_index] = clusters->cellWeights[j];
339 final_clusters->clusterIndices[new_index] = clusters->clusterIndices[j];
340 }
341 }
342
343 }
344
345 final_clusters->state = ClusterInformationState::WithBasicInfo;
346 }
347
348 for (unsigned int i = 0; i < cluster_order.size(); ++i)
349 {
350 final_clusters->clusterEnergy[i] = clusters->clusterEnergy[cluster_order[i]];
351 final_clusters->clusterEt[i] = clusters->clusterEt[cluster_order[i]];
352 final_clusters->clusterEta[i] = clusters->clusterEta[cluster_order[i]];
353 final_clusters->clusterPhi[i] = clusters->clusterPhi[cluster_order[i]];
354 final_clusters->seedCellIndex[i] = clusters->seedCellIndex[cluster_order[i]];
355 }
356
357 const auto err2 = StandaloneDataIO::save_event_to_folder(ctx.evt(), std::string(m_savePath), cell_info, final_clusters,
359
361 {
362 return StatusCode::FAILURE;
363 }
364
365 return StatusCode::SUCCESS;
366
367}
static Double_t a
if(febId1==febId2)
Gaudi::Property< std::string > m_savePath
The path specifying the folder to which the files should be saved.
std::mutex m_mutex
This mutex is locked when saving the constant data on the first event to ensure thread safety.
Gaudi::Property< bool > m_outputTags
Whether to output cell assignment as tags instead of a list of indices per cluster.
Gaudi::Property< std::string > m_filePrefix
The prefix of the saved files.
Gaudi::Property< unsigned int > m_numWidth
The number of digits to reserve for the events.
Gaudi::Property< bool > m_onlyCellInfo
If true, only output cell info (useful for reducing disk usage when running the full standalone versi...
Gaudi::Property< bool > m_sortedAndCutClusters
If true, sort the clusters by transverse energy and compactify the tags to ensure sequentiality.
Gaudi::Property< std::string > m_fileSuffix
The suffix of the saved files.
CaloRecGPU::Helpers::CUDA_object< CaloRecGPU::CellNoiseArr > m_cell_noise_dev
Definition DataHolders.h:38
CaloRecGPU::Helpers::CUDA_object< CaloRecGPU::GeometryArr > m_geometry_dev
Definition DataHolders.h:36
CaloRecGPU::Helpers::CUDA_object< CaloRecGPU::CellInfoArr > m_cell_info_dev
CaloRecGPU::Helpers::CUDA_object< CaloRecGPU::ClusterInfoArr > m_clusters_dev
SimpleHolder< T, MemoryContext::CPU, true > CPU_object
Holds an object of type T in CPU memory.
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.
setEventNumber uint32_t
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 constexpr carrier make_tag(const uint16_t cluster_index=0, const int32_t weight=0, const uint16_t second_cluster_index=0)
static ErrorState save_constants_to_folder(const std::filesystem::path &folder, const CaloRecGPU::Helpers::CPU_object< CaloRecGPU::GeometryArr > &geo, const CaloRecGPU::Helpers::CPU_object< CaloRecGPU::CellNoiseArr > &noise, const std::string &prefix="", const std::string &suffix="", const bool output_errors=true)
static ErrorState save_event_to_folder(const size_t event_number, const std::filesystem::path &folder, const CaloRecGPU::Helpers::CPU_object< CaloRecGPU::CellInfoArr > &cell_info, const CaloRecGPU::Helpers::CPU_object< CaloRecGPU::ClusterInfoArr > &clusters, const std::string &prefix="", const std::string &suffix="", const unsigned int num_width=9, const bool output_errors=true)
static ErrorState save_cell_info_to_folder(const size_t event_number, const std::filesystem::path &folder, const CaloRecGPU::Helpers::CPU_object< CaloRecGPU::CellInfoArr > &cell_info, const std::string &prefix="", const std::string &suffix="", const unsigned int num_width=9, const bool output_errors=true)
std::string number(const double &d, const std::string &s)
Definition utils.cxx:186

Member Data Documentation

◆ m_constantDataSaved

std::atomic<bool> CaloGPUOutput::m_constantDataSaved
mutableprivate

A flag to signal that the constant data has been adequately saved.

This is required for everything to work properly in a multi-threaded context...

Definition at line 84 of file CaloGPUOutput.h.

◆ m_filePrefix

Gaudi::Property<std::string> CaloGPUOutput::m_filePrefix {this, "FilePrefix", "", "Prefix of the saved files"}
private

The prefix of the saved files.

Empty string by default.

Definition at line 53 of file CaloGPUOutput.h.

53{this, "FilePrefix", "", "Prefix of the saved files"};

◆ m_fileSuffix

Gaudi::Property<std::string> CaloGPUOutput::m_fileSuffix {this, "FileSuffix", "", "Suffix of the saved files"}
private

The suffix of the saved files.

Empty string by default.

Definition at line 58 of file CaloGPUOutput.h.

58{this, "FileSuffix", "", "Suffix of the saved files"};

◆ m_mutex

std::mutex CaloGPUOutput::m_mutex
mutableprivate

This mutex is locked when saving the constant data on the first event to ensure thread safety.

Otherwise, it's unused.

Definition at line 89 of file CaloGPUOutput.h.

◆ m_numWidth

Gaudi::Property<unsigned int> CaloGPUOutput::m_numWidth {this, "NumberWidth", 9, "The number of digits to reserve for the events"}
private

The number of digits to reserve for the events.

9 by default.

Definition at line 63 of file CaloGPUOutput.h.

63{this, "NumberWidth", 9, "The number of digits to reserve for the events"};

◆ m_onlyCellInfo

Gaudi::Property<bool> CaloGPUOutput::m_onlyCellInfo {this, "OnlyOutputCellInfo", false, "Only output cell info"}
private

If true, only output cell info (useful for reducing disk usage when running the full standalone version of the algorithms).

Definition at line 78 of file CaloGPUOutput.h.

78{this, "OnlyOutputCellInfo", false, "Only output cell info"};

◆ m_outputTags

Gaudi::Property<bool> CaloGPUOutput::m_outputTags {this, "OutputCellsAsTags", true, "Whether to output cell assignment as tags instead of a list of indices per cluster."}
private

Whether to output cell assignment as tags instead of a list of indices per cluster.

True by default.

Definition at line 73 of file CaloGPUOutput.h.

73{this, "OutputCellsAsTags", true, "Whether to output cell assignment as tags instead of a list of indices per cluster."};

◆ m_savePath

Gaudi::Property<std::string> CaloGPUOutput::m_savePath {this, "SavePath", "./saved_clusters", "Path to where the files should be saved"}
private

The path specifying the folder to which the files should be saved.

Default ./saved_clusters

Definition at line 48 of file CaloGPUOutput.h.

48{this, "SavePath", "./saved_clusters", "Path to where the files should be saved"};

◆ m_sortedAndCutClusters

Gaudi::Property<bool> CaloGPUOutput::m_sortedAndCutClusters {this, "UseSortedAndCutClusters", true, "Sort the clusters by transverse energy, apply a cut and ensure contiguous tags"}
private

If true, sort the clusters by transverse energy and compactify the tags to ensure sequentiality.

Definition at line 68 of file CaloGPUOutput.h.

68{this, "UseSortedAndCutClusters", true, "Sort the clusters by transverse energy, apply a cut and ensure contiguous tags"};

The documentation for this class was generated from the following files: