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