92 const EventContext& ctx,
97 std::vector<ISFParticle*> selectedParticles;
98 for (
const auto isfp : particles) {
100 ATH_MSG_VERBOSE(
"ISFParticle " << *isfp <<
" does not pass selection. Ignoring.");
103 selectedParticles.push_back(isfp);
105 if (selectedParticles.empty()) {
107 return StatusCode::SUCCESS;
111 CLHEP::HepRandomEngine* randomEngine = m_randomEngine->getEngine(ctx);
112 Generator generator(CLHEP::RandFlat::shoot(randomEngine->flat()));
113 ATH_MSG_VERBOSE(name() <<
" RNG seed " << CLHEP::RandFlat::shoot(randomEngine->flat()));
115 << selectedParticles.size() <<
" particles for simulation.");
119 auto bField = std::make_shared<ATLASMagneticFieldWrapper>();
138 simulator.charged.interactions = ActsFatras::makeStandardChargedElectroMagneticInteractions(
m_interact_minPt * Acts::UnitConstants::MeV);
147 auto anygctx = gctx.context();
149 ATH_MSG_VERBOSE(name() <<
" Processing particles in ISFParticleVector.");
150 for (
const auto isfp : selectedParticles) {
157 ATH_MSG_DEBUG(name() <<
" Convert ISF::Particle(mass) " << isfp->id()<<
"|" << *isfp<<
"(" << isfp->mass() <<
")");
158 std::vector<ActsFatras::Particle> input = std::vector<ActsFatras::Particle>{
159 ActsFatras::Particle(ActsFatras::Barcode().withVertexPrimary(0).withParticle(isfp->id()),
static_cast<Acts::PdgParticle
>(isfp->pdgCode()),
160 isfp->charge(),isfp->mass() * Acts::UnitConstants::MeV)
161 .setDirection(Acts::makeDirectionFromPhiEta(isfp->momentum().phi(), isfp->momentum().eta()))
162 .setAbsoluteMomentum(isfp->momentum().mag() * Acts::UnitConstants::MeV)
164 ATH_MSG_DEBUG(name() <<
" Propagating ActsFatras::Particle vertex|particle|generation|subparticle, " << input[0]);
165 std::vector<ActsFatras::Particle> simulatedInitial;
166 std::vector<ActsFatras::Particle> simulatedFinal;
167 std::vector<ActsFatras::Hit> hits;
169 auto result=simulator.simulate(anygctx, mctx, generator, input, simulatedInitial, simulatedFinal, hits);
170 auto simulatedFailure=
result.value();
171 if (simulatedFailure.size()>0){
172 for (
const auto& simfail : simulatedFailure){
173 auto errCode = Acts::make_error_code(Acts::PropagatorError(simfail.error.value()));
174 ATH_MSG_WARNING(name() <<
" Particle id " <<simfail.particle.particleId()<<
": fail to be simulated during Propagation: " << errCode.message());
175 ATH_MSG_WARNING(name() <<
" Particle vertex|particle|generation|subparticle"<<simfail.particle <<
" starts from position" << Acts::toString(simfail.particle.position()) <<
" and direction " << Acts::toString(simfail.particle.direction()));
176 return StatusCode::SUCCESS;
180 ATH_MSG_DEBUG(name() <<
" initial particle " << simulatedInitial[0]);
181 ATH_MSG_DEBUG(name() <<
" ActsFatras simulator hits: " << hits.size());
183 for (
const auto& hit : hits) {
188 ATH_MSG_DEBUG(name() <<
" No. of particles after ActsFatras simulator: " << simulatedFinal.size());
189 if (!simulatedFinal.empty()){
191 auto itr = simulatedFinal.begin();
193 std::vector<ActsFatras::Hit> particle_hits;
194 if (itr->numberOfHits() > 0) {
195 std::copy(hits.begin(), hits.begin()+itr->numberOfHits(), std::back_inserter(particle_hits));
199 auto isKilled = !itr->isAlive();
200 int maxGeneration = simulatedFinal.back().particleId().generation();
202 for (
int gen = 0; gen <= maxGeneration; ++gen){
203 ATH_MSG_DEBUG(name() <<
" start with generation "<< gen <<
"|" << maxGeneration <<
": "<< *itr);
204 auto vecsecisfp = std::make_unique<ISF::ISFParticleVector>();
205 std::unique_ptr<ISF::ISFParticle> newisfp =
nullptr;
207 while (itr != simulatedFinal.end() &&
static_cast<int>(itr->particleId().generation()) == gen) {
208 ATH_MSG_DEBUG(name() <<
" genration "<< gen <<
"|" << maxGeneration <<
": "<< *itr);
209 if(itr->isSecondary()){
213 double mass = itr->mass() / Acts::UnitConstants::MeV;
214 double charge = itr->charge();
215 int pdgid = itr->pdg();
219 auto secisfp = std::make_unique<ISF::ISFParticle>(pos,mom,mass,
charge,pdgid,status,properTime,*isfp,
id);
220 secisfp->setNextGeoID(
m_geoIDSvc->identifyNextGeoID(*secisfp));
221 ATH_MSG_DEBUG(name() <<
" secondaries particle (ACTS): "<<*itr<<
"("<<itr->momentum()<<
")|time "<<itr->time()<<
"|process "<<
getATLASProcessCode(itr->process()));
222 ATH_MSG_DEBUG(name() <<
" secondaries particle (ISF): pdg=" << secisfp->pdgCode()
223 <<
" pos=" << secisfp->position() <<
" mom=" << secisfp->momentum()
224 <<
" GeoID=" <<
m_geoIDSvc->identifyNextGeoID(*secisfp));
225 vecsecisfp->push_back(secisfp.release());
229 ATH_MSG_DEBUG(name() <<
" primary particle found with generation ("<< gen <<
")");
232 auto fisfp = std::make_unique<ISF::ISFParticle>(*isfp);
235 ATH_MSG_DEBUG(name() <<
" After simulation, primary particle state: " << *fisfp);
237 ATH_MSG_VERBOSE(
"ISFParticle" << fisfp <<
" after simulation does not pass selection. Ignoring for boundary check.");
241 <<
" new particle GeoID: " <<
m_geoIDSvc->identifyGeoID(*fisfp)
242 <<
", nextGeoID: " <<
m_geoIDSvc->identifyNextGeoID(*fisfp));
245 ATH_MSG_DEBUG(name() <<
" Extrapolating using ActsExtrapolationTool");
248 Acts::BoundTrackParameters startParams = Acts::BoundTrackParameters::createCurvilinear(
249 itr->fourPosition(), itr->direction(), itr->qOverP(), std::nullopt, itr->hypothesis());
252 auto do_exit_startsurface =
checkStartSurface(mctx, anygctx, surfaceCheckPropagator, startParams);
253 ATH_MSG_DEBUG(name() <<
" checkStartSurface returned: " << do_exit_startsurface);
254 if (do_exit_startsurface) {
255 ATH_MSG_DEBUG(name() <<
" Particle starts at a valid surface, doing extrapolation...");
261 auto stepsResult =
m_extrapolationTool->propagationSteps(ctx, startParams, Acts::Direction::Forward());
262 auto steps = stepsResult.value().first;
263 ATH_MSG_DEBUG(name() <<
" Number of propagation steps: " << steps.size());
264 if (steps.size() != 0) {
265 for (
const auto& step : steps) {
266 ATH_MSG_DEBUG(name() <<
" [Acts] Step at position " << step.position
267 <<
" (eta " << Acts::VectorHelpers::eta(step.position)
268 <<
") with GeoID " << step.geoID);
270 nextGeoID =
m_geoIDSvc->identifyGeoID(entryPos);
271 ATH_MSG_DEBUG(name() <<
" [Acts] GeoID from service: " << nextGeoID);
273 ATH_MSG_DEBUG(name() <<
" Boundary crossing detected at GeoID " << nextGeoID);
278 ATH_MSG_WARNING(name() <<
" No propagation steps returned by ActsExtrapolationTool");
281 catch (
const std::exception& e) {
288 double mass = itr->mass() / Acts::UnitConstants::MeV;
289 double charge = itr->charge();
290 int pdgid = itr->pdg();
294 newisfp = std::make_unique<ISF::ISFParticle>(entryPos, mom, mass,
charge, pdgid, isfp->status(), properTime, *isfp, isfp->id(), isfp->barcode());
295 newisfp->setNextGeoID(nextGeoID);
296 ATH_MSG_DEBUG(name() <<
" Truthbinding of parent ISFParticle: " << (isfp->getTruthBinding() ?
"exists" :
"null"));
297 if (isfp->getTruthBinding()) {
298 ATH_MSG_DEBUG(name() <<
" Current GenParticle: " << isfp->getTruthBinding()->getCurrentGenParticle());
300 ATH_MSG_DEBUG(name() <<
" Created new ISFParticle at boundary with nextGeoID: "
302 <<
"(" << nextGeoID <<
")");
307 ATH_MSG_DEBUG(name() <<
" [ISF] Processing boundary particle with nextGeoID: "
309 <<
"(" << newisfp->nextGeoID() <<
")");
329 vecsecisfp->push_back(newisfp.release());
336 ATH_MSG_DEBUG(name() <<
" No starting surface found, skipping boundary check and extrapolation.");
344 if (!vecsecisfp->empty()) {
353 geoID = newisfp->nextGeoID();
374 for (
auto *secisfp : *vecsecisfp){
375 if (secisfp->getTruthBinding()) {
376 secondaries.push_back(secisfp);
377 ATH_MSG_DEBUG(name() <<
" Secondary particle written out to truth.\n Parent ("
378 << *isfp <<
")\n Secondary (" << *secisfp <<
")");
380 ATH_MSG_DEBUG(
"Secondary particle push back to ISF, TruthBinding: " << secisfp->getTruthBinding()->getCurrentGenParticle() <<
" (current) | " << secisfp->getTruthBinding()->getPrimaryGenParticle() <<
" (primary) | " << secisfp->getTruthBinding()->getGenerationZeroGenParticle() <<
" (zero)");
381 if (secisfp->getTruthBinding()->getCurrentGenParticle() !=
nullptr)
ATH_MSG_DEBUG(
"Secondary particle GenParticle EndVertex: " << (secisfp->getTruthBinding()->getCurrentGenParticle()->end_vertex() ?
HepMC::barcode(secisfp->getTruthBinding()->getCurrentGenParticle()->end_vertex()) : 1));
383 ATH_MSG_WARNING(
"Secondary particle not written out to truth.\n Parent ("
384 << *isfp <<
")\n Secondary (" << *secisfp <<
")");
391 ATH_MSG_VERBOSE(name() <<
" No. of secondaries: " << secondaries.size());
394 std::vector<ActsFatras::Particle>().swap(input);
395 std::vector<ActsFatras::Particle>().swap(simulatedInitial);
396 std::vector<ActsFatras::Particle>().swap(simulatedFinal);
397 std::vector<ActsFatras::Hit>().swap(hits);
399 return StatusCode::SUCCESS;