ATLAS Offline Software
Loading...
Searching...
No Matches
SimKernelMT.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
3*/
4
10
11#include "SimKernelMT.h"
12
13// ISF includes
19
20// STL
21#include <memory>
22#include <queue>
23#include <utility>
24
25#undef ISFDEBUG
26
28
29 ATH_CHECK( m_simulationTools.retrieve() );
30 for ( auto& curSimTool : m_simulationTools )
31 {
32 if ( curSimTool )
33 {
34 const auto flavor = curSimTool->simFlavor();
35 auto itr = m_simToolMap.find(flavor);
36 if (itr != m_simToolMap.end() )
37 {
38 ATH_MSG_FATAL("Two ISimulatorTool instances (" << itr->second->name() << "," << curSimTool->name() << ") with the same flavor in this job!\n Check your configuration!");
39 return StatusCode::FAILURE;
40 }
41 // New flavour add it to the map.
42 m_simToolMap[flavor] = &*curSimTool;
43 if (flavor == ISF::ParticleKiller) {
44 m_particleKillerTool = &*curSimTool;
45 }
46 }
47 }
48 // Check that a particle killer simulator tool was provided
50 ATH_MSG_FATAL("No fallback ParticleKiller Simulator Tool provided in SimulationTools, the job will bail out now.");
51 return StatusCode::FAILURE;
52 }
53
54 ATH_MSG_INFO("The following Simulators will be used in this job: \t" << m_simulationTools);
55 // retrieve simulation selectors (i.e. the "routing chain")
56 for ( auto& selectorsToolHandleArray: m_simSelectors )
57 {
58 ATH_CHECK( selectorsToolHandleArray.retrieve() );
59 }
60
61 // info screen output
62 ATH_MSG_INFO( "The following routing chains are defined:" );
64 {
65 auto& localSelectors = m_simSelectors[geoID];
66 ATH_MSG_INFO( AtlasDetDescr::AtlasRegionHelper::getName(geoID) << " (GeoID=" << geoID << "): \t" << localSelectors );
67
68 for ( auto& selector : localSelectors )
69 {
70 const auto flavor = selector->simFlavor();
71 auto itr = m_simToolMap.find(flavor);
72 if (itr == m_simToolMap.end() )
73 {
74 ATH_MSG_WARNING( "Map from SimulationFlavor to SimulatorTool:" );
75 for ( auto& entry : m_simToolMap )
76 {
77 ATH_MSG_WARNING( "SimulationFlavor: " << entry.first << ", SimulatorTool Name: " << entry.second->name() );
78 }
79 ATH_MSG_FATAL( "No SimulationTool with flavor " << flavor << " expected by " << selector->name() << " found in this job!\n Check your configuration!" );
80 return StatusCode::FAILURE;
81 }
82 }
83 }
84
85 ATH_CHECK( m_entryLayerTool.retrieve() );
86
87 if ( not m_orderingTool.empty() ) {
88 ATH_CHECK( m_orderingTool.retrieve() );
89 }
90
91 ATH_CHECK( m_inputEvgenKey.initialize() );
92 ATH_CHECK( m_outputTruthKey.initialize() );
93
94 ATH_CHECK( m_caloEntryLayerKey.initialize() );
95 ATH_CHECK( m_muonEntryLayerKey.initialize() );
96 ATH_CHECK( m_muonExitLayerKey.initialize() );
97
98 ATH_CHECK( m_inputConverter.retrieve() );
99 if ( not m_truthPreselectionTool.empty() ) {
101 }
102
103 ATH_CHECK ( m_truthRecordSvc.retrieve() );
104
105 if ( not m_qspatcher.empty() ) {
106 ATH_CHECK( m_qspatcher.retrieve() );
107 }
108
109 ATH_CHECK( m_geoIDSvc.retrieve() );
110
111 return StatusCode::SUCCESS;
112}
113
114
115StatusCode ISF::SimKernelMT::execute(const EventContext& ctx) {
116
117 auto hitCollections = std::make_shared<HitCollectionMap>();
118 // Call setupEvent for all simulators (TODO: make the tools do this)
119 for (auto& curSimTool: m_simulationTools) {
120 if ( curSimTool ) {
121 if (auto* g4sim =
122 dynamic_cast<ISF::BaseSimulatorG4Tool*>(curSimTool.get())) {
123 ATH_CHECK(g4sim->setupEvent(ctx, *hitCollections));
124 } else {
125 ATH_CHECK(curSimTool->setupEvent(ctx));
126 }
127 ATH_MSG_DEBUG( "Event setup done for " << curSimTool->name() );
128 }
129 }
130
132 if (!inputEvgen.isValid()) {
133 ATH_MSG_FATAL("Unable to read input GenEvent collection '" << inputEvgen.key() << "'");
134 return StatusCode::FAILURE;
135 }
136
137 // create output Truth collection
139 std::unique_ptr<McEventCollection> shadowTruth{};
140 if (m_useShadowEvent) {
141 outputTruth = std::make_unique<McEventCollection>();
142 // copy input Evgen collection to shadow Truth collection
143 shadowTruth = std::make_unique<McEventCollection>(*inputEvgen);
144 for (HepMC::GenEvent* currentGenEvent : *shadowTruth ) {
145 // Apply QS patch if required
146 if ( not m_qspatcher.empty() ) {
147 ATH_CHECK(m_qspatcher->applyWorkaround(*currentGenEvent));
148 }
149 // Copy GenEvent and remove daughters of quasi-stable particles to be simulated
150 std::unique_ptr<HepMC::GenEvent> outputEvent = m_truthPreselectionTool->filterGenEvent(*currentGenEvent);
151 outputTruth->push_back(outputEvent.release());
152 }
153 }
154 else {
155 // copy input Evgen collection to output Truth collection
156 outputTruth = std::make_unique<McEventCollection>(*inputEvgen);
157 // Apply QS patch if required
158 if ( not m_qspatcher.empty() ) {
159 for (HepMC::GenEvent* currentGenEvent : *outputTruth ) {
160 ATH_CHECK(m_qspatcher->applyWorkaround(*currentGenEvent));
161 }
162 }
163 }
164
165 const int largestGeneratedParticleBC = (outputTruth->empty()) ? HepMC::UNDEFINED_ID
166 : HepMC::maxGeneratedParticleBarcode(outputTruth->at(0)); // TODO make this more robust
167 const int largestGeneratedVertexBC = (outputTruth->empty()) ? HepMC::UNDEFINED_ID
168 : HepMC::maxGeneratedVertexBarcode(outputTruth->at(0)); // TODO make this more robust
169 // tell TruthService we're starting a new event
170 ATH_CHECK( m_truthRecordSvc->initializeTruthCollection(largestGeneratedParticleBC, largestGeneratedVertexBC) );
171
172 // Create TrackRecordCollections and pass them to the entryLayerTool
174 caloEntryLayer = std::make_unique<TrackRecordCollection>(caloEntryLayer.name());
175 ATH_CHECK(m_entryLayerTool->registerTrackRecordCollection(caloEntryLayer.ptr(), fAtlasCaloEntry));
177 muonEntryLayer = std::make_unique<TrackRecordCollection>(muonEntryLayer.name());
178 ATH_CHECK(m_entryLayerTool->registerTrackRecordCollection(muonEntryLayer.ptr(), fAtlasMuonEntry));
180 muonExitLayer = std::make_unique<TrackRecordCollection>(muonExitLayer.name());
181 ATH_CHECK(m_entryLayerTool->registerTrackRecordCollection(muonExitLayer.ptr(), fAtlasMuonExit));
182
183 // read and convert input
184 ISFParticleContainer simParticles;
185 ATH_CHECK( m_inputConverter->convert(*outputTruth, simParticles) );
186
187 // create an ordered queue of particles
188 ISFParticleOrderedQueue particleQueue;
189 for ( auto& particle : simParticles ) {
190
191 if ( m_orderingTool.empty() ) {
192 // Without a defined ordering, preserve old FIFO behaviour
193 particle->setOrder( -particleQueue.size() );
194 } else {
195 m_orderingTool->setOrder( *particle );
196 }
197
198 particleQueue.push( particle );
199 }
200
201 unsigned int loopCounter{0};
202 // loop until there are no more particles to simulate
203 ISF::ISFParticleVector particles{};
204 ISimulatorTool* lastSimulator{};
205 ISFParticleContainer newSecondaries{};
206 while ( particleQueue.size() ) {
207 ++loopCounter;
208 ATH_MSG_VERBOSE("Main Loop pass no. " << loopCounter);
209 ATH_MSG_VERBOSE("Queue starts with " << particleQueue.size() << " particles.");
210 // Create a vector of particles with the same simulator
211 ISFParticleOrderedQueue tempQueue;
212 while ( particleQueue.size() ) {
213 auto particlePtr = particleQueue.top();
214 ISFParticle& curParticle( *particlePtr );
215 particleQueue.pop();
216
217 // Get the geo ID for the particle
218 if ( m_forceGeoIDSvc || !validAtlasRegion( curParticle.nextGeoID() ) ) {
219 m_geoIDSvc->identifyAndRegNextGeoID( curParticle );
220 }
221
222 // Get the simulator using the GeoID
223 auto& simTool = identifySimulator(curParticle);
224
225 // Fill the vector
226 if ( particles.empty() ) {
227 // First particle in the vector defines the simulator
228 particles.push_back(particlePtr);
229 lastSimulator=&simTool;
230 }
231 else if (&simTool!=lastSimulator || particles.size() >= m_maxParticleVectorSize.value() ) {
232 // Change of simulator, end the current vector
233 tempQueue.push(particlePtr);
234 }
235 else {
236 particles.push_back(particlePtr);
237 }
238 }
239 particleQueue = std::move(tempQueue);
240 #ifdef ISFDEBUG
241 if (loopCounter>100 && particles.size()<3) {
242 ATH_MSG_INFO("Main Loop pass no. " << loopCounter);
243 ATH_MSG_INFO("Selected " << particles.size() << " particles to be processed by " << lastSimulator->name());
244 for ( const ISFParticle *particle : particles ) {
245 ATH_MSG_INFO(*particle);
246 }
247 }
248 #endif // ISFDEBUG
249
250 ATH_MSG_VERBOSE("Selected " << particles.size() << " particles to be processed by " << lastSimulator->name());
251 // Run the simulation
252 if (auto* g4sim = dynamic_cast<ISF::BaseSimulatorG4Tool*>(lastSimulator)) {
253 ATH_CHECK(g4sim->simulateVector(ctx, particles, newSecondaries,
254 outputTruth.ptr(), hitCollections,
255 shadowTruth.get()));
256 } else {
257 ATH_CHECK(lastSimulator->simulateVector(ctx, particles, newSecondaries,
258 outputTruth.ptr(),
259 shadowTruth.get()));
260 }
261
262 ATH_MSG_VERBOSE(lastSimulator->name() << " returned " << newSecondaries.size() << " new particles to be added to the queue." );
263 // Register returned particles with the entry layer tool, set their order and enqueue them
264 for ( auto* secondary : newSecondaries ) {
265
266 // Set up particle in ISF
267 m_entryLayerTool->registerParticle( *secondary );
268 if ( m_forceGeoIDSvc || !validAtlasRegion( secondary->nextGeoID() ) ) {
269 m_geoIDSvc->identifyAndRegNextGeoID( *secondary );
270 }
271
272 if ( m_orderingTool.empty() ) {
273 // Without a defined ordering, preserve old FIFO behaviour
274 secondary->setOrder( -particleQueue.size() );
275 } else {
276 m_orderingTool->setOrder( *secondary );
277 }
278
279 particleQueue.push( secondary );
280 }
281 newSecondaries.clear();
282
283 // Delete simulated particles
284 for ( auto usedParticle : particles ) {
285 delete usedParticle;
286 }
287 particles.clear();
288 }
289 ATH_MSG_VERBOSE("Final status: queue contains " << particleQueue.size() << " particles.");
290
291 // Release the event from all simulators (TODO: make the tools do this)
292 for (auto& curSimTool: m_simulationTools) {
293 if ( curSimTool ) {
294 if (auto* g4sim =
295 dynamic_cast<ISF::BaseSimulatorG4Tool*>(curSimTool.get())) {
296 ATH_CHECK(g4sim->releaseEvent(ctx, *hitCollections));
297 } else {
298 ATH_CHECK(curSimTool->releaseEvent(ctx));
299 }
300 ATH_MSG_DEBUG( "releaseEvent() completed for " << curSimTool->name() );
301 }
302 }
303
304 // Remove QS patch if required
305 if(!m_qspatcher.empty()) {
306 for (HepMC::GenEvent* currentGenEvent : *outputTruth ) {
307 ATH_CHECK(m_qspatcher->removeWorkaround(*currentGenEvent));
308 }
309 }
310
311 return StatusCode::SUCCESS;
312}
313
314
316 return StatusCode::SUCCESS;
317}
318
319
322 AtlasDetDescr::AtlasRegion geoID = particle.nextGeoID();
323
324 auto& localSelectors = m_simSelectors[geoID];
325 for (auto& selector: localSelectors) {
326 bool selected = selector->selfSelect(particle);
327 if (selected) {
328 return *m_simToolMap.at(selector->simFlavor());
329 }
330 }
331
332 ATH_MSG_WARNING("No simulator found for particle (" << particle << ")."
333 << " Will send it to " << m_particleKillerTool->name());
334 return *m_particleKillerTool;
335}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_FATAL(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
bool validAtlasRegion(AtlasDetDescr::AtlasRegion region)
Check a given AtlasRegion for its validity.
Definition AtlasRegion.h:40
static const char * getName(int region)
The generic ISF particle definition,.
Definition ISFParticle.h:42
AtlasDetDescr::AtlasRegion nextGeoID() const
next geoID the particle will be simulated in
virtual StatusCode simulateVector(const EventContext &ctx, const ISFParticleVector &particles, ISFParticleContainer &secondaries, McEventCollection *mcEventCollection, McEventCollection *shadowTruth=nullptr)=0
Simulation call for vectors of particles.
SG::WriteHandleKey< McEventCollection > m_outputTruthKey
Output Simulation Truth collection.
Definition SimKernelMT.h:91
ToolHandleArray< ISimulatorTool > m_simulationTools
Simulation Tools.
SG::ReadHandleKey< McEventCollection > m_inputEvgenKey
Input Generator Truth collection.
Definition SimKernelMT.h:89
BooleanProperty m_useShadowEvent
Definition SimKernelMT.h:97
SG::WriteHandleKey< TrackRecordCollection > m_muonEntryLayerKey
Definition SimKernelMT.h:94
std::map< ISF::SimulationFlavor, ISimulatorTool * > m_simToolMap
Map of the simulation flavours used in this job to the corresponding Simulation Services.
ToolHandle< IGenEventFilter > m_truthPreselectionTool
ToolHandle< IParticleOrderingTool > m_orderingTool
Tool to set particle ordering.
Gaudi::Property< size_t > m_maxParticleVectorSize
Number of particles simultaneously sent to simulator.
ISimulatorTool & identifySimulator(const ISF::ISFParticle &particle)
Returns the simulator to use for the given particle.
ServiceHandle< IGeoIDSvc > m_geoIDSvc
Service to set particle GeoIDs.
SG::WriteHandleKey< TrackRecordCollection > m_caloEntryLayerKey
Output TrackRecordCollections.
Definition SimKernelMT.h:93
ServiceHandle< Simulation::IZeroLifetimePatcher > m_qspatcher
Quasi-Stable Particle Simulation Patcher.
ServiceHandle< IInputConverter > m_inputConverter
Input converter service (from Generator->ISF particle types).
ISimulatorTool * m_particleKillerTool
When no appropriate simulator can be found for a given particle, the particle is sent to this "partic...
std::array< PublicToolHandleArray< ISimulationSelector >, AtlasDetDescr::fNumAtlasRegions > m_simSelectors
The simulation selectors defining the "routing chain".
SG::WriteHandleKey< TrackRecordCollection > m_muonExitLayerKey
Definition SimKernelMT.h:95
Gaudi::Property< bool > m_forceGeoIDSvc
Force geoID recalculation for each particle.
Definition SimKernelMT.h:99
PublicToolHandle< IEntryLayerTool > m_entryLayerTool
AthenaTool responsible for writing Calo/Muon Entry/Exit Layer collection.
StatusCode initialize() override
Check user configuration and configure the algorithm state accordingly.
StatusCode execute(const EventContext &ctx) override
Check the validity of the MC truth event in the current Athena event.
ServiceHandle< ITruthSvc > m_truthRecordSvc
Central truth service.
StatusCode finalize() override
Implements empty finalize (implementation required by AthAlgorithm).
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.
const std::string & name() const
Return the StoreGate ID for the referenced object.
pointer_type ptr()
Dereference the pointer.
AtlasRegion
A simple enum of ATLAS regions and sub-detectors.
Definition AtlasRegion.h:21
int maxGeneratedVertexBarcode(const HepMC::GenEvent *genEvent)
Get the maximal absolute value of barcode of vertex present in the event. Returns a negative number.
constexpr int UNDEFINED_ID
int maxGeneratedParticleBarcode(const HepMC::GenEvent *genEvent)
Get the maximal value of barcode of particle present in the event.
HepMC3::GenEvent GenEvent
Definition GenEvent.h:39
std::priority_queue< ISF::ISFParticle *, ISF::ISFParticleVector, ISF::ISFParticleOrdering > ISFParticleOrderedQueue
the actual particle priority_queue
@ fAtlasMuonExit
Definition EntryLayer.h:39
@ fAtlasCaloEntry
Definition EntryLayer.h:37
@ fAtlasMuonEntry
Definition EntryLayer.h:38
@ ParticleKiller
std::list< ISF::ISFParticle * > ISFParticleContainer
generic ISFParticle container (not necessarily a std::list!)
std::vector< ISF::ISFParticle * > ISFParticleVector
ISFParticle vector.