ATLAS Offline Software
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 
11 #include "SimKernelMT.h"
12 
13 // ISF includes
16 #include "ISF_Event/ISFParticle.h"
19 
20 // STL
21 #include <memory>
22 #include <queue>
23 #include <utility>
24 
25 #undef ISFDEBUG
26 
27 ISF::SimKernelMT::SimKernelMT( const std::string& name, ISvcLocator* pSvcLocator ) :
28  ::AthAlgorithm( name, pSvcLocator ),
29  m_simSelectors() //FIXME make private
30 {
31  // routing tools
32  declareProperty("BeamPipeSimulationSelectors", m_simSelectors[AtlasDetDescr::fAtlasForward] );
33  declareProperty("IDSimulationSelectors", m_simSelectors[AtlasDetDescr::fAtlasID] );
34  declareProperty("CaloSimulationSelectors", m_simSelectors[AtlasDetDescr::fAtlasCalo] );
35  declareProperty("MSSimulationSelectors", m_simSelectors[AtlasDetDescr::fAtlasMS] );
36  declareProperty("CavernSimulationSelectors", m_simSelectors[AtlasDetDescr::fAtlasCavern] );
37  // tuning parameters
38  declareProperty("MaximumParticleVectorSize" , m_maxParticleVectorSize );
39 }
40 
41 
43 }
44 
45 
47 
48  ATH_CHECK( m_simulationTools.retrieve() );
49  for ( auto& curSimTool : m_simulationTools )
50  {
51  if ( curSimTool )
52  {
53  const auto flavor = curSimTool->simFlavor();
54  auto itr = m_simToolMap.find(flavor);
55  if (itr != m_simToolMap.end() )
56  {
57  ATH_MSG_FATAL("Two ISimulatorTool instances (" << itr->second->name() << "," << curSimTool->name() << ") with the same flavor in this job!\n Check your configuration!");
58  return StatusCode::FAILURE;
59  }
60  // New flavour add it to the map.
61  m_simToolMap[flavor] = &*curSimTool;
62  if (flavor == ISF::ParticleKiller) {
63  m_particleKillerTool = &*curSimTool;
64  }
65  }
66  }
67  // Check that a particle killer simulator tool was provided
68  if (!m_particleKillerTool) {
69  ATH_MSG_FATAL("No fallback ParticleKiller Simulator Tool provided in SimulationTools, the job will bail out now.");
70  return StatusCode::FAILURE;
71  }
72 
73  ATH_MSG_INFO("The following Simulators will be used in this job: \t" << m_simulationTools);
74  // retrieve simulation selectors (i.e. the "routing chain")
75  for ( auto& selectorsToolHandleArray: m_simSelectors )
76  {
77  ATH_CHECK( selectorsToolHandleArray.retrieve() );
78  }
79 
80  // info screen output
81  ATH_MSG_INFO( "The following routing chains are defined:" );
82  for ( short geoID=AtlasDetDescr::fFirstAtlasRegion; geoID<AtlasDetDescr::fNumAtlasRegions; ++geoID )
83  {
84  auto& localSelectors = m_simSelectors[geoID];
85  ATH_MSG_INFO( AtlasDetDescr::AtlasRegionHelper::getName(geoID) << " (GeoID=" << geoID << "): \t" << localSelectors );
86 
87  for ( auto& selector : localSelectors )
88  {
89  const auto flavor = selector->simFlavor();
90  auto itr = m_simToolMap.find(flavor);
91  if (itr == m_simToolMap.end() )
92  {
93  ATH_MSG_WARNING( "Map from SimulationFlavor to SimulatorTool:" );
94  for ( auto& entry : m_simToolMap )
95  {
96  ATH_MSG_WARNING( "SimulationFlavor: " << entry.first << ", SimulatorTool Name: " << entry.second->name() );
97  }
98  ATH_MSG_FATAL( "No SimulationTool with flavor " << flavor << " expected by " << selector->name() << " found in this job!\n Check your configuration!" );
99  return StatusCode::FAILURE;
100  }
101  }
102  }
103 
104  ATH_CHECK( m_entryLayerTool.retrieve() );
105 
106  if ( not m_orderingTool.empty() ) {
107  ATH_CHECK( m_orderingTool.retrieve() );
108  }
109 
110  ATH_CHECK( m_inputEvgenKey.initialize() );
111  ATH_CHECK( m_outputTruthKey.initialize() );
112 
113  ATH_CHECK( m_caloEntryLayerKey.initialize() );
114  ATH_CHECK( m_muonEntryLayerKey.initialize() );
115  ATH_CHECK( m_muonExitLayerKey.initialize() );
116 
117  ATH_CHECK( m_inputConverter.retrieve() );
118  if ( not m_truthPreselectionTool.empty() ) {
119  ATH_CHECK(m_truthPreselectionTool.retrieve());
120  }
121 
122  ATH_CHECK ( m_truthRecordSvc.retrieve() );
123 
124  if ( not m_qspatcher.empty() ) {
125  ATH_CHECK( m_qspatcher.retrieve() );
126  }
127 
128  ATH_CHECK( m_geoIDSvc.retrieve() );
129 
130  return StatusCode::SUCCESS;
131 }
132 
133 
135 
136  const EventContext& ctx = Gaudi::Hive::currentContext();
137  auto hitCollections = std::make_shared<HitCollectionMap>();
138  // Call setupEvent for all simulators (TODO: make the tools do this)
139  for (auto& curSimTool: m_simulationTools) {
140  if ( curSimTool ) {
141  if (auto* g4sim =
142  dynamic_cast<ISF::BaseSimulatorG4Tool*>(curSimTool.get())) {
143  ATH_CHECK(g4sim->setupEvent(ctx, *hitCollections));
144  } else {
145  ATH_CHECK(curSimTool->setupEvent(ctx));
146  }
147  ATH_MSG_DEBUG( "Event setup done for " << curSimTool->name() );
148  }
149  }
150 
151  SG::ReadHandle<McEventCollection> inputEvgen(m_inputEvgenKey);
152  if (!inputEvgen.isValid()) {
153  ATH_MSG_FATAL("Unable to read input GenEvent collection '" << inputEvgen.key() << "'");
154  return StatusCode::FAILURE;
155  }
156 
157  // create output Truth collection
158  SG::WriteHandle<McEventCollection> outputTruth(m_outputTruthKey, ctx);
159  std::unique_ptr<McEventCollection> shadowTruth{};
160  if (m_useShadowEvent) {
161  outputTruth = std::make_unique<McEventCollection>();
162  // copy input Evgen collection to shadow Truth collection
163  shadowTruth = std::make_unique<McEventCollection>(*inputEvgen);
164  for (HepMC::GenEvent* currentGenEvent : *shadowTruth ) {
165  // Apply QS patch if required
166  if ( not m_qspatcher.empty() ) {
167  ATH_CHECK(m_qspatcher->applyWorkaround(*currentGenEvent));
168  }
169  // Copy GenEvent and remove daughters of quasi-stable particles to be simulated
170  std::unique_ptr<HepMC::GenEvent> outputEvent = m_truthPreselectionTool->filterGenEvent(*currentGenEvent);
171  outputTruth->push_back(outputEvent.release());
172  }
173  }
174  else {
175  // copy input Evgen collection to output Truth collection
176  outputTruth = std::make_unique<McEventCollection>(*inputEvgen);
177  // Apply QS patch if required
178  if ( not m_qspatcher.empty() ) {
179  for (HepMC::GenEvent* currentGenEvent : *outputTruth ) {
180  ATH_CHECK(m_qspatcher->applyWorkaround(*currentGenEvent));
181  }
182  }
183  }
184 
185  const int largestGeneratedParticleBC = (outputTruth->empty()) ? HepMC::UNDEFINED_ID
186  : HepMC::maxGeneratedParticleBarcode(outputTruth->at(0)); // TODO make this more robust
187  const int largestGeneratedVertexBC = (outputTruth->empty()) ? HepMC::UNDEFINED_ID
188  : HepMC::maxGeneratedVertexBarcode(outputTruth->at(0)); // TODO make this more robust
189  // tell TruthService we're starting a new event
190  ATH_CHECK( m_truthRecordSvc->initializeTruthCollection(largestGeneratedParticleBC, largestGeneratedVertexBC) );
191 
192  // Create TrackRecordCollections and pass them to the entryLayerTool
193  SG::WriteHandle<TrackRecordCollection> caloEntryLayer(m_caloEntryLayerKey, ctx);
194  caloEntryLayer = std::make_unique<TrackRecordCollection>(caloEntryLayer.name());
195  ATH_CHECK(m_entryLayerTool->registerTrackRecordCollection(caloEntryLayer.ptr(), fAtlasCaloEntry));
196  SG::WriteHandle<TrackRecordCollection> muonEntryLayer(m_muonEntryLayerKey, ctx);
197  muonEntryLayer = std::make_unique<TrackRecordCollection>(muonEntryLayer.name());
198  ATH_CHECK(m_entryLayerTool->registerTrackRecordCollection(muonEntryLayer.ptr(), fAtlasMuonEntry));
199  SG::WriteHandle<TrackRecordCollection> muonExitLayer(m_muonExitLayerKey, ctx);
200  muonExitLayer = std::make_unique<TrackRecordCollection>(muonExitLayer.name());
201  ATH_CHECK(m_entryLayerTool->registerTrackRecordCollection(muonExitLayer.ptr(), fAtlasMuonExit));
202 
203  // read and convert input
204  ISFParticleContainer simParticles;
205  ATH_CHECK( m_inputConverter->convert(*outputTruth, simParticles) );
206 
207  // create an ordered queue of particles
208  ISFParticleOrderedQueue particleQueue;
209  for ( auto& particle : simParticles ) {
210 
211  if ( m_orderingTool.empty() ) {
212  // Without a defined ordering, preserve old FIFO behaviour
213  particle->setOrder( -particleQueue.size() );
214  } else {
215  m_orderingTool->setOrder( *particle );
216  }
217 
218  particleQueue.push( particle );
219  }
220 
221  unsigned int loopCounter{0};
222  // loop until there are no more particles to simulate
224  ISimulatorTool* lastSimulator{};
225  ISFParticleContainer newSecondaries{};
226  while ( particleQueue.size() ) {
227  ++loopCounter;
228  ATH_MSG_VERBOSE("Main Loop pass no. " << loopCounter);
229  ATH_MSG_VERBOSE("Queue starts with " << particleQueue.size() << " particles.");
230  // Create a vector of particles with the same simulator
231  ISFParticleOrderedQueue tempQueue;
232  while ( particleQueue.size() ) {
233  auto particlePtr = particleQueue.top();
234  ISFParticle& curParticle( *particlePtr );
235  particleQueue.pop();
236 
237  // Get the geo ID for the particle
238  if ( m_forceGeoIDSvc || !validAtlasRegion( curParticle.nextGeoID() ) ) {
239  m_geoIDSvc->identifyAndRegNextGeoID( curParticle );
240  }
241 
242  // Get the simulator using the GeoID
243  auto& simTool = identifySimulator(curParticle);
244 
245  // Fill the vector
246  if ( particles.empty() ) {
247  // First particle in the vector defines the simulator
248  particles.push_back(particlePtr);
249  lastSimulator=&simTool;
250  }
251  else if (&simTool!=lastSimulator || particles.size() >= m_maxParticleVectorSize ) {
252  // Change of simulator, end the current vector
253  tempQueue.push(particlePtr);
254  }
255  else {
256  particles.push_back(particlePtr);
257  }
258  }
259  particleQueue = std::move(tempQueue);
260  #ifdef ISFDEBUG
261  if (loopCounter>100 && particles.size()<3) {
262  ATH_MSG_INFO("Main Loop pass no. " << loopCounter);
263  ATH_MSG_INFO("Selected " << particles.size() << " particles to be processed by " << lastSimulator->name());
264  for ( const ISFParticle *particle : particles ) {
266  }
267  }
268  #endif // ISFDEBUG
269 
270  ATH_MSG_VERBOSE("Selected " << particles.size() << " particles to be processed by " << lastSimulator->name());
271  // Run the simulation
272  if (auto* g4sim = dynamic_cast<ISF::BaseSimulatorG4Tool*>(lastSimulator)) {
273  ATH_CHECK(g4sim->simulateVector(ctx, particles, newSecondaries,
274  outputTruth.ptr(), hitCollections,
275  shadowTruth.get()));
276  } else {
277  ATH_CHECK(lastSimulator->simulateVector(ctx, particles, newSecondaries,
278  outputTruth.ptr(),
279  shadowTruth.get()));
280  }
281 
282  ATH_MSG_VERBOSE(lastSimulator->name() << " returned " << newSecondaries.size() << " new particles to be added to the queue." );
283  // Register returned particles with the entry layer tool, set their order and enqueue them
284  for ( auto* secondary : newSecondaries ) {
285 
286  // Set up particle in ISF
287  m_entryLayerTool->registerParticle( *secondary );
288  if ( m_forceGeoIDSvc || !validAtlasRegion( secondary->nextGeoID() ) ) {
289  m_geoIDSvc->identifyAndRegNextGeoID( *secondary );
290  }
291 
292  if ( m_orderingTool.empty() ) {
293  // Without a defined ordering, preserve old FIFO behaviour
294  secondary->setOrder( -particleQueue.size() );
295  } else {
296  m_orderingTool->setOrder( *secondary );
297  }
298 
299  particleQueue.push( secondary );
300  }
301  newSecondaries.clear();
302 
303  // Delete simulated particles
304  for ( auto usedParticle : particles ) {
305  delete usedParticle;
306  }
307  particles.clear();
308  }
309  ATH_MSG_VERBOSE("Final status: queue contains " << particleQueue.size() << " particles.");
310 
311  // Release the event from all simulators (TODO: make the tools do this)
312  for (auto& curSimTool: m_simulationTools) {
313  if ( curSimTool ) {
314  if (auto* g4sim =
315  dynamic_cast<ISF::BaseSimulatorG4Tool*>(curSimTool.get())) {
316  ATH_CHECK(g4sim->releaseEvent(ctx, *hitCollections));
317  } else {
318  ATH_CHECK(curSimTool->releaseEvent(ctx));
319  }
320  ATH_MSG_DEBUG( "releaseEvent() completed for " << curSimTool->name() );
321  }
322  }
323 
324  // Remove QS patch if required
325  if(!m_qspatcher.empty()) {
326  for (HepMC::GenEvent* currentGenEvent : *outputTruth ) {
327  ATH_CHECK(m_qspatcher->removeWorkaround(*currentGenEvent));
328  }
329  }
330 
331  return StatusCode::SUCCESS;
332 }
333 
334 
336  return StatusCode::SUCCESS;
337 }
338 
339 
342  AtlasDetDescr::AtlasRegion geoID = particle.nextGeoID();
343 
344  auto& localSelectors = m_simSelectors[geoID];
345  for (auto& selector: localSelectors) {
346  bool selected = selector->selfSelect(particle);
347  if (selected) {
348  return *m_simToolMap.at(selector->simFlavor());
349  }
350  }
351 
352  ATH_MSG_WARNING("No simulator found for particle (" << particle << ")."
353  << " Will send it to " << m_particleKillerTool->name());
354  return *m_particleKillerTool;
355 }
ISFParticleOrderedQueue.h
ISF::ISFParticleContainer
std::list< ISF::ISFParticle * > ISFParticleContainer
generic ISFParticle container (not necessarily a std::list!)
Definition: ISFParticleContainer.h:23
AtlasDetDescr::fNumAtlasRegions
@ fNumAtlasRegions
Definition: AtlasRegion.h:33
ISF::fAtlasMuonEntry
@ fAtlasMuonEntry
Definition: EntryLayer.h:38
ATH_MSG_FATAL
#define ATH_MSG_FATAL(x)
Definition: AthMsgStreamMacros.h:34
Trk::ParticleSwitcher::particle
constexpr ParticleHypothesis particle[PARTICLEHYPOTHESES]
the array of masses
Definition: ParticleHypothesis.h:79
HitCollectionMap.h
AtlasDetDescr::fAtlasForward
@ fAtlasForward
Definition: AtlasRegion.h:28
ATH_MSG_INFO
#define ATH_MSG_INFO(x)
Definition: AthMsgStreamMacros.h:31
ISF::fAtlasMuonExit
@ fAtlasMuonExit
Definition: EntryLayer.h:39
SG::ReadHandle< McEventCollection >
SG::VarHandleBase::name
const std::string & name() const
Return the StoreGate ID for the referenced object.
Definition: AthToolSupport/AsgDataHandles/Root/VarHandleBase.cxx:75
AtlasDetDescr::AtlasRegion
AtlasRegion
Definition: AtlasRegion.h:21
SimKernelMT.h
ISF::ISFParticleOrderedQueue
std::priority_queue< ISF::ISFParticle *, ISF::ISFParticleVector, ISF::ISFParticleOrdering > ISFParticleOrderedQueue
the actual particle priority_queue
Definition: ISFParticleOrderedQueue.h:28
ISF::ISimulatorTool
Definition: ISimulatorTool.h:24
ISF::ParticleKiller
@ ParticleKiller
Definition: SimulationFlavor.h:21
ISF::ISFParticle
Definition: ISFParticle.h:42
HepMC::maxGeneratedVertexBarcode
int maxGeneratedVertexBarcode(const HepMC::GenEvent *genEvent)
Get the maximal absolute value of barcode of vertex present in the event. Returns a negative number.
Definition: MagicNumbers.h:444
ISF::SimKernelMT::m_simSelectors
std::array< ToolHandleArray< ISimulationSelector >, AtlasDetDescr::fNumAtlasRegions > m_simSelectors
The simulation selectors defining the "routing chain".
Definition: SimKernelMT.h:131
DataVector::get
const T * get(size_type n) const
Access an element, as an rvalue.
ATH_MSG_VERBOSE
#define ATH_MSG_VERBOSE(x)
Definition: AthMsgStreamMacros.h:28
AtlasDetDescr::AtlasRegionHelper::getName
static const char * getName(int region)
Definition: AtlasRegionHelper.cxx:13
ISF::SimKernelMT::identifySimulator
ISimulatorTool & identifySimulator(const ISF::ISFParticle &particle)
Returns the simulator to use for the given particle.
Definition: SimKernelMT.cxx:341
ISFParticle.h
ISF::SimKernelMT::~SimKernelMT
virtual ~SimKernelMT()
Implements empty destructor.
Definition: SimKernelMT.cxx:42
AtlasDetDescr::fAtlasMS
@ fAtlasMS
Definition: AtlasRegion.h:30
ISF::ISFParticle::nextGeoID
AtlasDetDescr::AtlasRegion nextGeoID() const
next geoID the particle will be simulated in
ISF::SimKernelMT::m_maxParticleVectorSize
size_t m_maxParticleVectorSize
Number of particles simultaneously sent to simulator.
Definition: SimKernelMT.h:137
EL::StatusCode
::StatusCode StatusCode
StatusCode definition for legacy code.
Definition: PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/StatusCode.h:22
SG::WriteHandle::ptr
pointer_type ptr()
Dereference the pointer.
ATH_MSG_DEBUG
#define ATH_MSG_DEBUG(x)
Definition: AthMsgStreamMacros.h:29
AthCommonDataStore< AthCommonMsg< Algorithm > >::declareProperty
Gaudi::Details::PropertyBase & declareProperty(Gaudi::Property< T, V, H > &t)
Definition: AthCommonDataStore.h:145
ISF::ISFParticleVector
std::vector< ISF::ISFParticle * > ISFParticleVector
ISFParticle vector.
Definition: ISFParticleContainer.h:26
ISF::SimKernelMT::SimKernelMT
SimKernelMT(const std::string &name, ISvcLocator *pSvcLocator)
Implements standard AthAlgorithm constructor.
Definition: SimKernelMT.cxx:27
AtlasRegionHelper.h
ATH_CHECK
#define ATH_CHECK
Definition: AthCheckMacros.h:40
AthAlgorithm
Definition: AthAlgorithm.h:47
SG::ReadHandle::isValid
virtual bool isValid() override final
Can the handle be successfully dereferenced?
HepMC::UNDEFINED_ID
constexpr int UNDEFINED_ID
Definition: MagicNumbers.h:56
ISF::SimKernelMT::finalize
StatusCode finalize() override
Implements empty finalize (implementation required by AthAlgorithm)
Definition: SimKernelMT.cxx:335
GetAllXsec.entry
list entry
Definition: GetAllXsec.py:132
name
std::string name
Definition: Control/AthContainers/Root/debug.cxx:240
AtlasDetDescr::fAtlasCavern
@ fAtlasCavern
Definition: AtlasRegion.h:31
AtlasDetDescr::fAtlasID
@ fAtlasID
Definition: AtlasRegion.h:27
DataVector::push_back
value_type push_back(value_type pElem)
Add an element to the end of the collection.
BaseSimulatorG4Tool.h
SG::VarHandleBase::key
virtual const std::string & key() const override final
Return the StoreGate ID for the referenced object.
Definition: AthToolSupport/AsgDataHandles/Root/VarHandleBase.cxx:64
AtlasDetDescr::fAtlasCalo
@ fAtlasCalo
Definition: AtlasRegion.h:29
SG::WriteHandle< McEventCollection >
python.selector.AtlRunQuerySelectorLhcOlc.selector
selector
Definition: AtlRunQuerySelectorLhcOlc.py:610
HepMC::maxGeneratedParticleBarcode
int maxGeneratedParticleBarcode(const HepMC::GenEvent *genEvent)
Get the maximal value of barcode of particle present in the event.
Definition: MagicNumbers.h:427
ATH_MSG_WARNING
#define ATH_MSG_WARNING(x)
Definition: AthMsgStreamMacros.h:32
ISF::fAtlasCaloEntry
@ fAtlasCaloEntry
Definition: EntryLayer.h:37
LArG4FSStartPointFilter.particles
list particles
Definition: LArG4FSStartPointFilter.py:84
ISF::BaseSimulatorG4Tool
Definition: BaseSimulatorG4Tool.h:28
ISF::SimKernelMT::execute
StatusCode execute() override
Check the validity of the MC truth event in the current Athena event.
Definition: SimKernelMT.cxx:134
DataVector::at
const T * at(size_type n) const
Access an element, as an rvalue.
validAtlasRegion
bool validAtlasRegion(AtlasDetDescr::AtlasRegion region)
Check a given AtlasRegion for its validity.
Definition: AtlasRegion.h:40
DataVector::empty
bool empty() const noexcept
Returns true if the collection is empty.
AtlasDetDescr::fFirstAtlasRegion
@ fFirstAtlasRegion
Definition: AtlasRegion.h:25
ISF::SimKernelMT::initialize
StatusCode initialize() override
Check user configuration and configure the algorithm state accordingly.
Definition: SimKernelMT.cxx:46