ATLAS Offline Software
BasicGPUToAthenaImporter.cxx
Go to the documentation of this file.
1 //
2 // Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3 //
4 // Dear emacs, this is -*- c++ -*-
5 //
6 
8 
10 
12 
13 #include <vector>
14 #include <algorithm>
15 #include <memory>
16 
19 
20 #include "boost/chrono/chrono.hpp"
21 #include "boost/chrono/thread_clock.hpp"
22 
23 using namespace CaloRecGPU;
24 
25 BasicGPUToAthenaImporter::BasicGPUToAthenaImporter(const std::string & type, const std::string & name, const IInterface * parent):
26  base_class(type, name, parent),
27  CaloGPUTimed(this)
28 {
29 }
30 
31 #include "MacroHelpers.h"
32 
34 {
36 
37  ATH_CHECK( detStore()->retrieve(m_calo_id, "CaloCell_ID") );
38 
39  auto get_option_from_string = [](const std::string & str, bool & failed)
40  {
41  failed = false;
44  SW_55ele,
45  SW_35ele,
46  SW_37ele,
47  SW_55gam,
48  SW_35gam,
49  SW_37gam,
50  SW_55Econv,
51  SW_35Econv,
52  SW_37Econv,
53  SW_softe,
54  Topo_420,
55  Topo_633,
56  SW_7_11,
57  SuperCluster,
58  Tower_01_01,
59  Tower_005_005,
60  Tower_fixed_area
61  )
62  )
63  //I know Topological Clustering only supports a subset of those,
64  //but this is supposed to be a general data exporting tool...
65  else
66  {
67  //failed = true;
69  }
70  };
71 
72  bool size_failed = false;
73  m_clusterSize = get_option_from_string(m_clusterSizeString, size_failed);
74 
76  {
77  ATH_MSG_ERROR("Invalid Cluster Size: " << m_clusterSizeString);
78  }
79 
80  if (size_failed)
81  {
82  return StatusCode::FAILURE;
83  }
84 
85  return StatusCode::SUCCESS;
86 }
87 
88 
89 
91  const ConstantDataHolder &,
92  EventDataHolder & ed,
93  xAOD::CaloClusterContainer * cluster_container) const
94 {
95 
96 
97  using clock_type = boost::chrono::thread_clock;
98  auto time_cast = [](const auto & before, const auto & after)
99  {
100  return boost::chrono::duration_cast<boost::chrono::microseconds>(after - before).count();
101  };
102 
103  const auto start = clock_type::now();
104 
105  SG::ReadHandle<CaloCellContainer> cell_collection(m_cellsKey, ctx);
106  if ( !cell_collection.isValid() )
107  {
108  ATH_MSG_ERROR( " Cannot retrieve CaloCellContainer: " << cell_collection.name() );
109  return StatusCode::FAILURE;
110  }
111  const DataLink<CaloCellContainer> cell_collection_link (cell_collection.name(), ctx);
112 
113  ed.returnToCPU(!m_keepGPUData, true, true, false);
114 
115  const auto after_send = clock_type::now();
116 
117  std::vector<std::unique_ptr<CaloClusterCellLink>> cell_links;
118 
119  cell_links.reserve(ed.m_clusters->number);
120 
121  for (int i = 0; i < ed.m_clusters->number; ++i)
122  {
123  if (ed.m_clusters->seedCellID[i] >= 0)
124  {
125  cell_links.emplace_back(std::make_unique<CaloClusterCellLink>(cell_collection_link));
126  cell_links.back()->reserve(256);
127  //To be adjusted.
128  }
129  else
130  {
131  cell_links.emplace_back(nullptr);
132  //The excluded clusters don't have any cells.
133  }
134  }
135 
136  const auto after_creation = clock_type::now();
137 
138 
139  //cell_index is the actual cell index in the full set of cells (identifier hash)
140  //cell_count is the cell position in the cell collection (what we want for the weight)
141  const auto process_cell = [&](const int cell_index, const int cell_count)
142  {
143  const ClusterTag this_tag = ed.m_cell_state->clusterTag[cell_index];
144  if (this_tag.is_part_of_cluster())
145  {
146  const int this_index = this_tag.cluster_index();
147  const int32_t weight_pattern = this_tag.secondary_cluster_weight();
148 
149  float tempf = 1.0f;
150 
151  std::memcpy(&tempf, &weight_pattern, sizeof(float));
152  //C++20 would give us bit cast to do this more properly.
153  //Still, given how the bit pattern is created,
154  //it should be safe.
155 
156  const float reverse_weight = tempf;
157 
158  const float this_weight = 1.0f - reverse_weight;
159 
160  if (cell_links[this_index])
161  {
162  cell_links[this_index]->addCell(cell_count, this_weight);
163 
164  if (cell_index == ed.m_clusters->seedCellID[this_index] && cell_links[this_index]->size() > 1)
165  //Seed cells aren't shared,
166  //so no need to check this on the other case.
167  {
168  CaloClusterCellLink::iterator begin_it = cell_links[this_index]->begin();
169  CaloClusterCellLink::iterator back_it = std::prev(cell_links[this_index]->end());
170 
171  const unsigned int first_idx = begin_it.index();
172  const double first_wgt = begin_it.weight();
173 
174  begin_it.reindex(back_it.index());
175  begin_it.reweight(back_it.weight());
176 
177  back_it.reindex(first_idx);
178  back_it.reweight(first_wgt);
179 
180  //Of course, this is to ensure the first cell is the seed cell,
181  //in accordance to the way some cluster properties
182  //(mostly phi-related) are calculated.
183  }
184  }
185 
186  if (this_tag.is_shared_between_clusters())
187  {
188  const int other_index = this_tag.secondary_cluster_index();
189  if (cell_links[other_index])
190  {
191  cell_links[other_index]->addCell(cell_count, reverse_weight);
192  }
193  }
194  }
195  };
196 
197  if (cell_collection->isOrderedAndComplete())
198  //Fast path: cell indices within the collection and identifierHashes match!
199  {
200  for (int cell_index = 0; cell_index < NCaloCells; ++cell_index)
201  {
202  process_cell(cell_index, cell_index);
203  }
204  }
205  else if (cell_collection->isOrdered() && m_missingCellsToFill.size() > 0)
206  {
207  size_t missing_cell_count = 0;
208  for (int cell_index = 0; cell_index < NCaloCells; ++cell_index)
209  {
210  if (missing_cell_count < m_missingCellsToFill.size() && cell_index == m_missingCellsToFill[missing_cell_count])
211  {
212  ++missing_cell_count;
213  continue;
214  }
215  process_cell(cell_index, cell_index - missing_cell_count);
216  }
217  }
218  else
219  //Slow path: be careful.
220  {
221  CaloCellContainer::const_iterator iCells = cell_collection->begin();
222 
223  for (int cell_count = 0; iCells != cell_collection->end(); ++iCells, ++cell_count)
224  {
225  const CaloCell * cell = (*iCells);
226 
227  //const int cell_index = m_calo_id->calo_cell_hash(cell->ID());
228  const int cell_index = cell->caloDDE()->calo_hash();
229 
230  process_cell(cell_index, cell_count);
231  }
232  }
233  const auto after_cells = clock_type::now();
234 
235  std::vector<int> cluster_order(ed.m_clusters->number);
236 
237  std::iota(cluster_order.begin(), cluster_order.end(), 0);
238 
239  std::sort(cluster_order.begin(), cluster_order.end(), [&](const int a, const int b) -> bool
240  {
241  const bool a_valid = ed.m_clusters->seedCellID[a] >= 0;
242  const bool b_valid = ed.m_clusters->seedCellID[b] >= 0;
243  if (a_valid && b_valid)
244  {
245  return ed.m_clusters->clusterEt[a]
246  > ed.m_clusters->clusterEt[b];
247  }
248  else if (a_valid)
249  {
250  return true;
251  }
252  else if (b_valid)
253  {
254  return false;
255  }
256  else
257  {
258  return b > a;
259  }
260  } );
261 
262  //Ordered by Et as in the default algorithm...
263  //The fact that some invalid clusters
264  //(with possibly trash values for Et)
265  //can crop up is irrelevant since
266  //we don't add those anyway:
267  //the rest is still ordered like we want it to be.
268 
269  const auto after_sort = clock_type::now();
270 
271  cluster_container->clear();
272  cluster_container->reserve(cell_links.size());
273 
274  for (size_t i = 0; i < cluster_order.size(); ++i)
275  {
276  const int cluster_index = cluster_order[i];
277 
278  if (cell_links[cluster_index] != nullptr && cell_links[cluster_index]->size() > 0)
279  {
280  xAOD::CaloCluster * cluster = new xAOD::CaloCluster();
281  cluster_container->push_back(cluster);
282 
283  cluster->addCellLink(cell_links[cluster_index].release());
284  cluster->setClusterSize(m_clusterSize);
285  if (m_useCPUPropertiesCalculation)
286  {
287  CaloClusterKineHelper::calculateKine(cluster, false, true, true);
288  }
289  else
290  {
291  cluster->setE(ed.m_clusters->clusterEnergy[cluster_index]);
292  cluster->setEta(ed.m_clusters->clusterEta[cluster_index]);
293  cluster->setPhi(ed.m_clusters->clusterPhi[cluster_index]);
294  }
295 
296  if (m_saveUncalibrated)
297  {
298  cluster->setRawE(cluster->calE());
299  cluster->setRawEta(cluster->calEta());
300  cluster->setRawPhi(cluster->calPhi());
301  cluster->setRawM(cluster->calM());
302  }
303 
304  }
305 
306  }
307 
308  const auto after_fill = clock_type::now();
309 
310  if (m_measureTimes)
311  {
312  record_times(ctx.evt(), time_cast(start, after_send),
313  time_cast(after_send, after_creation),
314  time_cast(after_creation, after_cells),
315  time_cast(after_cells, after_sort),
316  time_cast(after_sort, after_fill)
317  );
318  }
319 
320  return StatusCode::SUCCESS;
321 
322 }
323 
324 
326 {
327 
328  if (m_measureTimes)
329  {
330  print_times("Transfer_from_GPU Cluster_Creation Cell_Adding Sorting Collection_Filling", 5);
331  }
332  return StatusCode::SUCCESS;
333 }
334 
python.PyKernel.retrieve
def retrieve(aClass, aKey=None)
Definition: PyKernel.py:110
CaloClusterKineHelper.h
xAOD::CaloCluster_v1::CSize_Unknown
@ CSize_Unknown
Definition: CaloCluster_v1.h:115
DataModel_detail::const_iterator
Const iterator class for DataVector/DataList.
Definition: DVLIterator.h:82
xAOD::CaloCluster_v1::setRawM
void setRawM(flt_t)
Set mass for singal state UNCALIBRATED.
Definition: CaloCluster_v1.cxx:299
ReadCellNoiseFromCool.cell
cell
Definition: ReadCellNoiseFromCool.py:53
CaloRecGPU::ClusterTag::secondary_cluster_index
constexpr int32_t secondary_cluster_index() const
Definition: TagDefinitions.h:253
CaloRecGPU::ClusterTag::is_shared_between_clusters
constexpr bool is_shared_between_clusters() const
Definition: TagDefinitions.h:273
SG::ReadHandle
Definition: StoreGate/StoreGate/ReadHandle.h:67
SG::VarHandleBase::name
const std::string & name() const
Return the StoreGate ID for the referenced object.
Definition: AthToolSupport/AsgDataHandles/Root/VarHandleBase.cxx:75
xAOD::CaloCluster_v1::calE
flt_t calE() const
Geet Energy in signal state CALIBRATED.
mergePhysValFiles.start
start
Definition: DataQuality/DataQualityUtils/scripts/mergePhysValFiles.py:13
xAOD::CaloCluster_v1::calEta
flt_t calEta() const
Get in signal state CALIBRATED.
BasicGPUToAthenaImporter::m_clusterSizeString
Gaudi::Property< std::string > m_clusterSizeString
Cluster size. Should be set accordingly to the threshold.
Definition: BasicGPUToAthenaImporter.h:65
CaloCellContainer::isOrdered
bool isOrdered() const
tell wether container is ordered
Definition: CaloCellContainer.h:293
BasicGPUToAthenaImporter::convert
virtual StatusCode convert(const EventContext &ctx, const CaloRecGPU::ConstantDataHolder &constant_data, CaloRecGPU::EventDataHolder &event_data, xAOD::CaloClusterContainer *cluster_collection) const override
Definition: BasicGPUToAthenaImporter.cxx:90
BasicGPUToAthenaImporter.h
BasicGPUToAthenaImporter::m_keepGPUData
Gaudi::Property< bool > m_keepGPUData
If true, do not delete the GPU data representation.
Definition: BasicGPUToAthenaImporter.h:51
xAOD::CaloCluster_v1::calM
flt_t calM() const
Get mass in signal state CALIBRATED.
python.CaloAddPedShiftConfig.type
type
Definition: CaloAddPedShiftConfig.py:42
CaloGPUTimed
Base class to provide some basic common infrastructure for timing measurements...
Definition: CaloGPUTimed.h:25
xAOD::CaloCluster
CaloCluster_v1 CaloCluster
Define the latest version of the calorimeter cluster class.
Definition: Event/xAOD/xAODCaloEvent/xAODCaloEvent/CaloCluster.h:19
CaloDetDescrManager.h
Definition of CaloDetDescrManager.
CaloRecGPU::ClusterTag
Definition: TagDefinitions.h:222
mergePhysValFiles.end
end
Definition: DataQuality/DataQualityUtils/scripts/mergePhysValFiles.py:92
BasicGPUToAthenaImporter::m_clusterSize
xAOD::CaloCluster::ClusterSize m_clusterSize
Definition: BasicGPUToAthenaImporter.h:67
CRGPU_CHEAP_STRING_TO_ENUM
#define CRGPU_CHEAP_STRING_TO_ENUM(VAR, PREFIX, ONE,...)
Checks a string variable, VAR, for matching enum identifiers (ONE and the remaining variadic argument...
Definition: MacroHelpers.h:148
python.setupRTTAlg.size
int size
Definition: setupRTTAlg.py:39
CaloRecGPU::ClusterTag::cluster_index
constexpr int32_t cluster_index() const
Definition: TagDefinitions.h:243
xAOD::CaloCluster_v1::setE
void setE(flt_t)
Definition: CaloCluster_v1.cxx:375
BasicGPUToAthenaImporter::m_cellsKey
SG::ReadHandleKey< CaloCellContainer > m_cellsKey
vector of names of the cell containers to use as input.
Definition: BasicGPUToAthenaImporter.h:62
xAOD::CaloCluster_v1
Description of a calorimeter cluster.
Definition: CaloCluster_v1.h:62
python.handimod.now
now
Definition: handimod.py:674
CaloRecGPU::EventDataHolder
Definition: DataHolders.h:35
xAOD::CaloCluster_v1::setRawE
void setRawE(flt_t)
Set Energy for signal state UNCALIBRATED.
Definition: CaloCluster_v1.cxx:284
ATH_MSG_ERROR
#define ATH_MSG_ERROR(x)
Definition: AthMsgStreamMacros.h:33
lumiFormat.i
int i
Definition: lumiFormat.py:85
BasicGPUToAthenaImporter::m_missingCellsToFill
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).
Definition: BasicGPUToAthenaImporter.h:71
CRGPU_RECURSIVE_MACRO
#define CRGPU_RECURSIVE_MACRO(...)
Expands recursive macros.
Definition: MacroHelpers.h:70
EL::StatusCode
::StatusCode StatusCode
StatusCode definition for legacy code.
Definition: PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/StatusCode.h:22
python.CaloAddPedShiftConfig.str
str
Definition: CaloAddPedShiftConfig.py:42
BasicGPUToAthenaImporter::BasicGPUToAthenaImporter
BasicGPUToAthenaImporter(const std::string &type, const std::string &name, const IInterface *parent)
Definition: BasicGPUToAthenaImporter.cxx:25
CaloGPUTimed::print_times
void print_times(const std::string &header, const size_t time_size) const
Definition: CaloGPUTimed.h:143
test_pyathena.parent
parent
Definition: test_pyathena.py:15
xAOD::CaloCluster_v1::setRawEta
void setRawEta(flt_t)
Set for signal state UNCALIBRATED.
Definition: CaloCluster_v1.cxx:289
ATH_CHECK
#define ATH_CHECK
Definition: AthCheckMacros.h:40
xAOD::CaloCluster_v1::calPhi
flt_t calPhi() const
Get in signal state CALIBRATED.
CaloRecGPU::NCaloCells
constexpr int NCaloCells
Definition: BaseDefinitions.h:12
CaloRecGPU::EventDataHolder::returnToCPU
void returnToCPU(const bool clear_GPU=false, const bool return_cells=true, const bool return_clusters=true, const bool return_moments=true)
SG::VarHandleKey::initialize
StatusCode initialize(bool used=true)
If this object is used as a property, then this should be called during the initialize phase.
Definition: AthToolSupport/AsgDataHandles/Root/VarHandleKey.cxx:103
BasicGPUToAthenaImporter::finalize
virtual StatusCode finalize() override
Definition: BasicGPUToAthenaImporter.cxx:325
DataVector
Derived DataVector<T>.
Definition: DataVector.h:794
SG::ReadHandle::isValid
virtual bool isValid() override final
Can the handle be successfully dereferenced?
MacroHelpers.h
python.PyKernel.detStore
detStore
Definition: PyKernel.py:41
BasicGPUToAthenaImporter::m_calo_id
const CaloCell_ID * m_calo_id
Pointer to Calo ID Helper.
Definition: BasicGPUToAthenaImporter.h:76
python.EventInfoMgtInit.release
release
Definition: EventInfoMgtInit.py:23
name
std::string name
Definition: Control/AthContainers/Root/debug.cxx:240
plotBeamSpotMon.b
b
Definition: plotBeamSpotMon.py:76
errorcheck.h
Helpers for checking error return status codes and reporting errors.
CaloRecGPU::ClusterTag::is_part_of_cluster
constexpr bool is_part_of_cluster() const
Definition: TagDefinitions.h:233
DataVector::end
const_iterator end() const noexcept
Return a const_iterator pointing past the end of the collection.
BasicGPUToAthenaImporter::initialize
virtual StatusCode initialize() override
Definition: BasicGPUToAthenaImporter.cxx:33
a
TList * a
Definition: liststreamerinfos.cxx:10
CaloCellContainer::isOrderedAndComplete
bool isOrderedAndComplete() const
tell wether container is complete and in order
Definition: CaloCellContainer.h:298
xAOD::CaloCluster_v1::addCellLink
void addCellLink(CaloClusterCellLink *CCCL)
Definition: CaloCluster_v1.h:724
CaloCell
Data object for each calorimeter readout cell.
Definition: CaloCell.h:57
xAOD::CaloCluster_v1::setClusterSize
void setClusterSize(const ClusterSize)
Get cluster size.
Definition: CaloCluster_v1.cxx:369
CaloRecGPU::EventDataHolder::m_clusters
CaloRecGPU::Helpers::CUDA_pinned_CPU_object< CaloRecGPU::ClusterInfoArr > m_clusters
Definition: DataHolders.h:76
CaloGPUTimed::m_measureTimes
Gaudi::Property< bool > m_measureTimes
If true, times are recorded to the file given by m_timeFileName.
Definition: CaloGPUTimed.h:46
CaloRecGPU::EventDataHolder::m_cell_state
CaloRecGPU::Helpers::CUDA_pinned_CPU_object< CaloRecGPU::CellStateArr > m_cell_state
Definition: DataHolders.h:75
CUDAFriendlyClasses.h
if
if(febId1==febId2)
Definition: LArRodBlockPhysicsV0.cxx:567
xAOD::CaloCluster_v1::setPhi
bool setPhi(const CaloSample sampling, const float phi)
Set in a given sampling. Returns false if the sample isn't part of the cluster.
Definition: CaloCluster_v1.cxx:549
CaloRecGPU::ConstantDataHolder
Definition: DataHolders.h:19
CaloClusterKineHelper::calculateKine
static void calculateKine(xAOD::CaloCluster *clu, const bool useweight=true, const bool updateLayers=true, const bool useGPUCriteria=false)
Helper class to calculate cluster kinematics based on cells.
Definition: CaloClusterKineHelper.cxx:223
str
Definition: BTagTrackIpAccessor.cxx:11
xAOD::CaloCluster_v1::setEta
bool setEta(const CaloSample sampling, const float eta)
Set in a given sampling. Returns false if the sample isn't part of the cluster.
Definition: CaloCluster_v1.cxx:534
CaloRecGPU
Definition: BaseDefinitions.h:11
CaloRecGPU::ClusterTag::secondary_cluster_weight
constexpr int32_t secondary_cluster_weight() const
Definition: TagDefinitions.h:263
DataVector::begin
const_iterator begin() const noexcept
Return a const_iterator pointing at the beginning of the collection.
xAOD::CaloCluster_v1::setRawPhi
void setRawPhi(flt_t)
Set for signal state UNCALIBRATED.
Definition: CaloCluster_v1.cxx:294