ATLAS Offline Software
Loading...
Searching...
No Matches
FPGATrackSimDataPrepAlg.cxx
Go to the documentation of this file.
1
2
3// Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
4
6
14
18
20
23
25
26#include "GaudiKernel/IEventProcessor.h"
27
28constexpr bool enableBenchmark =
29#ifdef BENCHMARK_FPGATRACKSIM
30 true;
31#else
32 false;
33#endif
34
36// Initialize
37
38FPGATrackSimDataPrepAlg::FPGATrackSimDataPrepAlg (const std::string& name, ISvcLocator* pSvcLocator) :
39 AthReentrantAlgorithm(name, pSvcLocator)
40{
41}
42
43
45{
46 std::stringstream ss(m_description);
47 std::string line;
48 ATH_MSG_INFO("Tag config:");
49 if (!m_description.empty()) {
50 while (std::getline(ss, line, '\n')) {
51 ATH_MSG_INFO('\t' << line);
52 }
53 }
54
55 // Dump the configuration to make sure it propagated through right
56 const std::vector<Gaudi::Details::PropertyBase*> props = this->getProperties();
57 for( Gaudi::Details::PropertyBase* prop : props ) {
58 if (prop->ownerTypeName()==this->type()) {
59 ATH_MSG_DEBUG("Property:\t" << prop->name() << "\t : \t" << prop->toString());
60 }
61 }
62
63
64 ATH_CHECK(m_hitSGInputTool.retrieve(EnableTool{!m_hitSGInputTool.empty()}));
65
66 ATH_CHECK(m_hitInputTool.retrieve(EnableTool{!m_hitInputTool.empty()}));
67 ATH_CHECK(m_hitInputTool2.retrieve(EnableTool{m_secondInputToolN > 0 && !m_hitInputTool2.empty()}));
68 ATH_CHECK(m_hitMapTools.retrieve());
69 ATH_CHECK(m_hitFilteringTool.retrieve(EnableTool{m_doHitFiltering}));
70 ATH_CHECK(m_clusteringTool.retrieve(EnableTool{m_clustering > 0}));
71
72 ATH_CHECK(m_writeOutputTool.retrieve());
74
75
76 ATH_MSG_DEBUG("initialize() Instantiating root objects");
78 m_logicEventHeader = m_writeOutputTool->addInputBranch(m_postClusterBranch.value(), true);
79
80 ATH_MSG_DEBUG("initialize() Setting branch");
81
82 if (!m_monTool.empty())
83 ATH_CHECK(m_monTool.retrieve());
84
85 ATH_CHECK( m_FPGAClusterKey.initialize() );
86 ATH_CHECK( m_FPGAHitKey.initialize() );
87 ATH_CHECK( m_FPGAHitUnmappedKey.initialize() );
89 ATH_CHECK( m_truthLinkContainerKey.initialize() );
90 ATH_CHECK( m_FPGATruthTrackKey.initialize() );
91 ATH_CHECK( m_FPGAOfflineTrackKey.initialize() );
92 ATH_CHECK( m_FPGAEventInfoKey.initialize() );
93
94 ATH_CHECK( m_chrono.retrieve() );
95 ATH_MSG_DEBUG("initialize() Finished");
96
97
98 return StatusCode::SUCCESS;
99}
100
101
103// MAIN EXECUTE ROUTINE //
105
106StatusCode FPGATrackSimDataPrepAlg::execute(const EventContext& ctx) const
107{
108 // Local event headers (thread-safe)
110 FPGATrackSimEventInputHeader firstInputHeader;
111
112 FPGATrackSimLogicalEventInputHeader logicEventHeader_precluster;
114
115 // Local storage for clusters and unmapped hits
117 std::vector<std::unique_ptr<FPGATrackSimHit>> hits_miss;
118
119 // Read inputs
120 bool done = false;
121 ATH_CHECK(readInputs(ctx, eventHeader, firstInputHeader, done));
122
123 if (done) {
124 SmartIF<IEventProcessor> appMgr{service("ApplicationMgr")};
125 if (!appMgr) {
126 ATH_MSG_ERROR("Failed to retrieve ApplicationMgr as IEventProcessor");
127 return StatusCode::FAILURE;
128 }
129 return appMgr->stopRun();
130 }
131
133 ATH_CHECK( FPGAHits.record (std::make_unique<FPGATrackSimHitCollection>()));
134
136 ATH_CHECK( FPGAHitUnmapped.record (std::make_unique<FPGATrackSimHitCollection>()));
137
139 ATH_CHECK( FPGAClusters.record (std::make_unique<FPGATrackSimClusterCollection>()));
140
142 ATH_CHECK(truthLinkVec.record(std::make_unique<xAODTruthParticleLinkVector>()));
143
145 ATH_CHECK(FPGATruthTracks.record(std::make_unique<FPGATrackSimTruthTrackCollection>()));
146
148 ATH_CHECK(FPGAOfflineTracks.record(std::make_unique<FPGATrackSimOfflineTrackCollection>()));
149 // Apply event selection based on truth tracks
150 if (m_doEvtSel) {
151 bool acceptEvent = false;
152 if constexpr (enableBenchmark) m_chrono->chronoStart("DataPrep: EventSelection");
153 for (auto eventSelector : m_eventSelectionTools)
154 {
155 if (eventSelector->selectEvent(eventHeader)) {
156 ATH_MSG_DEBUG("Event accepted by: " << eventSelector->name());
157 acceptEvent = true;
158 if ((m_writeRegion>=0)&&(m_writeRegion==eventSelector->getRegionID())) {
159 m_writeOutputTool->activateEventOutput();
160 }
161 }
162 }
163 if constexpr (enableBenchmark) m_chrono->chronoStop("DataPrep: EventSelection");
164 if (m_useInternalTruthTracks && acceptEvent) {
165 if constexpr (enableBenchmark) m_chrono->chronoStart("DataPrep: TruthMatching");
166 SG::ReadHandle<xAOD::TruthParticleContainer> truthParticleContainer(m_inputTruthParticleContainerKey, ctx); // Read offline TruthParticles
167 if (!truthParticleContainer.isValid()) {
168 ATH_MSG_ERROR("No valid truth particle container with key " << truthParticleContainer.key());
169 return StatusCode::FAILURE;
170 }
171 ATH_MSG_DEBUG("making mp of truth particles");
172 std::unordered_map<HepMcParticleLink::barcode_type, std::pair<const xAOD::TruthParticle*, size_t>> truthParticlesMap;
173 size_t truthParticleIndex = 0;
174 for (const xAOD::TruthParticle* truthParticle : *truthParticleContainer) {
175 truthParticlesMap.insert(std::make_pair(HepMC::uniqueID(truthParticle), std::make_pair(truthParticle,truthParticleIndex)));
176 truthParticleIndex++;
177 }
178 const FPGATrackSimTruthTrackCollection& fpgaTruthTracks = eventHeader.optional().getTruthTracks();
179 truthLinkVec->reserve(fpgaTruthTracks.size());
180 ATH_MSG_DEBUG("begin truth matching for " << fpgaTruthTracks.size() << " FPGA truth tracks");
181 for (const FPGATrackSimTruthTrack& fpgaTruthTrack : fpgaTruthTracks) {
182 auto it = truthParticlesMap.find(fpgaTruthTrack.getUniqueID()); // TODO FIXME need to check FPGATrackSimTruthTrack uniqueIDs are properly filled
183 if (it != truthParticlesMap.end()) {
184 ElementLink<xAOD::TruthParticleContainer> truthParticleLink(*truthParticleContainer, it->second.second);
185 // TODO: check if we can avoid using the previously-created map and look directly for the unique ID in the link vector container
186 truthLinkVec->push_back(new xAODTruthParticleLink(HepMcParticleLink(HepMC::uniqueID(it->second.first), 0,
188 ATH_MSG_DEBUG("Truth link added");
189 }
190 }
191 if (truthLinkVec->empty()) {
192 ATH_MSG_DEBUG("No truth particles selected. Skipping event...");
193 return StatusCode::SUCCESS;
194 }
195 std::stable_sort(truthLinkVec->begin(), truthLinkVec->end(), SortTruthParticleLink());
196 if constexpr (enableBenchmark) m_chrono->chronoStop("DataPrep: TruthMatching");
197 }
198 }
199 else {
200 ATH_MSG_DEBUG("No Event Selection applied");
201 for (auto eventSelector : m_eventSelectionTools) {
202 eventSelector->setSelectedEvent(true);
203 }
204 }
205
206 // Event passes cuts, count it
207 m_evt++;
208
209 // Map, cluster, and filter hits
210 ATH_CHECK(processInputs(eventHeader, logicEventHeader, logicEventHeader_precluster,
211 hits_miss, clusters, FPGAHitUnmapped, FPGAClusters));
212
213 if constexpr (enableBenchmark) m_chrono->chronoStart("DataPrep: get truth/offline tracks");
214 for (const auto& truthtrack : logicEventHeader.optional().getTruthTracks()) {
215 FPGATruthTracks->push_back(truthtrack);
216 }
217
218 // Need to do the same for offline tracks.
219 for (const auto& offlineTrack : logicEventHeader.optional().getOfflineTracks()) {
220 FPGAOfflineTracks->push_back(offlineTrack);
221 }
222
223 if constexpr (enableBenchmark) m_chrono->chronoStop("DataPrep: get truth/offline tracks");
224
225 // Get reference to hits
226
227 // Recording Data
228 for (auto eventSelector : m_eventSelectionTools)
229 {
230 // Get reference to hits
231 const unsigned& regionID = eventSelector->getRegionID();
232
233 auto mon_regionID = Monitored::Scalar<unsigned>("regionID", regionID);
234 Monitored::Group(m_monTool, mon_regionID);
235 }
236
237 std::vector<FPGATrackSimHit> const& hits = logicEventHeader.towers().at(0).hits();
238 if (m_recordHits) {
239 if constexpr (enableBenchmark) m_chrono->chronoStart("DataPrep: record hits");
240 // If and when we set up code to run over more than one region/tower at a time this will need to be updated
241 FPGAHits->reserve(hits.size());
242 for (const auto& hit : hits) {
243 if (hit.isReal()) FPGAHits->push_back(new FPGATrackSimHit(hit));
244 }
245 if constexpr (enableBenchmark) m_chrono->chronoStop("DataPrep: record hits");
246 }
247
248 auto mon_nhits = Monitored::Scalar<unsigned>("nHits", hits.size());
249 auto mon_nhits_unmapped = Monitored::Scalar<unsigned>("nHits_unmapped", hits_miss.size());
250 Monitored::Group(m_monTool, mon_nhits, mon_nhits_unmapped);
251
253 // Put the FPGATrackSim event info on storegate so later algorithms can access it easily.
255 ATH_CHECK(FPGAEventInfo.record(std::make_unique<FPGATrackSimEventInfo>(eventHeader.event())));
256 }
257
258 // Write the output and reset
259 if (m_writeOutputData) {
260 // Lock and transfer to ROOT-managed pointers
261 std::lock_guard<std::mutex> lock(m_rootWriteMutex);
262
263 *m_logicEventHeader = std::move(logicEventHeader);
265 *m_logicEventHeader_precluster = std::move(logicEventHeader_precluster);
266 }
267
268 ATH_CHECK(m_writeOutputTool->writeData());
269 }
270
271 return StatusCode::SUCCESS;
272}
273
274
276// INPUT PASSING, READING AND PROCESSING //
278
280 const EventContext& ctx,
281 FPGATrackSimEventInputHeader& eventHeader,
282 FPGATrackSimEventInputHeader& firstInputHeader,
283 bool& done) const
284{
285
286 if (!m_hitSGInputTool.empty()) {
287 ATH_CHECK(m_hitSGInputTool->readData(&eventHeader, ctx));
288 ATH_MSG_DEBUG("Loaded " << eventHeader.nHits() << " hits in event header from SG");
289 return StatusCode::SUCCESS;
290 }
291
292 if (m_ev % m_firstInputToolN == 0)
293 {
294 // Read primary input
295 ATH_CHECK(m_hitInputTool->readData(&firstInputHeader, done));
296 if (done)
297 {
298 ATH_MSG_DEBUG("Cannot read more events from file, returning");
299 return StatusCode::SUCCESS;
300 }
301 }
302
303 eventHeader = firstInputHeader;
304
305 // Read secondary input
306 for (int i = 0; i < m_secondInputToolN; i++)
307 {
308 ATH_CHECK(m_hitInputTool2->readData(&eventHeader, done, false));
309 if (done)
310 {
311 ATH_MSG_INFO("Cannot read more events from file, returning");
312 return StatusCode::SUCCESS;
313 }
314 }
315
316 m_ev++;
317
318 return StatusCode::SUCCESS;
319}
320
321
322// Applies clustering, mapping, hit filtering, and space points
324 const FPGATrackSimEventInputHeader& eventHeader,
325 FPGATrackSimLogicalEventInputHeader& logicEventHeader,
326 FPGATrackSimLogicalEventInputHeader& logicEventHeader_precluster,
327 std::vector<std::unique_ptr<FPGATrackSimHit>>& hits_miss,
331{
332 // Map hits
333 ATH_MSG_DEBUG("Running hits conversion");
334 logicEventHeader.reset();
335 logicEventHeader_precluster.reset();
336
337 if constexpr (enableBenchmark) m_chrono->chronoStart("DataPrep: RawToLogical");
338 for (auto hitMapTool : m_hitMapTools){
339 ATH_CHECK(hitMapTool->convert(1, eventHeader, logicEventHeader));
340 }
341 if constexpr (enableBenchmark) m_chrono->chronoStop("DataPrep: RawToLogical");
342
343
344 for (auto& hit : hits_miss) FPGAHitUnmapped->push_back(std::move(hit));
345 hits_miss.clear();
346
347
348 ATH_MSG_DEBUG("Hits conversion done, #unmapped hists = " << FPGAHitUnmapped->size());
349
350 // At this stage, copy the logicEventHeader.
352 logicEventHeader_precluster = logicEventHeader;
353 }
354
355 if constexpr (enableBenchmark) m_chrono->chronoStart("DataPrep: Clustering");
356 // Clustering
357 for (int ic = 0; ic < m_clustering; ic++) {
358 ATH_MSG_DEBUG("Running clustering");
359 ATH_CHECK(m_clusteringTool->DoClustering(logicEventHeader, clusters));
360 // I think I also want to pass clusters to random removal (but won't work currently)
361 if (m_doHitFiltering) ATH_CHECK(m_hitFilteringTool->DoRandomRemoval(logicEventHeader, false));
362 unsigned npix(0), nstrip(0);
363 for (const FPGATrackSimCluster& cluster : clusters) {
364 if (cluster.getClusterEquiv().isPixel()) npix++;
365 else nstrip++;
366 }
367
368 m_nPixClusters += npix;
369 m_nStripClusters += nstrip;
370
371
372 // The following is hopefully a thread-safe approach to update nMaximum clusters
373 // using compare-exchange loop to prevent race conditions.
374 // Logic: Without atomics, two threads could both read e.g. max=100, calculate new values (150, 200),
375 // and the last write would win, potentially losing the true maximum (200 vs 150).
376 // compare_exchange_weak atomically checks if the value is unchanged before updating,
377 // and retries if another thread modified it concurrently, ensuring correctness.
378
379 // max update for m_nMaxPixClusters
380 unsigned current_max_pix = m_nMaxPixClusters;
381 while (npix > current_max_pix &&
382 !m_nMaxPixClusters.compare_exchange_weak(current_max_pix, npix));
383
384 // max update for m_nMaxStripClusters
385 unsigned current_max_strip = m_nMaxStripClusters;
386 while (nstrip > current_max_strip &&
387 !m_nMaxStripClusters.compare_exchange_weak(current_max_strip, nstrip));
388
389 // max update for m_nMaxClusters
390 unsigned current_max_clusters = m_nMaxClusters;
391 unsigned clusters_size = clusters.size();
392 while (clusters_size > current_max_clusters &&
393 !m_nMaxClusters.compare_exchange_weak(current_max_clusters, clusters_size));
394 }
395 // Record clusters
396 FPGAClusters->insert(
397 FPGAClusters->end(),
398 std::make_move_iterator(clusters.begin()),
399 std::make_move_iterator(clusters.end()));
400
401 if constexpr (enableBenchmark) m_chrono->chronoStop("DataPrep: Clustering");
402
403 return StatusCode::SUCCESS;
404}
405
407// Finalize
408
410{
411 ATH_MSG_INFO("PRINTING FPGATRACKSIM SIMPLE DATAPREP STATS");
412 ATH_MSG_INFO("========================================================================================");
413 ATH_MSG_INFO("Number of pixel clusters/event = " << m_nPixClusters/m_evt);
414 ATH_MSG_INFO("Number of strip clusters/event = " << m_nStripClusters/m_evt);
415 ATH_MSG_INFO("Max number of pixel clusters in an event = " << m_nMaxPixClusters);
416 ATH_MSG_INFO("Max number of strip clusters in an event = " << m_nMaxStripClusters);
417 ATH_MSG_INFO("Max number of clusters in an event = " << m_nMaxClusters);
418
419 return StatusCode::SUCCESS;
420}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_DEBUG(x)
std::vector< FPGATrackSimCluster > FPGATrackSimClusterCollection
: FPGATrackSim-specific class to represent an hit in the detector.
Utilize NN score to build track candidates.
Overlap removal tool for FPGATrackSimTrack.
Maps ITK module indices to FPGATrackSim regions.
Stores slice definitions for FPGATrackSim regions.
Defines a class for roads.
Structs that store the 5 track parameters.
std::vector< FPGATrackSimTruthTrack > FPGATrackSimTruthTrackCollection
static Double_t ss
An algorithm that can be simultaneously executed in multiple threads.
ToolHandleArray< FPGATrackSimRawToLogicalHitsTool > m_hitMapTools
SG::WriteHandleKey< FPGATrackSimOfflineTrackCollection > m_FPGAOfflineTrackKey
virtual StatusCode execute(const EventContext &ctx) const override final
SG::WriteHandleKey< FPGATrackSimHitCollection > m_FPGAHitUnmappedKey
Gaudi::Property< std::string > m_preClusterBranch
Gaudi::Property< bool > m_isDataPrepPipeline
Gaudi::Property< std::string > m_postClusterBranch
virtual StatusCode finalize() override
Gaudi::Property< bool > m_doEvtSel
SG::WriteHandleKey< FPGATrackSimTruthTrackCollection > m_FPGATruthTrackKey
FPGATrackSimLogicalEventInputHeader * m_logicEventHeader_precluster
ToolHandle< IFPGATrackSimInputTool > m_hitSGInputTool
Gaudi::Property< int > m_firstInputToolN
ToolHandle< FPGATrackSimOutputHeaderTool > m_writeOutputTool
StatusCode processInputs(const FPGATrackSimEventInputHeader &eventHeader, FPGATrackSimLogicalEventInputHeader &logicEventHeader, FPGATrackSimLogicalEventInputHeader &logicEventHeader_precluster, std::vector< std::unique_ptr< FPGATrackSimHit > > &hits_miss, FPGATrackSimClusterCollection &clusters, SG::WriteHandle< FPGATrackSimHitCollection > &FPGAHitUnmapped, SG::WriteHandle< FPGATrackSimClusterCollection > &FPGAClusters) const
virtual StatusCode initialize() override
Gaudi::Property< int > m_secondInputToolN
std::atomic< unsigned > m_nMaxStripClusters
ToolHandleArray< FPGATrackSim::FPGATrackSimEventSelectionTool > m_eventSelectionTools
std::atomic< unsigned > m_nMaxClusters
ToolHandle< FPGATrackSimReadRawRandomHitsTool > m_hitInputTool2
Gaudi::Property< bool > m_writePreClusterBranch
SG::ReadHandleKey< xAOD::TruthParticleContainer > m_inputTruthParticleContainerKey
ToolHandle< GenericMonitoringTool > m_monTool
Gaudi::Property< bool > m_useInternalTruthTracks
std::atomic< unsigned long > m_nPixClusters
ToolHandle< IFPGATrackSimEventInputHeaderTool > m_hitInputTool
FPGATrackSimLogicalEventInputHeader * m_logicEventHeader
std::atomic< unsigned > m_nMaxPixClusters
Gaudi::Property< int > m_writeRegion
SG::WriteHandleKey< FPGATrackSimHitCollection > m_FPGAHitKey
Gaudi::Property< int > m_clustering
std::atomic< unsigned long > m_nStripClusters
SG::WriteHandleKey< FPGATrackSimEventInfo > m_FPGAEventInfoKey
Gaudi::Property< bool > m_writeOutputData
SG::WriteHandleKeyArray< FPGATrackSimClusterCollection > m_FPGAClusterKey
ServiceHandle< IChronoStatSvc > m_chrono
ToolHandle< FPGATrackSimClusteringToolI > m_clusteringTool
FPGATrackSimDataPrepAlg(const std::string &name, ISvcLocator *pSvcLocator)
Gaudi::Property< bool > m_doHitFiltering
Gaudi::Property< bool > m_recordHits
std::atomic< unsigned > m_ev
ToolHandle< IFPGATrackSimHitFilteringTool > m_hitFilteringTool
SG::WriteHandleKey< xAODTruthParticleLinkVector > m_truthLinkContainerKey
StatusCode readInputs(const EventContext &ctx, FPGATrackSimEventInputHeader &eventHeader, FPGATrackSimEventInputHeader &firstInputHeader, bool &done) const
FPGATrackSimEventInfo const & event() const
FPGATrackSimOptionalEventInfo const & optional() const
const std::vector< FPGATrackSimTowerInputHeader > & towers() const
FPGATrackSimOptionalEventInfo const & optional() const
const std::vector< FPGATrackSimTruthTrack > & getTruthTracks() const
const std::vector< FPGATrackSimOfflineTrack > & getOfflineTracks() const
Group of local monitoring quantities and retain correlation when filling histograms
Declare a monitored scalar variable.
virtual bool isValid() override final
Can the handle be successfully dereferenced?
virtual const std::string & key() const override final
Return the StoreGate ID for the referenced object.
StatusCode record(std::unique_ptr< T > data)
Record a const object to the store.
int uniqueID(const T &p)
void stable_sort(DataModel_detail::iterator< DVL > beg, DataModel_detail::iterator< DVL > end)
Specialization of stable_sort for DataVector/List.
TruthParticle_v1 TruthParticle
Typedef to implementation.
constexpr bool enableBenchmark